Skip to content

Latest commit

 

History

History
197 lines (159 loc) · 6.06 KB

第 6 章 物件及資料結構 e5a9cc4050a24b3a8c1f273652d6591a.md

File metadata and controls

197 lines (159 loc) · 6.06 KB

第 6 章 物件及資料結構

Getter-Setter and private variables

作者在書中提醒我們這件事,我們使用私有 (private) 變數的理由是「不希望任何人依賴這些變數」,但是有許多工程師會自動為它們加上 getter 與 setter,讓私有變數如同公用 (public) 變數一般。

延伸閱讀: 🔗 Why do we need private variables?

資料結構與物件的差別

資料結構 (Data Structure) 與物件 (Object) 是反對稱性的存在:

  • 資料結構
    • 將資料直接暴露在外,可以直接對資料讀取與寫入。例如:list、dict、set 等。
    • 結構化(Procedural)的程式碼(使用資料結構的程式碼)容易添加新的function,而不需要變動已有的資料結構
  • 物件
  • 結構化的程式碼難以添加新的資料結構,因為必須改變所有的function。而物件導向的程式碼難以添加新的function,因為必須改變所有的class

將資料實現的過程隱藏起來,就是一種抽象化的過程。

例子: 圖型

  • Procedural:

    public class Square {
    	 public Point topLeft;
    	 public double side;
    }
    public class Rectangle {
    	 public Point topLeft;
    	 public double height;
    	 public double width;
    }
    public class Circle {
    	 public Point center;
    	 public double radius;
    }
    public class Geometry {
    	 public final double PI = 3.141592653589793;
    	 public double area(Object shape) throws NoSuchShapeException
    	 {
    		 if (shape instanceof Square) {
    			 Square s = (Square)shape;
    			 return s.side * s.side;
    		 }
    		 else if (shape instanceof Rectangle) {
    			 Rectangle r = (Rectangle)shape;
    			 return r.height * r.width;
    		 }
    		 else if (shape instanceof Circle) {
    			 Circle c = (Circle)shape;
    			 return PI * c.radius * c.radius;
    		 }
    		 throw new NoSuchShapeException();
    	 }
    }
  • OOP

    public class Square implements Shape {
    	 private Point topLeft;
    	 private double side;
    	 public double area() {
    		 return side*side;
    	 }
    }
    public class Rectangle implements Shape {
    	 private Point topLeft;
    	 private double height;
    	 private double width;
    	 public double area() {
    		 return height * width;
    	 }
    }
    public class Circle implements Shape {
    	 private Point center;
    	 private double radius;
    	 public final double PI = 3.141592653589793;
    	 public double area() {
    		 return PI * radius * radius;
    	 }
    }

德摩特爾法則 (The Law of Demeter)

  • 德摩特爾法則又可以稱作「最少知識原則
  • 符合 LoD 的函式要求在類別C 中的函式 f 只能呼叫以下幾種類型的函式:
    1. C 本身
    2. 任何由f所產生的物件
    3. 任何當作參數傳遞給f的物件
    4. C類別裡實體變數所持有的物件

LoD 範例使用 Python:

e = E()

class O:
    def __init__(self):
        self.a = A()

    def m(self, b):
        result = self.f()   # O 本身的函式
        b.execute()         # m 的參數
        d = D()             # 在 m 中建立的物件
        d.join()
        self.a.run()        # 宣告在 O 中的物件
        e.jump()            # 被 O 存取的全域變數,並且在 m 的 scope 中

    def f(self):
        return 1+1

如果你在網路上搜尋,也許會看到有人說:「LoD 禁止在一行程式碼中使用多個 dot」,這句話是一半對一半錯,使用多個 dot 的前題是「你知道它們是資料結構還是物件」。

如果是物件,則破壞了物件封裝;而如果是資料結構,資料結構本來就暴露了資料在外面,所以透過巢狀存取資料是沒有問題的。

// 破壞封裝,違反 LoD
class A {
    fun(b) {
        b.getOption().getSelection()
    }
}

// 存取資料結構,沒有違反 LoD 
data = {
    a: {
        b: 2
    }
}
console.log(data.a.b)

你不應該讓一個函式知道太多事情,否則會破壞物件封裝原本的意義。

資料傳輸物件(Data Transfer Objects, DTO)

  • 只是一個帶有資料的物件,沒有任何的操作行為
  • 常用於和資料庫溝通
  • 例子
public class Address {
	 private String street;
	 private String streetExtra;
	 private String city;
	 private String state;
	 private String zip;

	 public Address(String street, String streetExtra, 
									String city, String state, String zip) {
		 this.street = street;
		 this.streetExtra = streetExtra;
		 this.city = city;
		 this.state = state;
		 this.zip = zip;
	 }

	 public String getStreet() {
		 return street;
	 }

	 public String getStreetExtra() {
		 return streetExtra;
	 }

	 public String getCity() {
		 return city;
	 }

	 public String getState() {
		 return state;
	 }

	 public String getZip() {
		 return zip;
	 }
}

小結

在物件與資料結構這個章節,我們首先談到物件的私有變數不應該隨意將它們暴露在外,物件即是要隱藏其內容,只讓外部的人看見它們想公開的行為;因此,開發時必須小心謹慎管理私有變數,否則私有變數將如同公有變數影響整個系統。

此外,我們談到資料結構與物件的不同之處,你經常會在寫程式時用到這兩種技巧,但是它們的意義是不一樣的,為了方便管理與維護,必須時時刻刻在意究竟現在寫的是資料結構還是物件。

最後,德摩特爾法則是一種撰寫程式的規範,我們不應該知道太多物件內的事情,否則就會破壞物件的封裝,讓被使用的物件難以被維護。