預備知識:php
開放封閉原則(Open-Closed Principle OCP)java
Software entities(classes,modules,functions etc) should open for extension ,but close for modification.python
所謂開放封閉原則就是軟件實體應該對擴展開放,而對修改封閉。開放封閉原則是全部面向對象原則的核心。c++
軟件設計自己所追求的目標就是封裝變化,下降耦合,而開放封閉原則正是對這一目標的最直接體現。程序員
對擴展開放,意味着有新的需求或變化時,能夠對現有代碼進行擴展,以適應新的狀況。算法
對修改封閉,意味着類一旦設計完成,就能夠獨立其工做,而不要對類盡任何修改。c#
以上,是抄得。設計模式
ok, 故事開始。函數
從去年開始,咱們公司搞起了O2O開放平臺。提供了O2O各個語言版本的基礎庫,好比c# php java python等.測試
隨着時間的推移,接入開放平臺的企業愈來愈多,使用各類語言的版本。
那麼,咱們寫代碼模擬這個過程。
寫以前,介紹一下python的「屬性」。
爲何要介紹這個東西,由於我以前是個c#程序猿,用得最多的就是這貨。
在綁定屬性時,若是咱們直接把屬性暴露出去,雖然寫起來很簡單,可是,沒辦法檢查參數,致使能夠把成績隨便改:
s = Student() s.score = 9999
這顯然不合邏輯。爲了限制score的範圍,能夠經過一個set_score()
方法來設置成績,再經過一個get_score()
來獲取成績,這樣,在set_score()
方法裏,就能夠檢查參數:
class Student(object): def get_score(self): return self._score def set_score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value
如今,對任意的Student實例進行操做,就不能爲所欲爲地設置score了:
>>> s = Student() >>> s.set_score(60) # ok! >>> s.get_score() 60 >>> s.set_score(9999) Traceback (most recent call last): ... ValueError: score must between 0 ~ 100!
可是,上面的調用方法又略顯複雜,沒有直接用屬性這麼直接簡單。
有沒有既能檢查參數,又能夠用相似屬性這樣簡單的方式來訪問類的變量呢?對於追求完美的Python程序員來講,這是必需要作到的!
還記得裝飾器(decorator)能夠給函數動態加上功能嗎?對於類的方法,裝飾器同樣起做用。Python內置的@property
裝飾器就是負責把一個方法變成屬性調用的:
class Student(object): @property def score(self): return self._score @score.setter def score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value
就是這麼好用(固然,比起c#來講,仍是有點醜陋,爲何我就不說了..畢竟php是世界上最好的語言)
ok,如今開始開放平臺的接入工做。因而咱們寫了以下代碼:
>>> s = Student() >>> s.score = 60 # OK,實際轉化爲s.set_score(60) >>> s.score # OK,實際轉化爲s.get_score() 60 >>> s.score = 9999 Traceback (most recent call last): ... ValueError: score must between 0 ~ 100!
# -*- coding: utf-8 -*-
class OpenService(object): @property def language(self): return self.__language @language.setter def language(self, value): self.__language = value def __init__(self): self.__language = 'csharp' def attack_cities(self): if self.__language == 'python': print '攻城師 苦逼兮兮, 寫python' elif self.__language == 'java': print '攻城師 苦逼兮兮, 寫java' elif self.__language == 'php': print '攻城師 屌屌的, 寫php,畢竟php是世界上最好的語言' elif self.__language == 'c#': print '攻城師 苦逼兮兮, 寫c#' if __name__ == "__main__": open_service = OpenService() print '某公司1開始對接python...' open_service.language = 'python' open_service.attack_cities() print '某公司2開始對接java...' open_service.language = 'java' open_service.attack_cities() print '某公司3開始對接php...' open_service.language = 'php' open_service.attack_cities() print '某公司4開始對接c#...' open_service.language = 'c#' open_service.attack_cities()
PM一聲令下,公司1,2,3,4 紛紛對接開放平臺.

過了一段時間,觸寶又增長了,c++,c,vb.....等各類語言的開放平臺sdk...
那麼怎麼辦呢?很簡單..在attack_cities 方法里加更多的if else 判讀唄...
因而代碼的壞味道出現了....
1. 過長的if else 是明顯的壞味道..
2. 寫代碼是個複雜的過程,所有寫在一個方法裏...這個方法明顯職責太重.違背了本文一開始的單一職責原則。
是時候重構了,噹噹噹當,靜態工廠模式善良登場。

ok,上面也是我抄得。
知道了定義,咱們就要對咱們的場景作出抽象。變化的部分是語言,以及攻城獅如何攻城。那麼咱們就須要設計一個語言基類。帶有攻城的抽象方法。
而後須要定義一個工廠,根據不一樣的需求產出不一樣的語言攻城獅。
固然,爲了使用抽象方法,咱們必須引入衛生巾模塊abc
這樣的話,子類若是不實現基類的抽象方法,會拋出not implement 異常(我猜是這個異常)
以下:
import abc
class OpenServiceFactory(object): languageInstance = None def __init__(self): pass @staticmethod def CreateLanguage(language): if language == 'c#': OpenServiceFactory.languageInstance = CsharpLanguage() elif language == 'java': OpenServiceFactory.languageInstance = JavaLanguage() elif language == 'php': OpenServiceFactory.languageInstance = PHPLanguage() elif language == 'python': OpenServiceFactory.languageInstance = PythonLanguage() return OpenServiceFactory.languageInstance class LanageBase(object): @abc.abstractmethod def att_cities(self): pass class CsharpLanguage(LanageBase): def att_cities(self): print '攻城師 苦逼兮兮, 寫c#' class JavaLanguage(LanageBase): def att_cities(self): print '攻城師 苦逼兮兮, 寫java' class PHPLanguage(LanageBase): def att_cities(self): print '攻城師 屌屌的, 寫php,畢竟php是世界上最好的語言' class PythonLanguage(LanageBase): def att_cities(self): print '攻城師 苦逼兮兮, 寫python'
測試:
if __name__ == "__main__": print '簡單工廠思密達' print '某公司1開始對接python...' open_service = OpenServiceFactory.CreateLanguage('python') open_service.att_cities() print '某公司2開始對接java...' open_service = OpenServiceFactory.CreateLanguage('java') open_service.att_cities() print '某公司3開始對接php...' open_service = OpenServiceFactory.CreateLanguage('php') open_service.att_cities() print '某公司4開始對接c#...' open_service = OpenServiceFactory.CreateLanguage('c#') open_service.att_cities()
運行:
➜ static_factory python static_factory.py
某公司1開始對接python...
攻城師 苦逼兮兮, 寫python
某公司2開始對接java...
攻城師 苦逼兮兮, 寫java
某公司3開始對接php...
攻城師 屌屌的, 寫php,畢竟php是世界上最好的語言
某公司4開始對接c#...
攻城師 苦逼兮兮, 寫c#
簡單工廠思密達
某公司1開始對接python...
攻城師 苦逼兮兮, 寫python
某公司2開始對接java...
攻城師 苦逼兮兮, 寫java
某公司3開始對接php...
攻城師 屌屌的, 寫php,畢竟php是世界上最好的語言
某公司4開始對接c#...
攻城師 苦逼兮兮, 寫c#
➜ static_factory
結論: 簡單工廠模式並無消除工廠類的條件判斷
當有需求擴展時候,必須修改工廠類,違背了了本文一開始提出的開放封閉原則
因此,這不是一個完美的模式,因而有人把簡單工廠開除出了設計模式。
真是的。其實人家也是有很多優勢的。
1. 減小耦合,子類單獨實現算法
2. 客戶端調用時,只需關心工廠類,減小複雜度
3. 工廠封裝和抽象了變化的部分,經過工廠來實現了子類的建立,子類經過多態實現基類方法.
以上,簡單工廠模式,但願對您有所幫助。。
to be continued