Python設(shè)計模式1-創(chuàng)建型模式

工作時間一長,需求多而雜,往往難得有時間去仔細思量代碼如何寫的更加優(yōu)雅,習慣使然。設(shè)計模式本身不是一個神秘的東西,也許無意中也會用到,只是沒有刻意去思考。正好找個時間,把設(shè)計模式用python實現(xiàn)一遍,加深印象,為寫出優(yōu)雅的代碼努力。

1 設(shè)計模式簡介

設(shè)計模式的概念最初來自建筑學,記得以前讀過一本《java與模式》,里面用道家思想講設(shè)計模式,頗有新意。不過對于計算機領(lǐng)域的設(shè)計模式,大家一致認可的是GoF提出的設(shè)計模式,那本《設(shè)計模式》一書有點晦澀,不過好在另外有一本《深入淺出設(shè)計模式》,講設(shè)計模式十分的通俗易懂,可以參考。設(shè)計模式分為創(chuàng)建型,結(jié)構(gòu)型,行為型等幾類。這個系列準備分三篇按照設(shè)計模式類型來總結(jié),本篇文章里面提到的設(shè)計模式都屬于創(chuàng)建型模式。

2 工廠模式

工廠模式可以分為簡單工廠模式,工廠方法模式以及抽象工廠模式,這里以制作披薩作為例子來看看這幾種模式的用法。所謂工廠模式其實是體現(xiàn)了設(shè)計模式中的依賴倒置原則,即要依賴抽象,而不是具體實現(xiàn)。

2.1 簡單工廠模式

簡單工廠模式實現(xiàn)很簡單,我們單獨用一個工廠類來創(chuàng)建不同類型的披薩。我們的例子中有三種類型的披薩,分別是cheese,clam,veggie三種口味。簡單工廠的優(yōu)點是實現(xiàn)簡單,但是對變化不大友好,如果要增加口味或者新開分店,就要改動create_pizza函數(shù)代碼。

#!/usr/bin/env python
#coding:utf8

class Pizza(object):
  def prepare(self):
    print 'prepare pizza'

  def bake(self):
    print 'bake pizza'

  def cut(self):
    print 'cut pizza'

  def box(self):
    print 'box pizza'


class CheesePizza(Pizza):
  def __init__(self):
    self.name = "cheese pizza"


class ClamPizza(Pizza):
  def __init__(self):
    self.name = "clam pizza"


class VeggiePizza(Pizza):
  def __init__(self):
    self.name = "veggie pizza"


class SimplePizzaFactory(object):
  def create_pizza(self, type):
    pizza = None

    if type == "cheese":
      pizza = CheesePizza()
    elif type == "clam":
      pizza = ClamPizza()
    elif type == "veggie":
      pizza = VeggiePizza()

    return pizza



class PizzaStore(object):
  def __init__(self, factory):
    self.factory = factory

  def order_pizza(self, type):
    pizza = self.factory.create_pizza(type)
    pizza.prepare()
    pizza.bake()
    pizza.cut()
    pizza.box()
    return pizza


if __name__ == "__main__":
  store = PizzaStore(SimplePizzaFactory())
  pizza = store.order_pizza('cheese')
  print pizza.name

2.2 工廠方法模式

簡單工廠模式在一個地方就把所有口味的披薩制作完成了,如果此時我們要在北京上海廣州開分店,如果全部代碼寫在簡單工廠里面,如果以后有代碼變動,會難以維護,缺少彈性,而且對象依賴太多,設(shè)計模式告訴我們要依賴抽象,不要依賴具體類,要針對接口編程,而不是具體實現(xiàn)編程。簡單工廠模式里面,PizzaStore是高層的組件,而Pizza是低層組件,PizzaStore過于依賴這些具體的披薩類如CheesePizza,ClamPizza等。工廠方法模式就是針對每一個分店加一個工廠,代碼如下,當然在python里面我們也可以改進下create_pizza函數(shù)代碼,通過globals()[type]()這種方式來創(chuàng)建對象,不需要寫那么多的if-else:

#!/usr/bin/env python
#coding:utf8

class Pizza(object):
  def prepare(self):
    print 'prepare pizza'

  def bake(self):
    print 'bake pizza'

  def cut(self):
    print 'cut pizza'

  def box(self):
    print 'box pizza'


class GZCheesePizza(Pizza):
  def __init__(self):
    self.name = "guangzhou cheese pizza"


class GZClamPizza(Pizza):
  def __init__(self):
    self.name = "guangzhou clam pizza"


class GZVeggiePizza(Pizza):
  def __init__(self):
    self.name = "guangzhou veggie pizza"


class PizzaStore(object):
  def create_pizza(self, item):
    raise NotImplementedError

  def order_pizza(self, type):
    pizza = self.create_pizza(type)
    pizza.prepare()
    pizza.bake()
    pizza.cut()
    pizza.box()
    return pizza


class GZPizzaStore(PizzaStore):
  def create_pizza(self, type):
    pizza = None

    if type == "cheese":
      pizza = GZCheesePizza()
    elif type == "clam":
      pizza = GZClamPizza()
    elif type == "veggie":
      pizza = GZVeggiePizza()

    return pizza


if __name__ == "__main__":
  gz_store = GZPizzaStore()
  pizza = gz_store.order_pizza('cheese')
  print pizza.name

2.3 抽象工廠模式

抽象工廠模式則是針對同一個產(chǎn)品簇來建立抽象工廠和具體工廠角色。所謂產(chǎn)品簇就是位于不同產(chǎn)品等級結(jié)構(gòu)中,功能相關(guān)聯(lián)的產(chǎn)品組成的家族。比如在我們的例子中,北京上海廣州幾個不同地區(qū)的分店都會有cheese,clam,veggie等口味的披薩,不同地區(qū)雖然風味和口味有所不同,但使用的原料有許多是一樣的,這樣原料就可以使用抽象工廠模式,這樣不同地區(qū)不同口味的披薩可以使用相同的原料工廠。這里代碼就不加示例了。

3 單例模式

單例模式用于生成特定類型的唯一對象,即全局只能有一個實例。在許多場景中,我們只是需要一個唯一的實例,比如一些工具類對象,我們通常只要一個實例即可,這樣可以節(jié)約內(nèi)存。一個實現(xiàn)如下,用一個私有內(nèi)部類作為唯一單例對象:

class OnlyOne(object):
    class __OnlyOne(object):
        def __init__(self, arg):
            self.val = arg
        def __str__(self):
            print 'call str'
            return repr(self) + self.val
    instance = None
    def __init__(self, arg):
        if not OnlyOne.instance:
            OnlyOne.instance = OnlyOne.__OnlyOne(arg)
        else:
            OnlyOne.instance.val = arg

x = OnlyOne('sausage')
y = OnlyOne('eggs')
z = OnlyOne('spam')
assert id(x.instance) == id(y.instance) == id(z.instance)

另一種感覺更pythonic的方法是復寫類的__new__方法,如下:

#!/usr/bin/env python
#coding:utf8

class Singleton(object):
  def __new__(cls, *args, **kw):
    if not hasattr(cls, '_instance'):
        cls._instance = super(Singleton, cls).__new__(cls, *args, **kw)
    return cls._instance


class SingletonClass(Singleton):
  pass

if __name__ == "__main__":
  instance1 = SingletonClass()
  instance2 = SingletonClass()
  assert id(instance1) == id(instance2)

當然還有種稱之為borg模式,據(jù)說比單例模式實現(xiàn)單例更好。

class Borg(object):
    _state = {}
    def __new__(cls, *args, **kw):
      obj = super(Borg, cls).__new__(cls, *args, **kw)
      obj.__dict__ = cls._state
      return obj


class SingleInstance(Borg):
  pass


instance1 = SingleInstance()
instance2 = SingleInstance()
assert instance1.__dict__ is instance2.__dict__  #實例不同,但是字典相同
assert id(instance1) != id(instance2)

4 建造模式

建造模式用于將構(gòu)建復雜對象的過程和組成對象的組件解耦。一般有四個角色:指導者,抽象建造者,具體建造者,產(chǎn)品。首先需要創(chuàng)建一個具體建造者,然后將建造者傳入指導者對象進行配置,然后指導者對象調(diào)用建造者的方法創(chuàng)建產(chǎn)品對象,最后客戶端從指導者那里獲取產(chǎn)品。與工廠模式不同的是,建造模式創(chuàng)建產(chǎn)品是在建造者對象中完成,而不像工廠模式產(chǎn)品創(chuàng)建是在產(chǎn)品類中完成的,一個示例如下,代碼取自參考資料3:

#!/usr/bin/python
# -*- coding : utf-8 -*-

"""
@author: Diogenes Augusto Fernandes Herminio <diofeher@gmail.com>
https://gist.github.com/420905#file_builder_python.py
"""


# Director
class Director(object):

    def __init__(self):
        self.builder = None

    def construct_building(self):
        self.builder.new_building()
        self.builder.build_floor()
        self.builder.build_size()

    def get_building(self):
        return self.builder.building


# Abstract Builder
class Builder(object):

    def __init__(self):
        self.building = None

    def new_building(self):
        self.building = Building()


# Concrete Builder
class BuilderHouse(Builder):

    def build_floor(self):
        self.building.floor = 'One'

    def build_size(self):
        self.building.size = 'Big'


class BuilderFlat(Builder):

    def build_floor(self):
        self.building.floor = 'More than One'

    def build_size(self):
        self.building.size = 'Small'


# Product
class Building(object):

    def __init__(self):
        self.floor = None
        self.size = None

    def __repr__(self):
        return 'Floor: {0.floor} | Size: {0.size}'.format(self)


# Client
if __name__ == "__main__":
    director = Director()
    director.builder = BuilderHouse()
    director.construct_building()
    building = director.get_building()
    print(building)
    director.builder = BuilderFlat()
    director.construct_building()
    building = director.get_building()
    print(building)

### OUTPUT ###
# Floor: One | Size: Big
# Floor: More than One | Size: Small

5 原型模式

原型模式即是通過拷貝原型對象來創(chuàng)建新的對象,python中提供了copy模塊來實現(xiàn)原型模式。原型模式里面通常會有一個原型管理器,用于注冊管理原型對象。注意原型模式中的copy與創(chuàng)建一個對象是不一樣的,創(chuàng)建對象只是得到一個初始化的對象,而拷貝對象則可以保持對象的原有狀態(tài)并在原來對象基礎(chǔ)上進行操作。比如游戲里面創(chuàng)建角色,可以用到原型模式,對同樣的屬性不用改變,不同的屬性可以拷貝對象后再重新設(shè)置。示例代碼如下,取自參考資料3:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import copy


class Prototype(object):

    def __init__(self):
        self._objects = {}

    def register_object(self, name, obj):
        """Register an object"""
        self._objects[name] = obj

    def unregister_object(self, name):
        """Unregister an object"""
        del self._objects[name]

    def clone(self, name, **attr):
        """Clone a registered object and update inner attributes dictionary"""
        obj = copy.deepcopy(self._objects.get(name))
        obj.__dict__.update(attr)
        return obj


class A(object):
    def __init__(self):
        self.x = 3
        self.y = 8
        self.z = 15
        self.garbage = [38, 11, 19]

    def __str__(self):
        return '{} {} {} {}'.format(self.x, self.y, self.z, self.garbage)


def main():
    a = A()
    prototype = Prototype()
    prototype.register_object('objecta', a)
    b = prototype.clone('objecta')
    c = prototype.clone('objecta', x=1, y=2, garbage=[88, 1])
    print([str(i) for i in (a, b, c)])

if __name__ == '__main__':
    main()

### OUTPUT ###
# ['3 8 15 [38, 11, 19]', '3 8 15 [38, 11, 19]', '1 2 15 [88, 1]']

6 對象池模式

開發(fā)中總是少不了用到各種池,比如線程池,連接池等。對象池就是將對象用完后先不銷毀,而是存起來,后面繼續(xù)用,節(jié)省重新創(chuàng)建對象的開銷。示例代碼來自參考資料3:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

class QueueObject():

    def __init__(self, queue, auto_get=False):
        self._queue = queue
        self.object = self._queue.get() if auto_get else None

    def __enter__(self):
        if self.object is None:
            self.object = self._queue.get()
        return self.object

    def __exit__(self, Type, value, traceback):
        if self.object is not None:
            self._queue.put(self.object)
            self.object = None

    def __del__(self):
        if self.object is not None:
            self._queue.put(self.object)
            self.object = None


def main():
    try:
        import queue
    except ImportError:  # python 2.x compatibility
        import Queue as queue

    def test_object(queue):
        queue_object = QueueObject(queue, True)
        print('Inside func: {}'.format(queue_object.object))

    sample_queue = queue.Queue()

    sample_queue.put('yam')
    with QueueObject(sample_queue) as obj:
        print('Inside with: {}'.format(obj))
    print('Outside with: {}'.format(sample_queue.get()))

    sample_queue.put('sam')
    test_object(sample_queue)
    print('Outside func: {}'.format(sample_queue.get()))

    if not sample_queue.empty():
        print(sample_queue.get())


if __name__ == '__main__':
    main()

### OUTPUT ###
# Inside with: yam
# Outside with: yam
# Inside func: sam
# Outside func: sam

7 參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容