# 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; } } ```  --- 連鎖店(但每家店的披薩又有點不同) ```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) //抽象的工廠方法 } ```  造成問題:此設計中的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. 修改擁有許多依賴關係的類別會造成廣泛的影響  解決方法: 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
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up