---
# System prepended metadata

title: Clean code - System

---

# Clean code - System
```java=
Pizza orderPizza(String type){
    Pizza pizza;
    if (type.equals("cheese")){
        pizza = new CheesePizza();
    }else if (type.equals("greek")){
        pizza = new GreekPizza();
    }else if (type.equals("pepperoni")){
        pizza = new PepperoniPizza();
    }
    
    pizza.prepare();
    pizza.bake();
    pizza.cut();
    pizza.box();
    return pizza;
}
```
**造成難以維護和更新**：
需要修改或擴展程式時，需要重新打開這段程式碼，檢查需要加入（或刪除）哪些東西。

**解決方法**：
針對介面寫程式，因為介面可以針對多型來實作那一個介面的任何一種新類別互動。

## 主函示main的劃分／工廠／相依性注入
```java=
Pizza orderPizza(String type){
    Pizza pizza;
    
    //將建立物件的程式拿出來
    
    pizza.prepare();
    pizza.bake();
    pizza.cut();
    pizza.box();
    return pizza;
}
```
成立新的類別SimplePizzaFactory，而只負責一項工作：為用戶端建立披薩
```java=
public class SimplePizzaFactory{
    
     public class SimplePizzaFactory{
        Pizza pizza = null;
        if (type.equals("cheese")){
            pizza = new CheesePizza();
        }else if (type.equals("greek")){
            pizza = new GreekPizza();
        }else if (type.equals("pepperoni")){
            pizza = new PepperoniPizza();
        }
         return pizza;
    }
}
 
```
好處：為了其他用戶端做準備（程式碼重複使用，且只需要修改一個位置）

```java=
public class PizzaStore{
    SimplePizzaFactory factory;
    public PizzaStore(SimplePizzaFactory factory){
        this.factory = factory;
    }
    
    public Pizza orderPizza(String type){
        Pizza pizza;
        
        pizza = factory.createPizza(type);
        //orderPizza()方法將訂單的種類給工廠，用來建立披薩//
        //將new運算子換成factory物件的createPizza方法//
        
        
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        
        return pizza;
    }
}
```

![](https://i.imgur.com/n5pEpx1.png)

---

連鎖店（但每家店的披薩又有點不同）
```java=
//PizzaStore是抽象的
public abstract class PizzaStore{
    public Pizza orderPizza(String type){
        Pizza pizza;
        
        pizza = createPizza(type); //變回呼叫PizzaStore內的方法，而非factory物件的方法
        
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        
        return pizza;
    }
    abstract Pizza createPizza(String type) //抽象的工廠方法
}
```
![](https://i.imgur.com/moBp1Lm.png)


造成問題：此設計中的PizzaStore裡，會依據不同工廠創造披薩物件，而非將工作委託給工廠
導致PizzaStore需要**依賴**這些物件

**解決方法**：依賴反轉
設計原則為，**要依賴抽象，不要依賴具體**，與（針對介面寫程式，而非針對實作）比起來，依賴反轉更強調抽象。

---
## Ruby中的依賴關係
```ruby=
class Gear
    attr_reader :chainring, :cog, :rim, :tire
    def initialize(chainring, cog, rim, tire)
        @chainring = chainring
        @cog = cog
        @rim = rim
        @tire = tire
    end
    
    def gear_inches
        ratio * Wheel.new(rim, tire).diameter #Gear對wheel有依賴關係
    end
end
```
造成問題：
1. wheel更改名稱的話，這裡的程式碼也需要更改
2. gear_inches只為Wheel class合作

**解決方法**：讓Gear依賴程度只剩下diameter（理解成一種鴨子類型，Gear並不需要知道物件類別，只需要知道gear_inches會回應一個diameter的結果）
```ruby=
class Gear
    attr_reader :chainring, :cog, :wheel
    def initialize(chainring, cog, wheel)
        @chainring = chainring
        @cog = cog
        @wheel = wheel
    end
    
    def gear_inches
        ratio * wheel.diameter #Gear對wheel有依賴關係
    end
end
```
這個更改技巧稱為**依賴注入**，Gear之前對於Wheel類別以及初始化參數的類型和順序有著明確依賴，而使用依賴注入後，已經減少到對於diameter方法的單一依賴（因為他知道的更少）
而若要反轉依賴關係... 可以把方法改寫為Wheel class有Gear的實體...（沒意義）

### 依賴方向
#### 程式碼的簡單真理
1. 有些類別比其他類別更容易發生需求變化
2. 具體類別抽象類別更容易發生變化
3. 修改擁有許多依賴關係的類別會造成廣泛的影響
 ![](https://i.imgur.com/bYhcyGf.png)

解決方法：
1. 定義介面（抽象）
2. 將diameter定義為介面的一部分
3. 讓wheel class包括此介面
4. 讓Gear注入介面


---

## AOP 橫切關注點
針對業務處理**過程中**的切面進行提取，主要是處理過程裡的某個步驟或階段，以減少耦合的狀況

舉例來說：
在複雜系統裡想對於一些方法進行紀錄，最簡單的方法就是直接在每一個方法內塞入紀錄的語法

造成問題：
搜索不完全而有遺漏與增加耦合（畢竟“紀錄”與方法內邏輯無關）

AOP解決方法：
在方法執行時，先讓程式搜尋需要紀錄的方法名稱，對方法的起始進行紀錄，然後返回控制權執行方法，再回到紀錄的方法內記錄結果。(搭配around與yield使用)

---
參考
>https://www.tenlong.com.tw/products/9789865029364?list_name=c-design-patterns
>https://www.tenlong.com.tw/products/9780321721334?list_name=srh
>https://www.itread01.com/article/1458100408.html