鹤啸九天 自律更自由,平凡不平庸 Less is More

面向对象开发

2019-07-03
鹤啸九天
阅读量

Notes(温馨提示):

  1. ★ 首次阅读建议浏览:导航指南, 或划到本页末尾, 或直接点击跳转, 查看全站导航图
  2. 右上角工具条搜索文章,右下角二维码关注微信公众号(鹤啸九天),底栏分享、赞赏、评论
  3. ★ 转载请注明文章来源,知识点积累起来不容易,水滴石穿,绳锯木断,谢谢理解
  4. ★ 如有疑问,邮件讨论,欢迎贡献优质资料


面向对象

编程范式

编程范式对比

  • 面向过程:根据业务逻辑从上到下写垒代码
  • 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
  • 面向对象:对函数进行分类和封装,让开发“更快更好更强…”

OOP特点

面向对象三大特点:封装继承多态

  • 封装:将内容封装到某个地方,以后再去调用被封装在某处的内容
  • 继承:面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容
  • 多态:

资料

OOP 概念

  • :用户定义的对象原型(prototype),该原型定义了一组可描述该类任何对象的属性,属性是数据成员(类变量 和 实例变量)和方法,可以通过 ‘.’ 来访问。说简单一点,类是一个模板,我们可以使用该模板生成不同的具体的对象,来完成我们想要的操作
  • 实例:某一个类的单个对象,例如定义了一个 Person 类,而具体的人,比如小明,小黄就是 Person 类的实例
  • 属性:描述该类具有的特征,比如人类具备的属性,身份证,姓名,性别,身高,体重等等都是属性
  • 方法:是该类对象的行为,例如这个男孩会打篮球,那个女孩会唱歌等等都是属于方法,常常通过方法改变一些类中的属性值

类的成员可以分为三大类:字段、方法和属性

  • 字段:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同,
    • 普通字段属于对象
    • 静态字段属于类
  • 方法
  • 属性

类的所有成员在上一步骤中已经做了详细的介绍,对于每一个类的成员而言都有两种形式:

  • 公有成员,在任何地方都能访问
  • 私有成员,只有在类的内部才能方法
    • 私有成员命名时,前两个字符是下划线。(特殊成员除外,例如:__init____call____dict__等)

普通字段

  • 公有普通字段:对象可以访问;类内部可以访问;派生类中可以访问
  • 私有普通字段:仅类内部可以访问;
    • 如果想要强制访问私有字段,可以通过 【对象._类名__私有字段明 】访问(如:obj._C__foo),不建议强制访问私有成员。
#class C(object): # 新式类定义
class C: # 经典类定义
    # 所有静态成员在内存中只保留一份!
    name = "公有静态字段" # 任何地方都能访问
    __name = "私有静态字段" # 类内才能访问

    def __init__(self):
        # 普通字段每个实例独有一份
        self.name = '公有普通字段' # 对象、类本身、派生类可访问
        self.foo = "公有普通字段"
        self.__foo = "私有普通字段" # 仅类内可访问
        print('类名: ', self.__class__.__name__)

    def func(self):
        print(self.foo) # 访问对象公有普通字段
        print(C.name) # 访问类公有静态成员
        print(C.__name) # 访问类私有静态字段
        print(C.__name)

class D(C):
    # 子类继承
    def show(self):
        print(C.__name) # 派生类访问类私有静态成员

if __name__ == "__main__":
    C.__name       # 类访问            ==> 错误
    obj = C()      # 类实例化 → 对象
    obj.func()     # 类内部可以访问     ==> 正确
    obj_son = D()
    obj_son.show() # 派生类中可以访问   ==> 错误
    obj._C__foo # 强制访问对象私有成员(不推荐)

字段(属性)

分成两类:属性、实例属性

  • 类属性:静态字段在内存中只保存一份
  • 实例属性:普通字段在每个对象中都要保存一份
class Province:
    # 静态字段(所有实例公用)
    country  '中国'

    def __init__(self, name):
        # 普通字段(每个实例独有)
        self.name = name

# 直接访问普通字段
obj = Province('河北省')
print(obj.name)
# 直接访问静态字段
Province.country

应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段

方法

方法包括:普通方法、静态方法和方法,三种方法在内存中都归属于类,区别在于调用方式不同。

  • 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
  • 方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
  • 静态方法:由类调用;无默认参数;
class Foo:
    # 各种方法说明
    def __init__(self, name):
        # 构造函数
        self.name = name

    def ord_func(self):
        """ 定义普通方法,至少有一个self参数 """
        # print self.name
        print '普通方法'

    @classmethod
    def class_func(cls):
        """ 定义类方法,至少有一个cls参数 """
        print '类方法'

    @staticmethod
    def static_func():
        """ 定义静态方法 ,无默认参数"""
        print '静态方法'

# 调用普通方法
f = Foo() # 类实例化
f.ord_func() # 普通方法
# 调用类方法
Foo.class_func() # 类方法
# 调用静态方法
Foo.static_func() # 类静态方法
  • 相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。
  • 不同点:方法调用者不同、调用方法时自动传入的参数不同。

特殊方法

特殊方法也成为魔术方法,它的使用也很简单,用 __ 开始 以及结尾 就可以使用了

class Test():
    # __开头结尾的函数
    def __init__(self):
        print("我是初始化方法")

    def __len__(self):
        return 55

    def __str__(self):
        return "Hello World"

t = Test()

print(t)
print(len(t))
print(str(t))

# 常见的还有很多,大家可以自行尝试
'''
    特殊方法,也成为魔术方法
    特殊方法以 __ 开头和结尾,比如
    __init__ 初始化方法
    __str__() str() 这个特殊方法会在尝试将对象转换为字符串的时候调用
    __rpr__() rpr()
    __len__() 获取长度 len()
    __bool__() 返回布尔值 bool()
    __pow__
    __lshift__()
    __lt__()
    __add__()
    __and__
    __or__
    __eq__
    __sub__
'''

属性

Python中的属性其实是普通方法的变种。向调用属性一样调用函数

对于属性,有以下三个知识点:

  • 属性的基本使用
  • 属性的两种定义方式
class Foo:
    # 类属性使用方法
    def func(self):
        pass
    # 定义属性 —— 经典类只有这个方法,新式类就有多种功能
    @property
    def prop(self):
        pass
# ############### 调用 ###############
foo_obj = Foo()
foo_obj.func()
foo_obj.prop   # 调用属性,没有括号

由属性的定义和调用要注意一下几点:

  • 定义时,在普通方法的基础上添加 @property 装饰器;
  • 定义时,属性仅有一个self参数
  • 调用时,无需括号
    • 方法:foo_obj.func()
    • 属性:foo_obj.prop 注意:
  • 属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象
  • 属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。

Python属性的功能是:属性内部进行一系列的逻辑计算,最终将计算结果返回。

属性的定义有两种方式:

  • 装饰器:在方法上应用装饰器,在类的普通方法上应用@property装饰器
  • 静态字段:在类中定义值为property对象的静态字段

新式类的属性比经典类的属性丰富

  • 经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法
  • 新式类中的属性有三种访问方式,并分别对应了三个被 @property、 @方法名.setter、 @方法名.deleter修饰的方法,定义为对同一个属性:获取、修改、删除

封装

封装是将内容封装到某个地方,以后再去调用被封装在某处的内容。

所以,在使用面向对象的封装特性时,需要:

  • 将内容封装到某处
  • 从某处调用被封装的内容
    • 通过对象直接调用
    • 通过self间接调用

封装的核心:封装是隐藏对象中一些不希望被外部访问到的属性或方法

class Foo:
 
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def detail(self):
        print self.name
        print self.age
 
obj1 = Foo('wupeiqi', 18)
print obj1.name    # 直接调用obj1对象的name属性
print obj1.age     # 直接调用obj1对象的age属性
obj1.detail()  # Python默认会将obj1传给self参数,即:obj1.detail(obj1),所以,此时方法内部的 self = obj1,即:self.name 是 wupeiqi ;self.age 是 18
 
obj2 = Foo('alex', 73)
print obj2.name    # 直接调用obj2对象的name属性
print obj2.age     # 直接调用obj2对象的age属性
obj2.detail()  # Python默认会将obj2传给self参数,即:obj1.detail(obj2),所以,此时方法内部的 self = obj2,即:self.name 是 alex ; self.age 是 78

用装饰器把 getter 和 setter 更好的封装起来

class Person():
    '''
        __xxxx 成为隐藏属性
        __name -> _Person__name

        使用 _xxx 作为私有属性,没有特殊需求,不要修改私有属性
        类一般使用属性或方法不可见可以使用单下划线
    '''
    # 使用一个
    def __init__(self,name):
        self.__name = name

    @property
    def name(self):
        return self.__name
    
    '''
        setter
        getter 方法更好的使用
        @property,将一个 get 方法,转换为对象属性
        @属性名.setter 讲一个 set 方法,转换为对象属性
        两者缺一不可
    '''
    # setter 方法的的装饰器: @属性名.setter
    @name.setter
    def name(self, name):
        self.__name = name

p = Person('猴赛雷')
p.name = 'aaa' # 使用属性的方式 调用 setter 和 getter
print(p.name)

继承

经典类新式类,从字面上可以看出一个老一个新,新的必然包含了跟多的功能,也是之后推荐的写法,从写法上区分的话,如果 当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。

# 经典类
class C1():
    pass
# 新式类
class C1(object):
    pass

# 子类继承
class C2(C1):
    pass

super

super获取父类信息,用法

  • super() 直接获取当前类的父类
  • super(a, self) 获取指定类(a)的父类
import os

class Rectangle:
    def __init__(self, length, width):
        self.length = length
        self.width = width

    def area(self):
        return self.length * self.width

    def perimeter(self):
        return 2 * self.length + 2 * self.width

class Square(Rectangle):
    def __init__(self, length):
        super().__init__(length, length) # 默认获取当前类的父类
        super(Square, self).__init__(length, length) # 获取自定义类Square,等效于上一条语句

class Cube(Square):
    def surface_area(self):
        face_area = super(Square, self).area()# 获取自定义类 Square
        return face_area * 6

    def volume(self):
        face_area = super(Square, self).area()
        return face_area * self.length

多继承

Python的类如果继承了多个类,搜索方法分别是:深度优先和广度优先

  • 经典类时,多继承情况下,会按照深度优先方式查找
  • 新式类时,多继承情况下,会按照广度优先方式查找

多态

Pyhon不支持Java和C#这一类强类型语言中多态的写法,但是原生多态,其Python崇尚“鸭子类型”。

  • 多态:它指的是一个声明为 A类型的变量,可能是指 A类型的对象,也可以是 A类型的任何子类对象
class Dog():
    # 父类:狗
    def __init__(self,name,age):
        self.name = name

        if age > 100:
            raise ValueError("狗狗的年龄不可能这么大")
        self.__age = age

    def getAge(self):
        return self.__age

    def setAge(self,age):
        if age > 100:
            raise ValueError ("狗狗的年龄不可能这么大")
        self.__age = age

    # 定义公共方法
    def bark(self):
        print(f"{self.name} can bark")

    def eat(self):
        print(f"{self.name} 喜欢吃鸡肉")

class husky(Dog):
    # 子类:哈士奇
    def play(self):
        print(f"{self.name} 会打滚")
    # 方法覆盖
    def eat(self):
        print(f"{self.name} 喜欢吃火腿")
    # 方法覆盖
    def bark(self):
        print(f"{self.name} 在叫,嘻嘻....")

class teddy(Dog):
    # 子类:泰迪
    def other(self):
        print(f"{self.name} 会杂技")

# 公用函数:狗叫 —— 多态
def dog_bark(dog):
    # dog可以是父类的对象,也可以是子类的对象
    if isinstance(dog, Dog):
        dog.bark()

d = Dog("阿拉斯加",3)
dog_bark(d)

d1 = husky("哈士奇:二哈",2)
dog_bark(d1)

d2 = teddy("泰迪:皮皮",1)
dog_bark(d2)

使用多态时,并不需要给每一个 子类定义一个调用 bark() 的方法,pipi_bark(), dandan_bark(),只需要定义一个 公用的dog_bark(), 在调用的时候给它传递对应的子类对象即可。

接口实现

接口的特点如下:

  • 1、类通过继承接口的方式,来继承接口的抽象方法;
  • 2、接口并不是类(虽然编写类和方法的方式很相似);
  • 3、类描述对象的属性和方法(实现接口的类,必须实现接口内所描述的所有方法,否则必须声明为抽象类);
  • 4、接口包含类要实现的方法(接口无法被实例化,但可以被实现);

总结:接口只定义规范,不负责具体实现(具体实现由具体的实现者完成)

python中也有interface的概念,但是python其本身不提供interface的实现,需要通过第三方扩展库来使用类似interface的功能,一般都是Zope.interface

  • Python面向对象编程之Zope.interface安装使用( @implementer)implements
# coding=utf-8
from zope.interface import Interface
from zope.interface.declarations import implementer
 
# 定义接口
class MyMiss(Interface):
    def imissyouatlost(self,miss):
        """Say i miss you at lost to miss"""
 
@implementer(MyMiss) # 继承接口
class Miss:
    def imissyouatlost(self,somebody):
        """Say i miss you at lost to somebody"""
        return "i miss you at lost, %s!" % somebody
 
if __name__ == '__main__':
    z = Miss()
    hi = z.imissyouatlost('Zy')
    print(hi)

设计模式

二十三种设计模式及其python实现

本文源码寄方于github

参考文献:

  • 《大话设计模式》——吴强
  • 《Python设计模式》——pythontip.com
  • 《23种设计模式》——http://www.cnblogs.com/beijiguangyong/

设计模式是经过总结、优化的,针对一些编程问题的可重用解决方案。设计模式不像那样能够直接作用于代码,设计模式更为高级,是一种必须在特定情形下实现的一种方法模板。设计模式不会绑定具体的编程语言。

  • 好的设计模式应该能够用大部分编程语言实现(如果做不到全部的话,具体取决于语言特性)。
  • 设计模式也是一把双刃剑,如果用在不恰当的情形下将会造成灾难,进而带来无穷的麻烦。然而如果设计模式在正确的时间被用在正确地地方,它将是你的救星。

“模式”就是为了解决一类特定问题而特别想出来的明智之举。

虽然被称为“设计模式”,但是它们同“设计“领域并非紧密联系。设计模式同传统意义上的分析、设计与实现不同,事实上设计模式将一个完整的理念根植于程序中,所以它可能出现在分析阶段或是更高层的设计阶段。很有趣的是因为设计模式的具体体现是程序代码,因此可能会让你认为它不会在具体实现阶段之前出现(事实上在进入具体实现阶段之前你都没有意识到正在使用具体的设计模式)。

可以通过程序设计的基本概念来理解模式:增加一个抽象层。抽象一个事物就是隔离任何具体细节,这么做的目的是为了将那些不变的核心部分从其他细节中分离出来。当你发现你程序中的某些部分经常因为某些原因改动,而你不想让这些改动的部分引发其他部分的改动,这时候你就需要思考那些不会变动的设计方法了。这么做不仅会使代码可维护性更高,而且会让代码更易于理解,从而降低开发成本。

三种最基本的设计模式:

  • 创建模式,提供实例化的方法,为适合的状况提供相应的对象创建方法。
  • 结构化模式,通常用来处理实体之间的关系,使得这些实体能够更好地协同工作。
  • 行为模式,用于在不同的实体建进行通信,为实体之间的通信提供更容易,更灵活的通信方法。

创建型

  1. Factory Method(工厂方法)
  2. Abstract Factory(抽象工厂)
  3. Builder(建造者)
  4. Prototype(原型)
  5. Singleton(单例)

Factory Method(工厂方法)

意图:

  • 定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类

适用性:

  • 当一个类不知道它所必须创建的对象的类的时候。
  • 当一个类希望由它的子类来指定它所创建的对象的时候。
  • 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
#!/usr/bin/python
#coding:utf8
'''
Factory Method
'''

class ChinaGetter:
    """A simple localizer a la gettext"""
    def __init__(self):
        self.trans = dict(dog=u"小狗", cat=u"小猫")
 
    def get(self, msgid):
        """We'll punt if we don't have a translation"""
        try:
            return self.trans[msgid]
        except KeyError:
            return str(msgid)

class EnglishGetter:
    """Simply echoes the msg ids"""
    def get(self, msgid):
        return str(msgid)

def get_localizer(language="English"):
    """The factory method"""
    languages = dict(English=EnglishGetter, China=ChinaGetter)
    return languages[language]()
 
# Create our localizers
e, g = get_localizer("English"), get_localizer("China")
# Localize some text
for msgid in "dog parrot cat bear".split():
    print(e.get(msgid), g.get(msgid))

Abstract Factory(抽象工厂)

意图:

  • 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 适用性:
  • 一个系统要独立于它的产品的创建、组合和表示时。
  • 一个系统要由多个产品系列中的一个来配置时。
  • 当要强调一系列相关的产品对象的设计以便进行联合使用时。
  • 当提供一个产品类库,而只想显示它们的接口而不是实现时。
#!/usr/bin/python
#coding:utf8
'''
Abstract Factory
'''

import random
 
class PetShop:
    """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.get_pet()
        print("This is a lovely", str(pet))
        print("It says", pet.speak())
        print("It eats", self.pet_factory.get_food())

# Stuff that our factory makes
class Dog:
    def speak(self):
        return "woof"
 
    def __str__(self):
        return "Dog"

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

# Factory classes
 
class DogFactory:
    def get_pet(self):
        return Dog()
 
    def get_food(self):
        return "dog food"

class CatFactory:
    def get_pet(self):
        return Cat()
 
    def get_food(self):
        return "cat food"

# Create the proper family
def get_factory():
    """Let's be dynamic!"""
    return random.choice([DogFactory, CatFactory])()
 
# Show pets with various factories
if __name__ == "__main__":
    shop = PetShop()
    for i in range(3):
        shop.pet_factory = get_factory()
        shop.show_pet()
        print("=" * 20)

Builder(建造者)

意图:

  • 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

适用性:

  • 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
  • 当构造过程必须允许被构造的对象有不同的表示时。
#!/usr/bin/python
#coding:utf8
 
"""
    Builder
"""
 
# 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: %s | Size: %s' % (self.floor, self.size)
 
# 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)

Prototype(原型)

意图:

  • 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

适用性:

  • 当要实例化的类是在运行时刻指定时,例如,通过动态装载;或者为了避免创建一个与产品类层次平行的工厂类层次时;或者当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
#!/usr/bin/python
#coding:utf8
'''
Prototype
'''

import copy
 
class Prototype:
    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

def main():
    class A:
        def __str__(self):
            return "I am A"
 
    a = A()
    prototype = Prototype()
    prototype.register_object('a', a)
    b = prototype.clone('a', a=1, b=2, c=3)
 
    print(a)
    print(b.a, b.b, b.c)
 
 
if __name__ == '__main__':
    main()

Singleton(单例)

意图:

  • 保证一个类仅有一个实例,并提供一个访问它的全局访问点。

适用性:

  • 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
  • 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
#!/usr/bin/python
#coding:utf8
'''
Singleton
'''
 
class Singleton(object):
    ''''' A python style singleton '''
 
    def __new__(cls, *args, **kw):
        if not hasattr(cls, '_instance'):
            org = super(Singleton, cls)
            cls._instance = org.__new__(cls, *args, **kw)
        return cls._instance
 
 
if __name__ == '__main__':
    class SingleSpam(Singleton):
        def __init__(self, s):
            self.s = s
 
        def __str__(self):
            return self.s
 
 
    s1 = SingleSpam('spam')
    print id(s1), s1
    s2 = SingleSpam('spa')
    print id(s2), s2
    print id(s1), s1

结构型

Adapter Class/Object(适配器)

意图:

  • 将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

适用性:

  • 你想使用一个已经存在的类,而它的接口不符合你的需求。
  • 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
  • (仅适用于对象Adapter )你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
#!/usr/bin/python
#coding:utf8
'''
Adapter
'''
 
import os
 
 
class Dog(object):
    def __init__(self):
        self.name = "Dog"
 
    def bark(self):
        return "woof!"
 
 
class Cat(object):
    def __init__(self):
        self.name = "Cat"
 
    def meow(self):
        return "meow!"
 
 
class Human(object):
    def __init__(self):
        self.name = "Human"
 
    def speak(self):
        return "'hello'"

class Car(object):
    def __init__(self):
        self.name = "Car"
 
    def make_noise(self, octane_level):
        return "vroom%s" % ("!" * octane_level)

class Adapter(object):
    """
    Adapts an object by replacing methods.
    Usage:
    dog = Dog
    dog = Adapter(dog, dict(make_noise=dog.bark))
    """
    def __init__(self, obj, adapted_methods):
        """We set the adapted methods in the object's dict"""
        self.obj = obj
        self.__dict__.update(adapted_methods)
 
    def __getattr__(self, attr):
        """All non-adapted calls are passed to the object"""
        return getattr(self.obj, attr)

def main():
    objects = []
    dog = Dog()
    objects.append(Adapter(dog, dict(make_noise=dog.bark)))
    cat = Cat()
    objects.append(Adapter(cat, dict(make_noise=cat.meow)))
    human = Human()
    objects.append(Adapter(human, dict(make_noise=human.speak)))
    car = Car()
    car_noise = lambda: car.make_noise(3)
    objects.append(Adapter(car, dict(make_noise=car_noise)))
 
    for obj in objects:
        print "A", obj.name, "goes", obj.make_noise()
 
 
if __name__ == "__main__":
    main()

Bridge(桥接)

意图:

  • 将抽象部分与它的实现部分分离,使它们都可以独立地变化。

适用性:

  • 你不希望在抽象和它的实现部分之间有一个固定的绑定关系。例如这种情况可能是因为,在程序运行时刻实现部分应可以被选择或者切换。
  • 类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。这时Bridge 模式使你可以对不同的抽象接口和实现部分进行组合,并分别对它们进行扩充。
  • 对一个抽象的实现部分的修改应对客户不产生影响,即客户的代码不必重新编译。
  • (C++)你想对客户完全隐藏抽象的实现部分。在C++中,类的表示在类接口中是可见的。
  • 有许多类要生成。这样一种类层次结构说明你必须将一个对象分解成两个部分。Rumbaugh 称这种类层次结构为“嵌套的普化”(nested generalizations )。
  • 你想在多个对象间共享实现(可能使用引用计数),但同时要求客户并不知道这一点。一个简单的例子便是Coplien 的String 类[ Cop92 ],在这个类中多个对象可以共享同一个字符串表示(StringRep)。
#!/usr/bin/python
#coding:utf8
'''
Bridge
'''

# ConcreteImplementor 1/2
class DrawingAPI1(object):
    def draw_circle(self, x, y, radius):
        print('API1.circle at {}:{} radius {}'.format(x, y, radius))
 
 
# ConcreteImplementor 2/2
class DrawingAPI2(object):
    def draw_circle(self, x, y, radius):
        print('API2.circle at {}:{} radius {}'.format(x, y, radius))
 
 
# Refined Abstraction
class CircleShape(object):
    def __init__(self, x, y, radius, drawing_api):
        self._x = x
        self._y = y
        self._radius = radius
        self._drawing_api = drawing_api
 
    # low-level i.e. Implementation specific
    def draw(self):
        self._drawing_api.draw_circle(self._x, self._y, self._radius)
 
    # high-level i.e. Abstraction specific
    def scale(self, pct):
        self._radius *= pct
 
 
def main():
    shapes = (
        CircleShape(1, 2, 3, DrawingAPI1()),
        CircleShape(5, 7, 11, DrawingAPI2())
    )
 
    for shape in shapes:
        shape.scale(2.5)
        shape.draw()
 
 
if __name__ == '__main__':
    main()

Composite(组合)

意图:

  • 将对象组合成树形结构以表示“部分-整体”的层次结构。C o m p o s i t e 使得用户对单个对象和组合对象的使用具有一致性。

适用性:

  • 你想表示对象的部分-整体层次结构。
  • 你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
#!/usr/bin/python
#coding:utf8
 
"""
Composite
"""
 
class Component:
    def __init__(self,strName):
        self.m_strName = strName
    def Add(self,com):
        pass
    def Display(self,nDepth):
        pass
 
class Leaf(Component):
    def Add(self,com):
        print "leaf can't add"
    def Display(self,nDepth):
        strtemp = "-" * nDepth
        strtemp=strtemp+self.m_strName
        print strtemp
 
class Composite(Component):
    def __init__(self,strName):
        self.m_strName = strName
        self.c = []
    def Add(self,com):
        self.c.append(com)
    def Display(self,nDepth):
        strtemp = "-"*nDepth
        strtemp=strtemp+self.m_strName
        print strtemp
        for com in self.c:
            com.Display(nDepth+2)
 
if __name__ == "__main__":
    p = Composite("Wong")
    p.Add(Leaf("Lee"))
    p.Add(Leaf("Zhao"))
    p1 = Composite("Wu")
    p1.Add(Leaf("San"))
    p.Add(p1)
    p.Display(1);

Decorator(装饰)

意图:

  • 动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator 模式相比生成子类更为灵活。 适用性:
  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
  • 处理那些可以撤消的职责。
  • 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
#!/usr/bin/python
#coding:utf8
'''
Decorator
'''
 
class foo(object):
    def f1(self):
        print("original f1")
 
    def f2(self):
        print("original f2")
 
 
class foo_decorator(object):
    def __init__(self, decoratee):
        self._decoratee = decoratee
 
    def f1(self):
        print("decorated f1")
        self._decoratee.f1()
 
    def __getattr__(self, name):
        return getattr(self._decoratee, name)
 
u = foo()
v = foo_decorator(u)
v.f1()
v.f2()

Facade(外观)

意图:

  • 为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

适用性:

  • 当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。Facade 可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过facade层。
  • 客户程序与抽象类的实现部分之间存在着很大的依赖性。引入facade 将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。
  • 当你需要构建一个层次结构的子系统时,使用facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让它们仅通过facade进行通讯,从而简化了它们之间的依赖关系。
#!/usr/bin/python
#coding:utf8
'''
Decorator
'''
import time
 
SLEEP = 0.5
 
# Complex Parts
class TC1:
    def run(self):
        print("###### In Test 1 ######")
        time.sleep(SLEEP)
        print("Setting up")
        time.sleep(SLEEP)
        print("Running test")
        time.sleep(SLEEP)
        print("Tearing down")
        time.sleep(SLEEP)
        print("Test Finished\n")
 
 
class TC2:
    def run(self):
        print("###### In Test 2 ######")
        time.sleep(SLEEP)
        print("Setting up")
        time.sleep(SLEEP)
        print("Running test")
        time.sleep(SLEEP)
        print("Tearing down")
        time.sleep(SLEEP)
        print("Test Finished\n")

class TC3:
    def run(self):
        print("###### In Test 3 ######")
        time.sleep(SLEEP)
        print("Setting up")
        time.sleep(SLEEP)
        print("Running test")
        time.sleep(SLEEP)
        print("Tearing down")
        time.sleep(SLEEP)
        print("Test Finished\n")

# Facade
class TestRunner:
    def __init__(self):
        self.tc1 = TC1()
        self.tc2 = TC2()
        self.tc3 = TC3()
        self.tests = [i for i in (self.tc1, self.tc2, self.tc3)]
 
    def runAll(self):
        [i.run() for i in self.tests]
 
# Client
if __name__ == '__main__':
    testrunner = TestRunner()
    testrunner.runAll()

Flyweight(享元)

意图:

  • 运用共享技术有效地支持大量细粒度的对象。

适用性:

  • 一个应用程序使用了大量的对象。
  • 完全由于使用大量的对象,造成很大的存储开销。
  • 对象的大多数状态都可变为外部状态。
  • 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
  • 应用程序不依赖于对象标识。由于Flyweight 对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。
#!/usr/bin/python
#coding:utf8
'''
Flyweight
'''
 
import weakref 
 
 
class Card(object):
    """The object pool. Has builtin reference counting"""
    _CardPool = weakref.WeakValueDictionary()
 
    """Flyweight implementation. If the object exists in the
    pool just return it (instead of creating a new one)"""
    def __new__(cls, value, suit):        
        obj = Card._CardPool.get(value + suit, None)        
        if not obj:            
            obj = object.__new__(cls)            
            Card._CardPool[value + suit] = obj            
            obj.value, obj.suit = value, suit         
        return obj
 
    # def __init__(self, value, suit):        
    #     self.value, self.suit = value, suit     
 
    def __repr__(self):        
        return "<Card: %s%s>" % (self.value, self.suit)     
 
 
if __name__ == '__main__':
    # comment __new__ and uncomment __init__ to see the difference
    c1 = Card('9', 'h')
    c2 = Card('9', 'h')
    print(c1, c2)
    print(c1 == c2)
    print(id(c1), id(c2))

Proxy(代理)

意图:

  • 为其他对象提供一种代理以控制对这个对象的访问。 适用性:
  • 在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用Proxy模式。下面是一 些可以使用Proxy 模式常见情况:
    • 1) 远程代理(Remote Proxy )为一个对象在不同的地址空间提供局部代表。 NEXTSTEP[Add94] 使用NXProxy 类实现了这一目的。Coplien[Cop92] 称这种代理为“大使” (Ambassador )。
    • 2) 虚代理(Virtual Proxy )根据需要创建开销很大的对象。在动机一节描述的ImageProxy 就是这样一种代理的例子。
    • 3) 保护代理(Protection Proxy )控制对原始对象的访问。保护代理用于对象应该有不同 的访问权限的时候。例如,在Choices 操作系统[ CIRM93]中KemelProxies为操作系统对象提供 了访问保护。
    • 4) 智能指引(Smart Reference )取代了简单的指针,它在访问对象时执行一些附加操作。 它的典型用途包括:对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它(也称为SmartPointers[Ede92 ] )。
  • 当第一次引用一个持久对象时,将它装入内存。
  • 在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。
#!/usr/bin/python
#coding:utf8
'''
Proxy
'''
 
import time
 
class SalesManager:
    def work(self):
        print("Sales Manager working...")
 
    def talk(self):
        print("Sales Manager ready to talk")
 
class Proxy:
    def __init__(self):
        self.busy = 'No'
        self.sales = None
 
    def work(self):
        print("Proxy checking for Sales Manager availability")
        if self.busy == 'No':
            self.sales = SalesManager()
            time.sleep(2)
            self.sales.talk()
        else:
            time.sleep(2)
            print("Sales Manager is busy")

if __name__ == '__main__':
    p = Proxy()
    p.work()
    p.busy = 'Yes'
    p.work()

行为型

Interpreter(解释器)

意图:

  • 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

适用性:

  • 当有一个语言需要解释执行, 并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。而当存在以下情况时该模式效果最好:
  • 该文法简单对于复杂的文法, 文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式, 这样可以节省空间而且还可能节省时间。
  • 效率不是一个关键问题最高效的解释器通常不是通过直接解释语法分析树实现的, 而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下, 转换器仍可用解释器模式实现, 该模式仍是有用的。
#!/usr/bin/python
#coding:utf8
'''
Interpreter
'''
 
class Context:
    def __init__(self):
        self.input=""
        self.output=""
 
class AbstractExpression:
    def Interpret(self,context):
        pass
 
class Expression(AbstractExpression):
    def Interpret(self,context):
        print "terminal interpret"
 
class NonterminalExpression(AbstractExpression):
    def Interpret(self,context):
        print "Nonterminal interpret"
 
if __name__ == "__main__":
    context= ""
    c = []
    c = c + [Expression()]
    c = c + [NonterminalExpression()]
    c = c + [Expression()]
    c = c + [Expression()]
    for a in c:
        a.Interpret(context)

Template Method(模板方法)

意图:

  • 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。TemplateMethod 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

适用性:

  • 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
  • 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。这是Opdyke 和Johnson所描述过的“重分解以一般化”的一个很好的例子[ OJ93 ]。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
  • 控制子类扩展。模板方法只在特定点调用“hook ”操作(参见效果一节),这样就只允许在这些点进行扩展。
#!/usr/bin/python
#coding:utf8
'''
Template Method
'''
 
ingredients = "spam eggs apple"
line = '-' * 10
 
# Skeletons
def iter_elements(getter, action):    
    """Template skeleton that iterates items"""     
    for element in getter():        
        action(element)    
        print(line) 
 
def rev_elements(getter, action):
    """Template skeleton that iterates items in reverse order"""     
    for element in getter()[::-1]:        
        action(element)    
        print(line) 
 
# Getters
def get_list():    
    return ingredients.split() 
 
def get_lists():
    return [list(x) for x in ingredients.split()] 
 
# Actions
def print_item(item):    
    print(item) 
 
def reverse_item(item):
    print(item[::-1]) 
 
# Makes templates
def make_template(skeleton, getter, action):    
    """Instantiate a template method with getter and action"""    
    def template():        
        skeleton(getter, action)    
    return template 
 
# Create our template functions
templates = [make_template(s, g, a)             
             for g in (get_list, get_lists)             
             for a in (print_item, reverse_item)             
             for s in (iter_elements, rev_elements)] 
 
# Execute them
for template in templates:    
    template()

Chain of Responsibility(责任链)

意图:

  • 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

适用性:

  • 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
  • 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
  • 可处理一个请求的对象集合应被动态指定。
#!/usr/bin/python
#coding:utf8
 
"""
Chain
"""
class Handler:
    def successor(self, successor):
        self.successor = successor
 
class ConcreteHandler1(Handler):
    def handle(self, request):
        if request > 0 and request <= 10:
            print("in handler1")
        else:
            self.successor.handle(request)
 
class ConcreteHandler2(Handler):
    def handle(self, request):
        if request > 10 and request <= 20:
            print("in handler2")
        else:
            self.successor.handle(request)
 
class ConcreteHandler3(Handler):
    def handle(self, request):
        if request > 20 and request <= 30:
            print("in handler3")
        else:
            print('end of chain, no handler for {}'.format(request))
 
class Client:
    def __init__(self):
        h1 = ConcreteHandler1()
        h2 = ConcreteHandler2()
        h3 = ConcreteHandler3()
 
        h1.successor(h2)
        h2.successor(h3)
 
        requests = [2, 5, 14, 22, 18, 3, 35, 27, 20]
        for request in requests:
            h1.handle(request)
 
if __name__ == "__main__":
    client = Client()

Command(命令)

意图:

  • 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。

适用性:

  • 抽象出待执行的动作以参数化某对象,你可用过程语言中的回调(call back)函数表达这种参数化机制。所谓回调函数是指函数先在某处注册,而它将在稍后某个需要的时候被调用。Command 模式是回调机制的一个面向对象的替代品。
  • 在不同的时刻指定、排列和执行请求。一个Command对象可以有一个与初始请求无关的生存期。如果一个请求的接收者可用一种与地址空间无关的方式表达,那么就可将负责该请求的命令对象传送给另一个不同的进程并在那儿实现该请求。
  • 支持取消操作。Command的Excute 操作可在实施操作前将状态存储起来,在取消操作时这个状态用来消除该操作的影响。Command 接口必须添加一个Unexecute操作,该操作取消上一次Execute调用的效果。执行的命令被存储在一个历史列表中。可通过向后和向前遍历这一列表并分别调用Unexecute和Execute来实现重数不限的“取消”和“重做”。
  • 支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。在Command接口中添加装载操作和存储操作,可以用来保持变动的一个一致的修改日志。从崩溃中恢复的过程包括从磁盘中重新读入记录下来的命令并用Execute操作重新执行它们。
  • 用构建在原语操作上的高层操作构造一个系统。这样一种结构在支持事务( transaction)的信息系统中很常见。一个事务封装了对数据的一组变动。Command模式提供了对事务进行建模的方法。Command有一个公共的接口,使得你可以用同一种方式调用所有的事务。同时使用该模式也易于添加新事务以扩展系统。
#!/usr/bin/python
#coding:utf8
 
"""
Command
"""
import os
 
class MoveFileCommand(object):
    def __init__(self, src, dest):
        self.src = src
        self.dest = dest
 
    def execute(self):
        self()
 
    def __call__(self):
        print('renaming {} to {}'.format(self.src, self.dest))
        os.rename(self.src, self.dest)
 
    def undo(self):
        print('renaming {} to {}'.format(self.dest, self.src))
        os.rename(self.dest, self.src)
 
 
if __name__ == "__main__":
    command_stack = []
 
    # commands are just pushed into the command stack
    command_stack.append(MoveFileCommand('foo.txt', 'bar.txt'))
    command_stack.append(MoveFileCommand('bar.txt', 'baz.txt'))
 
    # they can be executed later on
    for cmd in command_stack:
        cmd.execute()
 
    # and can also be undone at will
    for cmd in reversed(command_stack):
        cmd.undo()

Iterator(迭代器)

意图:

  • 提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。

适用性:

  • 访问一个聚合对象的内容而无需暴露它的内部表示。
  • 支持对聚合对象的多种遍历。
  • 为遍历不同的聚合结构提供一个统一的接口(即, 支持多态迭代)。
#!/usr/bin/python
#coding:utf8
'''
Interator
'''
def count_to(count):
    """Counts by word numbers, up to a maximum of five"""
    numbers = ["one", "two", "three", "four", "five"]
    # enumerate() returns a tuple containing a count (from start which
    # defaults to 0) and the values obtained from iterating over sequence
    for pos, number in zip(range(count), numbers):
        yield number
 
# Test the generator
count_to_two = lambda: count_to(2)
count_to_five = lambda: count_to(5)
 
print('Counting to two...')
for number in count_to_two():
    print number
 
print " "
 
print('Counting to five...')
for number in count_to_five():
    print number
 
print " "

Mediator(中介者)

意图:

  • 用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

适用性:

  • 一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。
  • 一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
  • 想定制一个分布在多个类中的行为,而又不想生成太多的子类。
#!/usr/bin/python
#coding:utf8
'''
Mediator
'''
"""http://dpip.testingperspective.com/?p=28"""
 
import time
 
class TC:
    def __init__(self):
        self._tm = tm
        self._bProblem = 0
 
    def setup(self):
        print("Setting up the Test")
        time.sleep(1)
        self._tm.prepareReporting()
 
    def execute(self):
        if not self._bProblem:
            print("Executing the test")
            time.sleep(1)
        else:
            print("Problem in setup. Test not executed.")
 
    def tearDown(self):
        if not self._bProblem:
            print("Tearing down")
            time.sleep(1)
            self._tm.publishReport()
        else:
            print("Test not executed. No tear down required.")
 
    def setTM(self, TM):
        self._tm = tm
 
    def setProblem(self, value):
        self._bProblem = value
 
class Reporter:
    def __init__(self):
        self._tm = None
 
    def prepare(self):
        print("Reporter Class is preparing to report the results")
        time.sleep(1)
 
    def report(self):
        print("Reporting the results of Test")
        time.sleep(1)
 
    def setTM(self, TM):
        self._tm = tm
 
class DB:
    def __init__(self):
        self._tm = None
 
    def insert(self):
        print("Inserting the execution begin status in the Database")
        time.sleep(1)
        #Following code is to simulate a communication from DB to TC
        import random
        if random.randrange(1, 4) == 3:
            return -1
 
    def update(self):
        print("Updating the test results in Database")
        time.sleep(1)
 
    def setTM(self, TM):
        self._tm = tm
 
class TestManager:
    def __init__(self):
        self._reporter = None
        self._db = None
        self._tc = None
 
    def prepareReporting(self):
        rvalue = self._db.insert()
        if rvalue == -1:
            self._tc.setProblem(1)
            self._reporter.prepare()
 
    def setReporter(self, reporter):
        self._reporter = reporter
 
    def setDB(self, db):
        self._db = db
 
    def publishReport(self):
        self._db.update()
        rvalue = self._reporter.report()
 
    def setTC(self, tc):
        self._tc = tc
 
 
if __name__ == '__main__':
    reporter = Reporter()
    db = DB()
    tm = TestManager()
    tm.setReporter(reporter)
    tm.setDB(db)
    reporter.setTM(tm)
    db.setTM(tm)
    # For simplification we are looping on the same test.
    # Practically, it could be about various unique test classes and their
    # objects
    while (True):
        tc = TC()
        tc.setTM(tm)
        tm.setTC(tc)
        tc.setup()
        tc.execute()
        tc.tearDown()

Memento(备忘录)

意图:

  • 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

适用性:

  • 必须保存一个对象在某一个时刻的(部分)状态, 这样以后需要时它才能恢复到先前的状态。
  • 如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。
#!/usr/bin/python
#coding:utf8
'''
Memento
'''
 
import copy
 
def Memento(obj, deep=False):
    state = (copy.copy, copy.deepcopy)[bool(deep)](obj.__dict__)
 
    def Restore():
        obj.__dict__.clear()
        obj.__dict__.update(state)
    return Restore
 
class Transaction:
    """A transaction guard. This is really just
      syntactic suggar arount a memento closure.
      """
    deep = False
 
    def __init__(self, *targets):
        self.targets = targets
        self.Commit()
 
    def Commit(self):
        self.states = [Memento(target, self.deep) for target in self.targets]
 
    def Rollback(self):
        for st in self.states:
            st()
 
class transactional(object):
    """Adds transactional semantics to methods. Methods decorated  with
    @transactional will rollback to entry state upon exceptions.
    """
    def __init__(self, method):
        self.method = method
 
    def __get__(self, obj, T):
        def transaction(*args, **kwargs):
            state = Memento(obj)
            try:
                return self.method(obj, *args, **kwargs)
            except:
                state()
                raise
        return transaction
 
class NumObj(object):
    def __init__(self, value):
        self.value = value
 
    def __repr__(self):
        return '<%s: %r>' % (self.__class__.__name__, self.value)
 
    def Increment(self):
        self.value += 1
 
    @transactional
    def DoStuff(self):
        self.value = '1111'  # <- invalid value
        self.Increment()     # <- will fail and rollback
 
if __name__ == '__main__':
    n = NumObj(-1)
    print(n)
    t = Transaction(n)
    try:
        for i in range(3):
            n.Increment()
            print(n)
        t.Commit()
        print('-- commited')
        for i in range(3):
            n.Increment()
            print(n)
        n.value += 'x'  # will fail
        print(n)
    except:
        t.Rollback()
        print('-- rolled back')
    print(n)
    print('-- now doing stuff ...')
    try:
        n.DoStuff()
    except:
        print('-> doing stuff failed!')
        import traceback
        traceback.print_exc(0)
        pass
    print(n)

Observer(观察者)

意图:

  • 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。

适用性:

  • 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
  • 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
  • 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
#!/usr/bin/python
#coding:utf8
'''
Observer
'''
 
 
class Subject(object):
    def __init__(self):
        self._observers = []
 
    def attach(self, observer):
        if not observer in self._observers:
            self._observers.append(observer)
 
    def detach(self, observer):
        try:
            self._observers.remove(observer)
        except ValueError:
            pass
 
    def notify(self, modifier=None):
        for observer in self._observers:
            if modifier != observer:
                observer.update(self)
 
# Example usage
class Data(Subject):
    def __init__(self, name=''):
        Subject.__init__(self)
        self.name = name
        self._data = 0
 
    @property
    def data(self):
        return self._data
 
    @data.setter
    def data(self, value):
        self._data = value
        self.notify()
 
class HexViewer:
    def update(self, subject):
        print('HexViewer: Subject %s has data 0x%x' %
              (subject.name, subject.data))
 
class DecimalViewer:
    def update(self, subject):
        print('DecimalViewer: Subject %s has data %d' %
              (subject.name, subject.data))
 
# Example usage...
def main():
    data1 = Data('Data 1')
    data2 = Data('Data 2')
    view1 = DecimalViewer()
    view2 = HexViewer()
    data1.attach(view1)
    data1.attach(view2)
    data2.attach(view2)
    data2.attach(view1)
 
    print("Setting Data 1 = 10")
    data1.data = 10
    print("Setting Data 2 = 15")
    data2.data = 15
    print("Setting Data 1 = 3")
    data1.data = 3
    print("Setting Data 2 = 5")
    data2.data = 5
    print("Detach HexViewer from data1 and data2.")
    data1.detach(view2)
    data2.detach(view2)
    print("Setting Data 1 = 10")
    data1.data = 10
    print("Setting Data 2 = 15")
    data2.data = 15
 
if __name__ == '__main__':
    main()

State(状态)

意图:

  • 允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

适用性:

  • 一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。
  • 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常, 有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。
#!/usr/bin/python
#coding:utf8
'''
State
'''
 
class State(object):
    """Base state. This is to share functionality"""
 
    def scan(self):
        """Scan the dial to the next station"""
        self.pos += 1
        if self.pos == len(self.stations):
            self.pos = 0
        print("Scanning... Station is", self.stations[self.pos], self.name)
 
 
class AmState(State):
    def __init__(self, radio):
        self.radio = radio
        self.stations = ["1250", "1380", "1510"]
        self.pos = 0
        self.name = "AM"
 
    def toggle_amfm(self):
        print("Switching to FM")
        self.radio.state = self.radio.fmstate
 
class FmState(State):
    def __init__(self, radio):
        self.radio = radio
        self.stations = ["81.3", "89.1", "103.9"]
        self.pos = 0
        self.name = "FM"
 
    def toggle_amfm(self):
        print("Switching to AM")
        self.radio.state = self.radio.amstate
 
class Radio(object):
    """A radio.     It has a scan button, and an AM/FM toggle switch."""
    def __init__(self):
        """We have an AM state and an FM state"""
        self.amstate = AmState(self)
        self.fmstate = FmState(self)
        self.state = self.amstate
 
    def toggle_amfm(self):
        self.state.toggle_amfm()
 
    def scan(self):
        self.state.scan()
 
# Test our radio out
if __name__ == '__main__':
    radio = Radio()
    actions = [radio.scan] * 2 + [radio.toggle_amfm] + [radio.scan] * 2
    actions = actions * 2
 
    for action in actions:
        action()

Strategy(策略)

意图:

  • 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

适用性:

  • 许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
  • 需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时[H087] ,可以使用策略模式。
  • 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
  • 一个类定义了多种行为, 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
#!/usr/bin/python
#coding:utf8
"""
Strategy
In most of other languages Strategy pattern is implemented via creating some base strategy interface/abstract class and
subclassing it with a number of concrete strategies (as we can see at http://en.wikipedia.org/wiki/Strategy_pattern),
however Python supports higher-order functions and allows us to have only one class and inject functions into it's
instances, as shown in this example.
"""
import types
 
 
class StrategyExample:
    def __init__(self, func=None):
        self.name = 'Strategy Example 0'        
        if func is not None:
            self.execute = types.MethodType(func, self)     
 
    def execute(self):        
        print(self.name)  
 
def execute_replacement1(self):
    print(self.name + ' from execute 1')  
 
def execute_replacement2(self):
    print(self.name + ' from execute 2') 
 
if __name__ == '__main__':
    strat0 = StrategyExample()    
 
    strat1 = StrategyExample(execute_replacement1)
    strat1.name = 'Strategy Example 1'    
 
    strat2 = StrategyExample(execute_replacement2)
    strat2.name = 'Strategy Example 2'
 
    strat0.execute()
    strat1.execute()    
    strat2.execute()

Visitor(访问者)

意图:

  • 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。TemplateMethod 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

适用性:

  • 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
  • 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。这是Opdyke和Johnson所描述过的“重分解以一般化”的一个很好的例子[OJ93]。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
  • 控制子类扩展。模板方法只在特定点调用“hook ”操作(参见效果一节),这样就只允许在这些点进行扩展。
#!/usr/bin/python
#coding:utf8
'''
Visitor
'''
class Node(object):
    pass
 
class A(Node):
    pass
 
class B(Node):
    pass
 
class C(A, B):
    pass
 
class Visitor(object):
    def visit(self, node, *args, **kwargs):
        meth = None
        for cls in node.__class__.__mro__:
            meth_name = 'visit_'+cls.__name__
            meth = getattr(self, meth_name, None)
            if meth:
                break
 
        if not meth:
            meth = self.generic_visit
        return meth(node, *args, **kwargs)
 
    def generic_visit(self, node, *args, **kwargs):
        print('generic_visit '+node.__class__.__name__)
 
    def visit_B(self, node, *args, **kwargs):
        print('visit_B '+node.__class__.__name__)
 
a = A()
b = B()
c = C()
visitor = Visitor()
visitor.visit(a)
visitor.visit(b)
visitor.visit(c)

单例模式

【2021-7-14】单例设计模式的python实现

单例模式就是确保一个类只有一个实例.当你希望整个系统中,某个类只有一个实例时,单例模式就派上了用场.

  • 比如,某个服务器的配置信息存在在一个文件中,客户端通过AppConfig类来读取配置文件的信息.如果程序的运行的过程中,很多地方都会用到配置文件信息,则就需要创建很多的AppConfig实例,这样就导致内存中有很多AppConfig对象的实例,造成资源的浪费.其实这个时候AppConfig我们希望它只有一份,就可以使用单例模式.

模块实现

python的模块就是天然的单例模式,因为模块在第一次导入的时候,会生成.pyc文件,当第二次导入的时候,就会直接加载.pyc文件,而不是再次执行模块代码.如果我们把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了

新建一个python模块叫singleton, 文件名 mysingleton.py

class Singleton(object):
    def foo(self):
        pass
singleton = Singleton()

调用时:

from singleton.mysingleton import singleton

装饰器实现

装饰器里面的外层变量定义一个字典,里面存放这个类的实例.当第一次创建的收,就将这个实例保存到这个字典中. 然后以后每次创建对象的时候,都去这个字典中判断一下,如果已经被实例化,就直接取这个实例对象.如果不存在就保存到字典中.

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/6 10:22'

def singleton(cls):
    # 单下划线的作用是这个变量只能在当前模块里访问,仅仅是一种提示作用
    # 创建一个字典用来保存类的实例对象
    _instance = {}

    def _singleton(*args, **kwargs):
        # 先判断这个类有没有对象
        if cls not in _instance:
            _instance[cls] = cls(*args, **kwargs)  # 创建一个对象,并保存到字典当中
        # 将实例对象返回
        return _instance[cls]

    return _singleton

@singleton
class A(object):
    a = 1

    def __init__(self, x=0):
        self.x = x
        print('这是A的类的初始化方法')

a1 = A(2)
a2 = A(3)
print(id(a1), id(a2))

类实现

调用类的instance方法,这样有一个弊端就是在使用类创建的时候,并不是单例了.也就是说在创建类的时候一定要用类里面规定的方法创建

注意:

  • 这样的单例模式在单线程下是安全的,但是如果遇到多线程,就会出现问题.如果遇到多个线程同时创建这个类的实例的时候就会出现问题.
# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/6 11:06'

class Singleton(object):
    def __init__(self,*args,**kwargs):
        pass

    @classmethod
    def get_instance(cls, *args, **kwargs):
        # 利用反射,看看这个类有没有_instance属性
        if not hasattr(Singleton, '_instance'):
            Singleton._instance = Singleton(*args, **kwargs)

        return Singleton._instance

s1 = Singleton()  # 使用这种方式创建实例的时候,并不能保证单例
s2 = Singleton.get_instance()  # 只有使用这种方式创建的时候才可以实现单例
s3 = Singleton()
s4 = Singleton.get_instance()

print(id(s1), id(s2), id(s3), id(s4))

多线程安全版本

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/6 11:26'
import threading


class Singleton(object):
    def __init__(self, *args, **kwargs):
        time.sleep(1) # init时加阻塞,以便暴露问题
        pass

    @classmethod
    def get_instance(cls, *args, **kwargs):
        if not hasattr(Singleton, '_instance'):
            Singleton._instance = Singleton(*args, **kwargs)

        return Singleton._instance

def task(arg):
    obj = Singleton.get_instance(arg)
    print(obj)

for i in range(10):
    t = threading.Thread(target=task, args=[i, ])
    t.start()

结果创建了10个不同的实例对象,因为在一个对象创建的过程中,另外一个对象也创建了.当它判断的时候,会先去获取_instance属性,因为这个时候还没有,它就会调用init()方法.结果就是调用了10次,然后就创建了10个对象.

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/6 11:38'

import time
import threading

class Singleton(object):
    _instance_lock = threading.Lock()

    def __init__(self,*args,**kwargs):
        time.sleep(1)

    @classmethod
    def get_instance(cls,*args,**kwargs):
        if not hasattr(Singleton,'_instance'):
            with Singleton._instance_lock:
                if not hasattr(Singleton,'_instance'):
                    Singleton._instance = Singleton(*args,**kwargs)

        return Singleton._instance

def task(arg):
    obj = Singleton.get_instance(arg)
    print(obj)

for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()

obj = Singleton.get_instance()
print(obj)

这种方式创建的单例,必须使用Singleton_get_instance()方法,如果使用Singleton()的话,得到的并不是单例.所以我们推荐使用__new__()方法来创建单例,这样创建的单例可以使用类名()的方法进行实例化对象

基于__new__方法实现的单例模式(推荐使用,方便)

知识点:

  • 一个对象的实例化过程是先执行类的__new__方法,如果没有写,默认会调用object的__new__方法,返回一个实例化对象,然后再调用__init__方法,对这个对象进行初始化,我们可以根据这个实现单例.
  • 在一个类的__new__方法中先判断是不是存在实例,如果存在实例,就直接返回,如果不存在实例就创建.
# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/6 13:36'
import threading


class Singleton(object):
    _instance_lock = threading.Lock()

    def __init__(self, *args, **kwargs):
        pass

    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            with Singleton._instance_lock:
                if not hasattr(cls, '_instance'):
                    Singleton._instance = super().__new__(cls)

            return Singleton._instance

obj1 = Singleton()
obj2 = Singleton()
print(obj1, obj2)

def task(arg):
    obj = Singleton()
    print(obj)

for i in range(10):
    t = threading.Thread(target=task, args=[i, ])
    t.start()

结束


支付宝打赏 微信打赏

~ 海内存知已,天涯若比邻 ~

Share

Related Posts

上一篇 Python GUI 开发

标题:Python GUI 开发

摘要:Python GUI 开发知识点

标题:互联网人职业发展之路-Future for the tech

摘要:陈皓的心得,技术人员如何规划未来,中年危机如何破解?值得深思,反复研读

站内可视化导航

文章可视化导读:鼠标划过图形块时,如果出现蓝色光环, 点击即可跳转到对应主题

Comments

--disqus--

    Content
    My Moment ( 微信公众号 )
    欢迎关注鹤啸九天