[python實現設計模式]-3.簡單工廠模式-觸寶開放平臺

預備知識:php

開放封閉原則(Open-Closed Principle OCP)java

Software entities(classes,modules,functions etc) should open for extension ,but close for modification.python

   所謂開放封閉原則就是軟件實體應該對擴展開放,而對修改封閉。開放封閉原則是全部面向對象原則的核心。c++

   軟件設計自己所追求的目標就是封裝變化,下降耦合,而開放封閉原則正是對這一目標的最直接體現。程序員

   對擴展開放,意味着有新的需求或變化時,能夠對現有代碼進行擴展,以適應新的狀況。算法

   對修改封閉,意味着類一旦設計完成,就能夠獨立其工做,而不要對類盡任何修改。c#

 

單一職責原則:(SRP:Single responsibility principle)
 
又稱單一功能原則,面向對象五個基本原則(SOLID)之一。它規定一個類應該只有一個發生變化的緣由。
所謂職責是指類變化的緣由。若是一個類有多於一個的動機被改變,那麼這個類就具備多於一個的職責。而單一職責原則就是指一個類或者模塊應該有且只有一個改變的緣由。

 

以上,是抄得。設計模式

 

 

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. 寫代碼是個複雜的過程,所有寫在一個方法裏...這個方法明顯職責太重.違背了本文一開始的單一職責原則。

是時候重構了,噹噹噹當,靜態工廠模式善良登場。

簡單工廠模式

 
簡單工廠模式是屬於建立型模式,又叫作靜態工廠方法(Static Factory Method)模式。簡單工廠模式是由一個工廠對象決定建立出哪種產品類的實例。簡單工廠模式是工廠模式家族中最簡單實用的模式,能夠理解爲是不一樣工廠模式的一個特殊實現。
 
該模式中包含的角色及其職責
工廠(Creator)角色
簡單工廠模式的核心,它負責實現建立全部實例的內部邏輯。工廠類的建立產品類的方法能夠被外界直接調用,建立所需的產品對象。
抽象產品(Product)角色
簡單工廠模式所建立的全部對象的父類,它負責描述全部實例所共有的公共接口。
具體產品(Concrete Product)角色
是簡單工廠模式的建立目標,全部建立的對象都是充當這個角色的某個具體類的實例。




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

相關文章
相關標籤/搜索