---
# System prepended metadata

title: Clean Code - Ch06 資料結構及物件
tags: [Clean Code, Book]

---

# Clean Code - Ch06 資料結構及物件
將實現功能的過程隱藏，就是一種抽象化的過程，提供一個抽象的介面，讓使用者不需要知道怎麼實現功能但還是可將功能實現出來。
大概就是 **結構化程式設計 vs 物件導向程式設計**
[結構化程式設計](http://www.chwa.com.tw/TResource/VS/book2/ch3/3-1.htm)
[物件導向程式設計](https://expect7.pixnet.net/blog/post/38682120-%5B%E7%A8%8B%E5%BC%8F%5D%5B%E6%A6%82%E5%BF%B5%5D-%E7%89%A9%E4%BB%B6%E5%B0%8E%E5%90%91%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E3%80%82%E4%BB%80%E9%BA%BC%E6%98%AF%E7%89%A9%E4%BB%B6)

## 1. 資料結構 (struct，純model) VS 物件 (介面)

|資料結構|物件導向|
|-|-|
|將資料暴露在外面，而且也沒有提供操作用的函式|將數據隱藏在抽象層(Interface)後面，只將操作函式暴露在外面|
|如果要增加新的function，不會影響到舊的任何東西，但要增加新的class時，就會影響到舊有的function。|如果要增加新的class，不會影響到舊的class。但要增加新的function時，就會影響到所有的class。|

**資料結構：**
``` csharp
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.1436;

    public double area (Object shape) throw 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 ();
    }
}
```
**物件導向：**
``` csharp
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 PI = 3.14;

    public double area () {
        return PI * radius * radius;
    }
}
```
## 2. 火車事故 (Train Wreck)
因為代碼看起來像一長串車廂連接再一起，故名為火車事故 (?
``` csharp
String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
```

## 3. 得墨忒耳定律 (Law of Demeter，LoD) a.k.a 最少知識原則 (Principle of Least Knowledge)
*得墨忒耳定律是鬆耦合的一種具體案例，模組不該知道他所操縱的物件的內部運作*
**原則：**
1. 每個單元對於其他的單元只能擁有有限的知識：只能是與當前單元緊密聯繫的單元；
2. 每個單元只能和它的朋友交談：不能和陌生單元交談；
3. 只和自己直接的朋友交談。

基於上述原則，調用Aclass的Bfunc時，只能response以下東西：
1. Aclass
2. 由Bfunc產生的參數
3. Bfunc所接收到的參數
4. Aclass裡面的實體變數(直接定義在Aclass的變數)

根據上述原則，以下這行代碼不符合LoD，因為在getOptions()回來之前就已經知道它裡面有getScratchDir()，甚至還知道更深層的getAbsolutePath()
**因為他不知道後面function response的型別，但如果是同型別就可以**
``` csharp
String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
```
因為在LoD裡面，這些內部資訊應該被隱藏起來，不該暴露在外面，應該要分拆成如下：
``` csharp
Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();
```
但如果這些Options、ScratchDir、AbsolutePath，都不是物件，而是資料結構的話，
就不會違反LoD
``` csharp
String outputDir = ctxt.Options.ScratchDir.AbsolutePath;
```
[Law of Demeter](https://zh.wikipedia.org/wiki/%E5%BE%97%E5%A2%A8%E5%BF%92%E8%80%B3%E5%AE%9A%E5%BE%8B)
 
## 4. 混合體
即**半物件半資料結構**，有函式做重要的事，也有公共變數或公共存取器、修改器。使得難以添加新的函式，也難以添加新的資料結構，是一種糊塗的設計，因為作者不確定，它是否需要函式或型態的保護。
``` csharp
public class Member
{
    public int Age { get; set; }
    public int GetAge() {
        return this.Age;
    }
    public int SetAge(int value) {
        this.Age = value;
    }
}
```

## 總結
在設計系統時，我們有時候需要**增加新資料型態的彈性**，這時我們比較需要使用物件導向的設計。
但有時候，我們希望可以有**增加新行為的彈性**，這時我們比較傾向採用資料結構的設計。

###### tags: `Clean Code` `Book`