设计模式 - 创造模式(Creational Patterns)

文章目录

  • 零、 设计模式 - 创造模式(Creational Patterns)
  • 一、Borg Pattern - 共享单例模式
    • 1-1 简单认识
      • 1-1-1 Borg 特点
      • 1-1-2 Borg vs Singleton
    • 1-2 Python 实现
      • 1-2-1 装饰器实现
    • 1-3 应用场景
      • 1-3-1 数据库连接管理
  • 二、工厂模式、抽象工厂模式
    • 2-1 工厂模式(factory pattern )
      • 2-1-1 Python 实现
    • 2-2 抽象工厂模式(abstract_factory pattern )
      • 2-2-1 Python 实现
  • 三、Builder Pattern - 建造者模式
    • 3-1 Python 实现简单建造者 - 基类INIT控制建造
    • 3-2 Python 实现复杂建造者 - 外部函数控制建造
  • 四、Prototype Pattern - 原型模式
    • 4-1 Python 实现原型类克隆和调度器的状态保存
  • 五、Lazy_Evaluation Pattern - 懒评价模式
    • 5-1 Python实现
  • 六、Pool Pattern - 对象池模式
    • 6-1 Python 实现

零、 设计模式 - 创造模式(Creational Patterns)

这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。

一、Borg Pattern - 共享单例模式

borg-github 学习参考

1-1 简单认识

Borg Patterns :a singleton with shared-state among instances

单例模式(Singleton pattern) - 保证一个类仅有一个实例,并提供一个访问它的全局访问点。

单例共享模式(Borg Pattern ) - 实现单例而不是仅创建一个实例,是多实例下的状态共享,注重于分享状态而不是分享实例。

1-1-1 Borg 特点

  • 修改了属性字典 __ dict __ ,所以所有实例都拥有相同的字典
  • 同类实例修改属性,同步修改属性值
  • 虽然分享状态,但是同类实例的 id 并不相同

1-1-2 Borg vs Singleton

Python3 - 单例模式及其实现方式

  • Borg 实例地址各不相同;Singleton 实例地址相同
  • Borg 和 Singleton 属性都共享。
  • Singleton 由于只有一个地址,因而相对节省内存空间。

1-2 Python 实现

class Borg(object):
    # 因为是类属性,所以实例共享
    __shared_state = {}

    def __init__(self):
        # 重写 __dict__ 属性字典
        self.__dict__ = self.__shared_state
        self.state = 'Init'

    def __str__(self):
        # 打印输出属性值
        return self.state


class YourBorg(Borg):
    pass


if __name__ == '__main__':
    # 创建多个同类实例
    rm1 = Borg()
    rm2 = Borg()

    # 初始化实例内 state 属性
    rm1.state = 'Idle'
    rm2.state = 'Running'

    # 输出查询状态
    print('rm1: {0}'.format(rm1))
    print('rm2: {0}'.format(rm2))
    # rm1: Running
    # rm2: Running

    # 修改状态
    rm2.state = 'Zombie'

    print('rm1: {0}'.format(rm1))
    print('rm2: {0}'.format(rm2))
    # rm1: Zombie
    # rm2: Zombie

    # 地址不同
    print('rm1 id: {0}'.format(id(rm1)))
    print('rm2 id: {0}'.format(id(rm2)))
    # rm1 id: 1817362624072
    # rm2 id: 1817362624128

    # 子类调用父类初始化,覆盖了父类属性
    rm3 = YourBorg()

    print('rm1: {0}'.format(rm1))
    print('rm2: {0}'.format(rm2))
    print('rm3: {0}'.format(rm3))
    # rm1: Init
    # rm2: Init
    # rm3: Init

1-2-1 装饰器实现

def borg(cls):
    cls._state = {}
    orig_init = cls.__init__
    def new_init(self, *args, **kwargs):
        self.__dict__ = cls._state
        orig_init(self, *args, **kwargs)
    cls.__init__ = new_init
    return cls

@borg
class TestBorg(object):
    def say_borg(self):
        print "i am borg"

1-3 应用场景

1-3-1 数据库连接管理

https://github.com/onetwopunch/pythonDbTemplate/blob/master/database.py

sqlite3 的连接管理,所有实例使用都是同一个db文件。

二、工厂模式、抽象工厂模式

factory pattern :delegate a specialized function/method to create instances ;委托特别的函数去创建实例

abstract_factory pattern :use a generic function with specific factories

2-1 工厂模式(factory pattern )

A Factory is an object for creating other objects.

工厂对象被用于创建其他对象。

工厂类往往是用于提供实例化的“方法”,例如:动物工厂,根据动物叫声的不同而实例化为猫类或狗类。

2-1-1 Python 实现

# 实例类们
# Greek 语言类
class GreekLocalizer(object):
    """A simple localizer a la gettext"""

    def __init__(self):
        self.translations = {"dog": "σκύλος", "cat": "γάτα"}

    def localize(self, msg):
        """We'll punt if we don't have a translation"""
        return self.translations.get(msg, msg)


# English 语言类
class EnglishLocalizer(object):
    """Simply echoes the message"""

    def localize(self, msg):
        return msg


# 工厂函数,用于自主选择 language 是 English 还是 Greek
def get_localizer(language="English"):
    """Factory"""
    localizers = {
        "English": EnglishLocalizer,
        "Greek": GreekLocalizer,
    }
    # 返回实例对象
    return localizers[language]()


if __name__ == "__main__":
    e, g = get_localizer(language="English"), get_localizer(language="Greek")
    print(e.localize("dog"), e.localize("cat"))
    print(g.localize("dog"), g.localize("cat"))

2-2 抽象工厂模式(abstract_factory pattern )

In Java and other languages, the Abstract Factory Pattern serves to provide an interface for creating related/dependent objects without need to specify their actual class.

In Python, the interface we use is simply a callable, which is "builtin" interface in Python, and in normal circumstances we can simply use the class itself as that callable, because classes are first class objects in Python.

在java和其他语言中,抽象工厂模式服务被用于,提供一个为生成关系对象而不需要当前类的接口。

在 Python 语言中,我们简单的只是在Python中的固有接口,并且通常情况下,我们仅仅调用类本身,因为在 Python 中一切皆对象,类是第一个类对象。

即,在抽象工厂模式下,抽象工厂根据工厂类而改变,继而实现实例化(类调用类)

例如:宠物商店作为抽象工厂,可以调用狗类实例化变成狗商店,可以调用猫类实例化成猫商店。

抽象工厂模式- 工厂模式的抽象状态,可以随时根据需要更改工厂属性。(类比,猫和狗都属于动物,动物即为猫狗的抽象状态)

2-2-1 Python 实现

import random


# 抽象工厂类,用于调用类
class PetShop(object):
    """A pet shop"""

    def __init__(self, animal_factory=None):
        """pet_factory is our abstract factory.  We can set it at will."""

        self.pet_factory = animal_factory

    def show_pet(self):
        """Creates and shows a pet using the abstract factory"""

        # 实例化类对象
        pet = self.pet_factory()
        print("We have a lovely {}".format(pet))
        print("It says {}".format(pet.speak()))


class Dog(object):
    def speak(self):
        return "woof"

    def __str__(self):
        return "Dog"


class Cat(object):
    def speak(self):
        return "meow"

    def __str__(self):
        return "Cat"


# Additional factories:

# Create a random animal
def random_animal():
    """Let's be dynamic!"""
    return random.choice([Dog, Cat])()


# Show pets with various factories
if __name__ == "__main__":

    # A Shop that sells only cats
    cat_shop = PetShop(Cat)
    cat_shop.show_pet()
    print("")

    # A shop that sells random animals
    shop = PetShop(random_animal)
    for i in range(3):
        shop.show_pet()
        print("=" * 20)

三、Builder Pattern - 建造者模式

Builder Pattern :instead of using multiple constructors, builder object receives parameters and returns constructed objects

代替使用多个构造函数,建造者对象接受参数并返回构造对象。

用于解耦复杂对象和其表达的创建,以至于相同进程可以构建同一个家族的对象。

当必须将对象的规范从她实际表达中抽离时(通常用于对象的抽象化),建造者模式将非常有用

需求场景:‘复杂对象的创建’,该复杂对象的各个部分经常面临巨大的变化,但是各部分使用的算法却相对稳定。即,部分基本组件不会变化,但组件的使用经常变化。

需求总结

  • 需求的生成对象具有复杂的内部结构
  • 需求的生成对象内部属性本身存在相互依赖关系

使用方式:将变与不变的部分抽离解耦。建造者:创建和提供实例;导演:管理建造出的实例的依赖关系。

实际应用举例

  • 去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"

优缺点总结

  • 优点
    • 建造者独立、易扩展,便于控制细节风险
  • 缺点
    • 产品必须存在共同点,范围受限
    • 若内部变化复杂,容易出现多个建造类

Builder vs Factory:建造者更加注重零件装配的顺序

3-1 Python 实现简单建造者 - 基类INIT控制建造

类比:KFC 内经理指挥套餐组合,而非食客。

'''
通过使用抽象基类来实现,其中初始化器(_init__方法)指定所需的步骤,具体的子类实现这些步骤。
'''


# 抽象建造基类
# 表示建造类基本需要
class Building(object):
    def __init__(self):
        self.build_floor()
        self.build_size()

    def build_floor(self):
        # 若未被重写则抛错
        raise NotImplementedError

    def build_size(self):
        raise NotImplementedError

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


# Concrete Buildings 具体建造类
class House(Building):
    def build_floor(self):
        self.floor = 'One'

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


class Flat(Building):
    def build_floor(self):
        self.floor = 'More than One'

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


if __name__ == '__main__':
    house = House()
    print(house)
    # Floor: One | Size: Big
    flat = Flat()
    print(flat)
    # Floor: More than One | Size: Small

3-2 Python 实现复杂建造者 - 外部函数控制建造

类比:KFC 内食客自定义套餐组合,经理无法控制套餐组合。

'''
In some very complex cases,
it might be desirable to pull out the building logic into another function (or a method on another class),
rather than being in the base class '__init__'.
(This leaves you in the strange situation where a concrete class does not have a useful constructor)
在一些非常复杂的例子中,将建造逻辑抽离放入另一个函数(或者另一个类的方法)中是可能可取的。
而不是在基类中使用'__init__'
(这将使你处在一个陌生的情况中,即一个没有实用构造函数的具体类。)
'''


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


class ComplexHouse(ComplexBuilding):
    def build_floor(self):
        self.floor = 'One'

    def build_size(self):
        self.size = 'Big and fancy'


def construct_building(cls):
    # 注意参数传入的是一个类,而非对象
    # 调用类生成对象
    building = cls()
    building.build_floor()
    building.build_size()
    return building


# Client
if __name__ == "__main__":
    # Using an external constructor function
    complex_house = construct_building(ComplexHouse)
    print(complex_house)
    # Floor: One | Size: Big and fancy。

四、Prototype Pattern - 原型模式

Prototype Pattern:use a factory and clones of a prototype for new instances (if instantiation is expensive)

为新实例使用工厂和原型的克隆(如果实例化代价很高)

这个模式目标在于减少一个应用需要的类数量。它通过在执行时间拷贝原型实例去创建对象,而不是依赖子类 。

当类的实例只有几个不同的组合状态,和实例化代价很高的时候,这将十分有用。它使得派生新类型的对象变的简单。

**注意事项:**与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。

需求场景

  • 一个系统需要独立于它的产品创建,构成和表示时。
  • 需要实例化的类是在运行时刻指定的,如:动态装载
  • 需求避免创建一个与产品类层次平行的工厂类层次时
  • 当一个类的实例只有几个不同的状态组成中的一种时,建立相应数据的原型并克隆比每次手动实例合适的状态更方便。

优缺点总结

  • 优点
    • 性能提高
    • 逃避构造函数的约束
  • 缺点
    • 克隆方法需要对类功能进行通盘考虑,对于已有的类不一定容易实现。(特别的,一个类引用不支持串行化的间接对象,或者引用含有循环结构时)
    • 必须实现 Cloneable 接口

实际应用场景

  • 资源优化场景。
  • 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
  • 性能和安全要求的场景。
  • 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
  • 一个对象多个修改者的场景。
  • 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
  • 在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。

4-1 Python 实现原型类克隆和调度器的状态保存

'''
*What does this example do?
When the number of prototypes in an application can vary, it can be
useful to keep a Dispatcher (aka, Registry or Manager). This allows
clients to query the Dispatcher for a prototype before cloning a new
instance.
Below provides an example of such Dispatcher, which contains three
copies of the prototype: 'default', 'objecta' and 'objectb'.

当原型的数量在应用中能变化,这将在保持一个调度器(又名,注册表或管理器)时有用。
这允许客户端为了原型在克隆一个新的实例前去查询调度器。
下面提供了这样一个Dispatcher的示例,它包含三个
原型的副本:default、objecta 和 objectb。
'''


# 原型类 用于各状态克隆的基类,存在不同的状态
class Prototype(object):
    value = 'default'

    def clone(self, **attrs):
        """Clone a prototype and update inner attributes dictionary"""
        # obj 调用该对象的 __class__ 使其指向对应的类
        # 可用 obj 去调用对应的类属性
        obj = self.__class__()
        # 更新类字典
        obj.__dict__.update(attrs)
        return obj


# 原型调度者类,该例给予了Prototype类对象的状态副本保存
class PrototypeDispatcher(object):
    def __init__(self):
        self._objects = {}

    def get_objects(self):
        """Get all objects"""
        return 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 main():
    dispatcher = PrototypeDispatcher()
    prototype = Prototype()

    # 返回 类对象
    d = prototype.clone()
    # 参数被放入 类__dict__中
    a = prototype.clone(value='a-value', category='a')
    b = prototype.clone(value='b-value', is_checked=True)

    dispatcher.register_object('objecta', a)
    print(dispatcher.get_objects())
    # {'objecta': <__main__.Prototype object at 0x00000206CDA1BB00>}
    print(dispatcher.get_objects()['objecta'].value)  # a-value
    print(dispatcher.get_objects()['objecta'].category)  # a

    dispatcher.register_object('objectb', b)
    dispatcher.register_object('default', d)
    print([{n: p.value} for n, p in dispatcher.get_objects().items()])
    # [{'objecta': 'a-value'}, {'objectb': 'b-value'}, {'default': 'default'}]


if __name__ == '__main__':
    main()

五、Lazy_Evaluation Pattern - 懒评价模式

Lazy_Evaluation Pattern :lazily-evaluated property pattern in Python

懒评价模式延迟表达式的评估,特别应用于函数式编程语言,使用延迟求值时,表达式值绑定到变量就不会被计算,直到当求值程序被强制生成表达式的值时候。

优点总结

  • 节省执行时间,避免不必要的计算提高性能
  • 定义控制流(结构体)作为抽象,而不基元的能力
  • 定义潜在的无限数据结构体,这允许一些算法的更多直接实现。
  • 避免不必要的计算提升性能,并且避免当评估复合表达式时的错误情况

5-1 Python实现

import functools


class lazy_property(object):
    def __init__(self, function):
        self.function = function
        # 使 func 看起来像是被 self 包裹的函数
        # 将类作为装饰器
        functools.update_wrapper(self, function)

    def __get__(self, obj, type_):
        if obj is None:
            return self
        # 获取对象,执行函数调用
        val = self.function(obj)
        # 将执行结果放在对象字典内
        obj.__dict__[self.function.__name__] = val
        return val


# 装饰器
def lazy_property2(fn):
    attr = '_lazy__' + fn.__name__

    @property
    def _lazy_property(self):
        if not hasattr(self, attr):
            setattr(self, attr, fn(self))
        return getattr(self, attr)

    return _lazy_property


class Person(object):
    def __init__(self, name, occupation):
        self.name = name
        self.occupation = occupation
        self.call_count2 = 0

    @lazy_property
    def relatives(self):
        # Get all relatives, let's assume that it costs much time.
        relatives = "Many relatives."
        return relatives

    @lazy_property2
    def parents(self):
        self.call_count2 += 1
        return "Father and mother"


def main():
    Jhon = Person('Jhon', 'Coder')
    print(Jhon.name)  # Jhon
    print(Jhon.occupation)  # Coder
    res = sorted(Jhon.__dict__.items())
    print(res)
    # [('call_count2', 0), ('name', 'Jhon'), ('occupation', 'Coder')]
    print(Jhon.relatives)
    # Many relatives.
    r = sorted(Jhon.__dict__.items())
    print(r)
    # [('call_count2', 0), ('name', 'Jhon'), ('occupation', 'Coder'), ('relatives', 'Many relatives.')]
    print(Jhon.parents)
    # Father and mother
    re = sorted(Jhon.__dict__.items())
    print(re)
    # [('_lazy__parents', 'Father and mother'), ('call_count2', 1), ('name', 'Jhon'), ('occupation', 'Coder'), ('relatives', 'Many relatives.')]
    print(Jhon.call_count2)
    # 1


if __name__ == "__main__":
    main()

六、Pool Pattern - 对象池模式

Pool Pattern:preinstantiate and maintain a group of instances of the same type

实例化和维护一组同类型的实例

对象池模式被用于,当创建一个对象是代价高的(并且他们经常被创建),但一次值使用少量对象。使用池,我们可以通过缓存来管理到目前为止我们拥有的实例。现在跳过创建一个对象的昂贵对象是可能的,如果池中有对象是可使用的。

池允许检验一个不活跃的对象,并且之后将他返回。

若果在池中没有可使用的,池无需等待马上创建并提供一个可使用对象

6-1 Python 实现

'''
*What does this example do?
In this example queue.Queue is used to create the pool (wrapped in a
custom ObjectPool object to use with the with statement), and it is
populated with strings.
As we can see, the first string object put in "yam" is USED by the
with statement. But because it is released back into the pool
afterwards it is reused by the explicit call to sample_queue.get().
Same thing happens with "sam", when the ObjectPool created insided the
function is deleted (by the GC) and the object is returned.

在这个例子中,queue.Queue 被用于创建池(用自定义 ObjectPool 对象和 with 语句一起包裹)
并且他用字符串填充
第一个 string 对象放置在 “yam” 被 with 语句使用
但是因为它被释放进池子,之后它被 sample_queue.get() 显式的调用
相同的事情发生在 “sam”,当 ObjectPool 创建内部函数被删除(GC 删除),这个对象被返回
'''


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

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

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

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


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

    def test_object(queue):
        pool = ObjectPool(queue, True)
        print('Inside func: {}'.format(pool.item))

    sample_queue = queue.Queue()

    sample_queue.put('yam')
    with ObjectPool(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()

你可能感兴趣的:(设计模式)