簡單工廠模式簡介 工廠模式有一種非常形象的描述,建立對象的類就如一個工廠,而需要被建立的對象就是一個個產品;在工廠中加工產品,使用產品的人,不用在乎產品是如何生產出來的。從軟體開發的角度來說,這樣就有效的降低了模塊之間的耦合。 簡單工廠的作用是實例化對象,而不需要客戶瞭解這個對象屬於哪個具體的子類。 ...
簡單工廠模式簡介
工廠模式有一種非常形象的描述,建立對象的類就如一個工廠,而需要被建立的對象就是一個個產品;在工廠中加工產品,使用產品的人,不用在乎產品是如何生產出來的。從軟體開發的角度來說,這樣就有效的降低了模塊之間的耦合。
簡單工廠的作用是實例化對象,而不需要客戶瞭解這個對象屬於哪個具體的子類。簡單工廠實例化的類具有相同的介面或者基類,在子類比較固定並不需要擴展時,可以使用簡單工廠。如資料庫生產工廠就是簡單工廠的一個應用
- 優點:可以使用戶根據參數獲得對應的類實例,避免了直接實例化類,降低了耦合性;
- 缺點:可實例化的類型在編譯期間已經被確定,如果增加新類 型,則需要修改工廠,不符合OCP(開閉原則)的原則。簡單工廠需要知道所有要生成的類型,當子類過多或者子類層次過多時不適合使用。
面向對象的要點
- 面向對象三大特性:封裝、繼承、多態。
- 通過封裝、繼承、多態把程式耦合降低。
- 業務邏輯和界面邏輯分開。
程式設計的要點
- 程式應該做到:(1)可維護;(2)可復用;(3)可擴展;(4)靈活性好。
- 可維護:就是說代碼一處更改,不能產生連鎖反應,不能影響其他地方。
- 可復用:儘量減少重覆性代碼。
- 可擴展:如果要擴展新的功能、新的業務,則只需要增加新的類就好了,不對已有的類和邏輯產生影響。插拔式的應用。
實例分析
題目:
用任意一種面向對象語言實現一個計算器控制台程式。要求輸入兩個數和運算符號,得到結果。
設計有缺陷的代碼
#!/usr/bin/env python # _*_ coding utf-8 _*_ #Author: aaron num1 = int(input('請輸入第一個數:')) op =input('請輸入運算符(+ - * /):') num2 = int(input('請輸入第一個數:')) if op == '+': res = num1 + num2 elif op == '-': res = num1 - num2 elif op == '*': res = num1 * num2 elif op == '/': res = num1 / num2 print('運算結果為:',res)
存在的問題
- 業務邏輯和界面邏輯沒有分開
- 業務邏輯有漏洞(除法中num2沒有做非零判斷)
- 不具有擴展性(無法添加新功能,eg:加入平方運算,再加入一條“elif語句”是很糟糕的修改方式,下文詳細敘述)
- 可維護性差(eg:加入平方運算時,很可能修改其他地方的源代碼,有人就說了,怎麼可能,不就是再加入一條“elif語句”嗎?是的,反這種低級錯誤的概率很小,我所說的是惡意修改源代碼。當然,又有人反駁了,不就是添加一種運算嗎,我修改其他運算幹嘛?是的,非常正確。但你想過沒,如果我們這裡的“if語句”不是做運算符的判斷,而是做某個公司的薪資計算呢,那修改代碼的程式員會不會惡意將和自己相關的地方修改了呢?這也解釋了不具有擴展性的問題了)
完善後的代碼
設計思路分析
1. 首先,搞清楚業務中容易發生變化的部分。在本應用中,要求計算兩個數的運算結果,那麼要進行什麼樣的運算,這就是一個容易發生變化的部分。例如,我們現在只想實現加減乘除運算,後期又想增加開根或者求餘運算。那麼如何應對這種需求帶來的變化。在程式設計的時候就應該考慮到程式的可維護性、可擴展性、代碼的可復用性、靈活性等等。
2. 例如現在這個運算器只有加減乘除四種運算。首先建一個Operation類,這個類是各種具體運算類(加減乘除)的父類,主要是接受用戶輸入的數值。該類如下:
class Operation(): def __init__(self,NumberA=0,NumberB=0): self.NumberA = NumberA self.NumberB = NumberB def GetResult(self): pass
3. 然後是具體的運算類:Add、Sub、Mul、Div。他們都繼承了Operation類,並且重寫了getResult()方法。這樣就可以用多態性降低不同業務邏輯的耦合度,修改任何一種運算類都不會影響其他的運算類。具體類的代碼如下:
class AddOp(Operation): def GetResult(self): return self.NumberB + self.NumberA class MinusOp(Operation): def GetResult(self): return self.NumberA - self.NumberB class MultiOp(Operation): def GetResult(self): return self.NumberA * self.NumberB class DivideOp(Operation): def GetResult(self): try: return 1.0*self.NumberA / self.NumberB except ZeroDivisionError: raise
4. 那麼如何讓計算器知道我是要用哪一種運算呢?也就是說到底要實例化哪一個具體的運算類,Add?Sub?Mul?Div?這時就應該考慮用 一個單獨的類來做這個創造具體實例的過程,這個類就是工廠類。如下:
class OperationFatory(): def ChooseOperation(self,op): if op == '+': return AddOp() if op == '-': return MinusOp() if op == '*': return MultiOp() if op == '/': return DivideOp()
5. 這樣,用戶只要輸入運算符,工廠類就可以創建合適的實例,通過多態性,即返回給父類的方式實現運算結果。客戶端代碼如下:
if __name__ == '__main__': ch = '' while not ch=='q': NumberA = eval(raw_input('Please input number1: ')) op = str(raw_input('Please input the operation: ')) NumberB = eval(raw_input('Please input number2: ')) OPFactory = OperationFatory() OPType = OPFactory.ChooseOperation(op) OPType.NumberA = NumberA OPType.NumberB = NumberB print 'The result is:',OPType.GetResult() print '\n#-- input q to exit any key to continue' try: ch = str(raw_input()) except: ch = ''
完整版代碼:
# -*-coding:UTF-8-*- from abc import ABCMeta,abstractmethod class Operation(): def __init__(self,NumberA=0,NumberB=0): self.NumberA = NumberA self.NumberB = NumberB def GetResult(self): pass class AddOp(Operation): def GetResult(self): return self.NumberB + self.NumberA class MinusOp(Operation): def GetResult(self): return self.NumberA - self.NumberB class MultiOp(Operation): def GetResult(self): return self.NumberA * self.NumberB class DivideOp(Operation): def GetResult(self): try: return 1.0*self.NumberA / self.NumberB except ZeroDivisionError: raise class OperationFatory(): def ChooseOperation(self,op): if op == '+': return AddOp() if op == '-': return MinusOp() if op == '*': return MultiOp() if op == '/': return DivideOp() if __name__ == '__main__': ch = '' while not ch=='q': NumberA = eval(raw_input('Please input number1: ')) op = str(raw_input('Please input the operation: ')) NumberB = eval(raw_input('Please input number2: ')) OPFactory = OperationFatory() OPType = OPFactory.ChooseOperation(op) OPType.NumberA = NumberA OPType.NumberB = NumberB print 'The result is:',OPType.GetResult() print '\n#-- input q to exit any key to continue' try: ch = str(raw_input()) except: ch = ''