目录
一、创建型模式
1. 单例模式
1.1请手写一个单例
#encoding=utf8 import threading import time #这里使用方法__new__来实现单例模式 class Singleton(object):#抽象单例 def __new__(cls, *args, **kw): if not hasattr(cls, '_instance'): orig = super(Singleton, cls) cls._instance = orig.__new__(cls, *args, **kw) return cls._instance #总线 class Bus(Singleton): lock = threading.RLock() def sendData(self,data): self.lock.acquire() time.sleep(3) print "Sending Signal Data...",data self.lock.release() #线程对象,为更加说明单例的含义,这里将Bus对象实例化写在了run里 class VisitEntity(threading.Thread): my_bus="" name="" def getName(self): return self.name def setName(self, name): self.name=name def run(self): self.my_bus=Bus() self.my_bus.sendData(self.name) if __name__=="__main__": for i in range(3): print "Entity %d begin to run..."%i my_entity=VisitEntity() my_entity.setName("Entity_"+str(i)) my_entity.start()
1.2单例模式的优点和应用?
单例模式的优点: 1、由于单例模式要求在全局内只有一个实例,因而可以节省比较多的内存空间; 2、全局只有一个接入点,可以更好地进行数据同步控制,避免多重占用; 3、单例可长驻内存,减少系统开销。 单例模式的应用举例: 1、生成全局惟一的序列号; 2、访问全局复用的惟一资源,如磁盘、总线等; 3、单个对象占用的资源过多,如数据库等; 4、系统全局统一管理,如Windows下的Task Manager; 5、网站计数器。 6、数据库配置,数据库连接池 7、应用程序的日志应用
1.3单例模式的缺点
1、单例模式的扩展是比较困难的; 2、赋于了单例以太多的职责,某种程度上违反单一职责原则(六大原则后面会讲到); 3、单例模式是并发协作软件模块中需要最先完成的,因而其不利于测试; 4、单例模式在某种情况下会导致“资源瓶颈”。
2. 工厂类相关模式
2.1工厂模式、简单工厂模式、抽象工厂模式
工厂模式的定义如下:定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。其通用类图如下。其产品类定义产品的公共属性和接口,工厂类定义产品实例化的“方式”。
import random class PetShop(object): def __init__(self,animal_factory=None): # pet 宠物 factory 工厂 self.pet_factory = animal_factory def show_pet(self): pet = self.pet_factory.get_pet() print("this is a lovely", pet) print("it says",pet.speak()) print("it eats",self.pet_factory.get_food()) class Dog: def speak(self): return "Dog" def __str__(self): return "this is Dog" class Cat: def speak(self): return "Cat" def __str__(self): return "this is Cat" class CatFactory: def get_pet(self): return Cat() def get_food(self): return "cat food" class DogFactory: def get_pet(self): return Dog() def get_food(self): return "dog food" def get_factory(): return random.choice([DogFactory,CatFactory]) if __name__ == '__main__': shop = PetShop() # pet_factory 默认为None,后面延迟加载 shop.pet_factory = get_factory()() # 延迟加载,随机选择一个工厂然后实例出来一个对象给商店 shop.show_pet()
2.2工厂模式的优点和应用
工厂模式、抽象工厂模式的优点: 1、工厂模式巨有非常好的封装性,代码结构清晰;在抽象工厂模式中,其结构还可以随着需要进行更深或者更浅的抽象层级调整,非常灵活; 2、屏蔽产品类,使产品的被使用业务场景和产品的功能细节可以分而开发进行,是比较典型的解耦框架。 工厂模式、抽象工厂模式的使用场景: 1、当系统实例要求比较灵活和可扩展时,可以考虑工厂模式或者抽象工厂模式实现。比如, 在通信系统中,高层通信协议会很多样化,同时,上层协议依赖于下层协议,那么就可以对应建立对应层级的抽象工厂,根据不同的“产品需求”去生产定制的实例。
2.3工厂模式的不足
1、工厂模式相对于直接生成实例过程要复杂一些,所以,在小项目中,可以不使用工厂模式;
2、抽象工厂模式中,产品类的扩展比较麻烦。毕竟,每一个工厂对应每一类产品,产品扩展,就意味着相应的抽象工厂也要扩展。
3、建造者模式
3.1建造者模式
建造者模式的定义如下:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式的作用,就是将“构建”和“表示”分离,以达到解耦的作用。在上面订单的构建过程中,如果将order直接通过参数定义好(其构建与表示没有分离),同时在多处进行订单生成,此时需要修改订单内容,则需要一处处去修改,业务风险也就提高了不少。
在建造者模式中,还可以加一个Director类,用以安排已有模块的构造步骤。对于在建造者中有比较严格的顺序要求时,该类会有比较大的用处。
class orderDirector(): order_builder="" def __init__(self,order_builder): self.order_builder=order_builder def createOrder(self,burger,snack,beverage): self.order_builder.addBurger(burger) self.order_builder.addSnack(snack) self.order_builder.addBeverage(beverage) return self.order_builder.build()
3.2建造者模式的优点和使用场景
优点: 1、封装性好,用户可以不知道对象的内部构造和细节,就可以直接建造对象; 2、系统扩展容易; 3、建造者模式易于使用,非常灵活。在构造性的场景中很容易实现“流水线”; 4、便于控制细节。 使用场景: 1、目标对象由组件构成的场景中,很适合建造者模式。例如,在一款赛车游戏中,车辆生成时,需要根据级别、环境等,选择轮胎、悬挂、骨架等部件,构造一辆“赛车”; 2、在具体的场景中,对象内部接口需要根据不同的参数而调用顺序有所不同时,可以使用建造者模式。例如:一个植物养殖器系统,对于某些不同的植物,浇水、施加肥料的顺序要求可能会不同,因而可以在Director中维护一个类似于队列的结构,在实例化时作为参数代入到具体建造者中。
3.3建造者模式的缺点
1、“加工工艺”对用户不透明。(封装的两面性)
4、原型模式
4.1原型模式
原型模式定义如下:用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
需要注意一点的是,进行clone操作后,新对象的构造函数没有被二次执行,新对象的内容是从内存里直接拷贝的。
from copy import copy, deepcopy class simpleLayer: background=[0,0,0,0] content="blank" def getContent(self): return self.content def getBackgroud(self): return self.background def paint(self,painting): self.content=painting def setParent(self,p): self.background[3]=p def fillBackground(self,back): self.background=back def clone(self): return copy(self) def deep_clone(self): return deepcopy(self) if __name__=="__main__": dog_layer=simpleLayer() dog_layer.paint("Dog") dog_layer.fillBackground([0,0,255,0]) print "Background:",dog_layer.getBackgroud() print "Painting:",dog_layer.getContent() another_dog_layer=dog_layer.clone() print "Background:", another_dog_layer.getBackgroud() print "Painting:", another_dog_layer.getContent()
4.2原型模式的优点和使用场景
优点: 1、性能极佳,直接拷贝比在内存里直接新建实例节省不少的资源; 2、简化对象创建,同时避免了构造函数的约束,不受构造函数的限制直接复制对象,是优点,也有隐患,这一点还是需要多留意一些。 使用场景: 1、对象在修改过后,需要复制多份的场景。如本例和其它一些涉及到复制、粘贴的场景; 2、需要优化资源的情况。如,需要在内存中创建非常多的实例,可以通过原型模式来减少资源消耗。此时,原型模式与工厂模式配合起来,不管在逻辑上还是结构上,都会达到不错的效果; 3、某些重复性的复杂工作不需要多次进行。如对于一个设备的访问权限,多个对象不用各申请一遍权限,由一个设备申请后,通过原型模式将权限交给可信赖的对象,既可以提升效率,又可以节约资源。
4.3原型模式的缺点
1、深拷贝和浅拷贝的使用需要事先考虑周到; 2、某些编程语言中,拷贝会影响到静态变量和静态函数的使用。
二、结构类设计模式
1、适配器模式
1.1适配器模式
适配器模式定义如下:将一个类的接口变换成客户端期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。适配器模式和装饰模式有一定的相似性,都起包装的作用,但二者本质上又是不同的,装饰模式的结果,是给一个对象增加了一些额外的职责,而适配器模式,则是将另一个对象进行了“伪装”。
适配器可以认为是对现在业务的补偿式应用,所以,尽量不要在设计阶段使用适配器模式,在两个系统需要兼容时可以考虑使用适配器模式。
import os class Dog: def __init__(self): self.name ="Dog" def bark(self): # bark :叫声 return "woof!" # woof 低吠声 class Cat: def __init__(self): self.name = "Cat" def meow(self): # meow 猫叫声 return "meow" # meow 猫叫声 class Human: def __init__(self): self.name = "Human" def speak(self): return "hello, python" class Car: def __init__(self): self.name = "Car" def make_noise(self, noise_level): return "noise level is {}".format(noise_level) class Adapter: # adapter 适配器 def __init__(self,obj,adapted_methods): # adpted 适应 self.obj = obj self.__dict__.update(adapted_methods) # self.__dict__是打印对象所有的属性,结果是一个字典 {"kye":value} # key对应对象的属性,value对应属性的属性值。这里就相当于把不同类的方法都绑定到Adapter这个类实例化出来的 # 对象的make_noise 属性上面去,该属性的值对应其他类里面的方法。 def __getattr__(self, attr): # 当调用类不存的属性或者方法时,就会触发该魔法方法 return getattr(self.obj, attr) # getattr(object,attr [,default]) 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()) # 这里 obj.make_noise 就相当于 dog.bark 这些方法,后面加括号代表执行 print(obj.obj) # 原来的对象被存储到obj属性里面. if __name__ == '__main__': # 适配器模式在不改变原有类的基础上,统一了所有的方法,还能够保存原有对象的引用obj属性 main()
1.2适配器模式的优点和使用场景
优点:
1、适配器模式可以让两个接口不同,甚至关系不大的两个类一起运行;
2、提高了类的复用度,经过“伪装”的类,可以充当新的角色;
3、适配器可以灵活“拆卸”。
应用场景:
1、不修改现有接口,同时也要使该接口适用或兼容新场景业务中,适合使用适配器模式。例如,在一个嵌入式系统中,原本要将数据从Flash读入,现在需要将数据从磁盘读入,这种情况可以使用适配器模式,将从磁盘读入数据的接口进行“伪装”,以从Flash中读数据的接口形式,从磁盘读入数据。
1.3适配器模式的缺点
1、适配器模式与原配接口相比,毕竟增加了一层调用关系,所以,在设计系统时,不要使用适配器模式。
2、桥接模式
2.1桥接模式
桥梁模式又叫桥接模式,定义如下:将抽象与实现解耦(注意此处的抽象和实现,并非抽象类和实现类的那种关系,而是一种角色的关系,这里需要好好区分一下),可以使其独立变化。在形如上例中,Pen只负责画,但没有形状,它终究是不知道要画什么的,所以我们把它叫做抽象化角色;而Shape是具体的形状,我们把它叫做实现化角色。抽象化角色和实现化角色是解耦的,这也就意味着,所谓的桥,就是抽象化角色的抽象类和实现化角色的抽象类之间的引用关系。
class DrawingAPI1: def draw_circle(self, x, y, radius): print("API1.circle at {} : {} ,radius:{}".format(x, y, radius)) class DrawingAPI2: def draw_circle(self,x,y,radius): print("API2.cirle at {} : {} ,radius:{}".format(x, y, radius)) class CircleShape: def __init__(self,x,y,radius,drawing_api): self._x = x self._y = y self._radius = radius self._drawing_api = drawing_api def draw(self): self._drawing_api.draw_circle(self._x,self._y,self._radius) def scale(self,pct): # scale 规模 self._radius *= pct # pct 百分比 def main(): shapes = ( CircleShape(1,2,3,DrawingAPI1()), CircleShape(5,7,11,DrawingAPI2()), ) # 提供2个 for shape in shapes: shape.scale(2.5) shape.draw()
2.2桥梁模式的优点和应用场景
优点:
1、抽象角色与实现角色相分离,二者可以独立设计,不受约束;
2、扩展性强:抽象角色和实现角色可以非常灵活地扩展。
应用场景:
1、不适用继承或者原继承关系中抽象类可能频繁变动的情况,可以将原类进行拆分,拆成实现化角色和抽象化角色。例如本例中,若将形状、粗细、绘画样式等属于汇集在一个类中,一旦抽象类中有所变动,将造成巨大的风险;
2、重用性比较大的场景。比如开关控制逻辑的程序,开关就是抽象化角色,开关的形式有很多种,操作的实现化角色也有很多种,采用桥梁模式,(如当前例子)开关即可进行复用,整体会将设计的粒度减小。
2.3桥梁模式的缺点
1、增加对系统理解的难度。
3、 装饰器模式
3.1对装饰器的理解 ,并写出一个计时器记录方法执行性能的装饰器?
装饰器本质上是一个 Python 函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外
功能,装饰器的返回值也是一个函数对象。
import time def timeit(func): def wrapper(): start = time.clock() func() end =time.clock() print 'used:', end - start return wrapper @timeit def foo(): print 'in foo()'foo()
3.2装饰器模式的优点和应用场景
优点:
1、装饰器模式是继承方式的一个替代方案,可以轻量级的扩展被装饰对象的功能;
2、Python的装饰器模式是实现AOP的一种方式,便于相同操作位于不同调用位置的统一管理。
应用场景:
1、需要扩展、增强或者减弱一个类的功能,如本例。
3.3装饰器模式的缺点
1、多层装饰器的调试和维护有比较大的困难。
4、组合模式
4.1组合模式
组合模式也叫作部分-整体模式,其定义如下:将对象组合成树形结构以表示“部分”和“整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
4.2组合模式的优点和使用场景
优点: 1、节点增加和减少是非常自由和方便的,这也是树形结构的一大特点; 2、所有节点,不管是分支节点还是叶子结点,不管是调用一个结点,还是调用一个结点群,都是非常方便的。 使用场景: 1、维护部分与整体的逻辑关系,或者动态调用整体或部分的功能接口,可以考虑使用组合模式。例如,非常多的操作系统(如Linux)都把文件系统设计成树形结构,再比如说分布式应用中借助Zookeeper,也可以组织和调用分布式集群中的结点功能。
4.3组合模式的缺点
1、由于叶子结点和分支结点直接使用了实现类,而不方便使用抽象类,这大大限制了接口的影响范围;若结点接口发生变更,对系统造成的风险会比较大。
5、门面模式
5.1门面模式
门面模式也叫外观模式,定义如下:要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。门面模式注重“统一的对象”,也就是提供一个访问子系统的接口。门面模式与之前说过的模板模式有类似的地方,都是对一些需要重复方法的封装。但从本质上来说,是不同的。模板模式是对类本身的方法的封装,其被封装的方法也可以单独使用;而门面模式,是对子系统的封装,其被封装的接口理论上是不会被单独提出来用的。
5.2门面模式的优点和使用场景
优点: 1、减少了系统之间的相互依赖,提高了系统的灵活; 2、提高了整体系统的安全性:封装起的系统对外的接口才可以用,隐藏了很多内部接口细节,若方法不允许使用,则在门面中可以进行灵活控制。 使用场景: 1、为一个复杂的子系统提供一个外界访问的接口。这类例子是生活还是蛮常见的,例如电视遥控器的抽象模型,电信运营商的用户交互设备等; 2、需要简化操作界面时。例如常见的扁平化系统操作界面等,在生活中和工业中都很常见。
5.3门面模式的缺点
1、门面模式的缺点在于,不符合开闭原则,一旦系统成形后需要修改,几乎只能重写门面代码,这比继承或者覆写等方式,或者其它一些符合开闭原则的模式风险都会大一些。
6、享元模式
6.1享元模式
享元模式定义如下:使用共享对象支持大量细粒度对象。大量细粒度的对象的支持共享,可能会涉及这些对象的两类信息:内部状态信息和外部状态信息。内部状态信息就是可共享出来的信息,它们存储在享元对象内部,不会随着特定环境的改变而改变;外部状态信息就不可共享的信息了。享元模式中只包含内部状态信息,而不应该包含外部状态信息。这点在设计业务架构时,应该有所考虑。
6.2享元模式的优点和使用场景
优点: 1、减少重复对象,大大节约了系统资源。 使用场景: 1、系统中存在大量的相似对象时,可以选择享元模式提高资源利用率。咖啡订购平台比较小,若假设一个电商平台,每个买家和卖家建立起买卖关系后,买家对象和卖家对象都是占用资源的。如果一个卖家同时与多个买家建立起买卖关系呢?此时享元模式的优势就体现出来了; 2、需要缓冲池的场景中,可以使用享元模式。如进程池,线程池等技术,就可以使用享元模式(事实上,很多的池技术中已经使得了享元模式)。
6.3享元模式的缺点
1、享元模式虽然节约了系统资源,但同时也提高了系统的复杂性,尤其当遇到外部状态和内部状态混在一起时,需要先将其进行分离,才可以使用享元模式。否则,会引起逻辑混乱或业务风险; 2、享元模式中需要额外注意线程安全问题。
7、代理模式
7.1代理模式
代理模式定义如下:为某对象提供一个代理,以控制对此对象的访问和控制。代理模式在使用过程中,应尽量对抽象主题类进行代理,而尽量不要对加过修饰和方法的子类代理。如上例中,如果有一个xServer继承了Server,并新加了方法xMethod,xServer的代理应以Server为主题进行设计,而尽量不要以xServer为主题,以xServer为主题的代理可以从ServerProxy继承并添加对应的方法。
class serverProxy: pass class infoServerProxy(serverProxy): server="" def __init__(self,server): self.server=server def recv(self,info): return self.server.recv(info) def show(self): self.server.show() class whiteInfoServerProxy(infoServerProxy): white_list=[] def recv(self,info): try: assert type(info)==dict except: return "info structure is not correct" addr=info.get("addr",0) if not addr in self.white_list: return "Your address is not in the white list." else: content=info.get("content","") return self.server.recv(content) def addWhite(self,addr): self.white_list.append(addr) def rmvWhite(self,addr): self.white_list.remove(addr) def clearWhite(self): self.white_list=[]
7.2代理模式的优点和使用场景
优点: 1、职责清晰:非常符合单一职责原则,主题对象实现真实业务逻辑,而非本职责的事务,交由代理完成; 2、扩展性强:面对主题对象可能会有的改变,代理模式在不改变对外接口的情况下,可以实现最大程度的扩展; 3、保证主题对象的处理逻辑:代理可以通过检查参数的方式,保证主题对象的处理逻辑输入在理想范围内。 应用场景: 1、针对某特定对象进行功能和增强性扩展。如IP防火墙、远程访问代理等技术的应用; 2、对主题对象进行保护。如大流量代理,安全代理等; 3、减轻主题对象负载。如权限代理等。
7.3代理模式的缺点
可能会降低整体业务的处理效率和速度。
三、行为类设计模式
1、策略模式
1.1策略模式
class customer: customer_name="" snd_way="" info="" phone="" email="" def setPhone(self,phone): self.phone=phone def setEmail(self,mail): self.email=mail def getPhone(self): return self.phone def getEmail(self): return self.email def setInfo(self,info): self.info=info def setName(self,name): self.customer_name=name def setBrdWay(self,snd_way): self.snd_way=snd_way def sndMsg(self): self.snd_way.send(self.info) class msgSender: dst_code="" def setCode(self,code): self.dst_code=code def send(self,info): pass class emailSender(msgSender): def send(self,info): print "EMAIL_ADDRESS:%s EMAIL:%s"%(self.dst_code,info) class textSender(msgSender): def send(self,info): print "TEXT_CODE:%s EMAIL:%s"%(self.dst_code,info)
策略模式定义如下:定义一组算法,将每个算法都封装起来,并使他们之间可互换。以上述例子为例,customer类扮演的角色(Context)直接依赖抽象策略的接口,在具体策略实现类中即可定义个性化的策略方式,且可以方便替换。
1.2策略模式的优点和应用场景
优点: 1、各个策略可以自由切换:这也是依赖抽象类设计接口的好处之一; 2、减少代码冗余; 3、扩展性优秀,移植方便,使用灵活。 应用场景: 1、算法策略比较经常地需要被替换时,可以使用策略模式。如现在超市前台,会常遇到刷卡、某宝支付、某信支付等方式,就可以参考策略模式。
1.3策略模式的缺点
1、项目比较庞大时,策略可能比较多,不便于维护; 2、策略的使用方必须知道有哪些策略,才能决定使用哪一个策略,这与迪米特法则是相违背的。
2、责任链模式
2.1责任链模式
class manager(): successor = None name = '' def __init__(self, name): self.name = name def setSuccessor(self, successor): self.successor = successor def handleRequest(self, request): pass class lineManager(manager): def handleRequest(self, request): if request.requestType == 'DaysOff' and request.number <= 3: print '%s:%s Num:%d Accepted OVER' % (self.name, request.requestContent, request.number) else: print '%s:%s Num:%d Accepted CONTINUE' % (self.name, request.requestContent, request.number) if self.successor != None: self.successor.handleRequest(request) class departmentManager(manager): def handleRequest(self, request): if request.requestType == 'DaysOff' and request.number <= 7: print '%s:%s Num:%d Accepted OVER' % (self.name, request.requestContent, request.number) else: print '%s:%s Num:%d Accepted CONTINUE' % (self.name, request.requestContent, request.number) if self.successor != None: self.successor.handleRequest(request) class generalManager(manager): def handleRequest(self, request): if request.requestType == 'DaysOff': print '%s:%s Num:%d Accepted OVER' % (self.name, request.requestContent, request.number) class request(): requestType = '' requestContent = '' number = 0
责任链模式的定义如下:使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
2.2责任链模式的优点和应用场景
优点: 1、将请求者与处理者分离,请求者并不知道请求是被哪个处理者所处理,易于扩展。 应用场景: 1、若一个请求可能由一个对请求有链式优先级的处理群所处理时,可以考虑责任链模式。除本例外,银行的客户请求处理系统也可以用责任链模式实现(VIP客户和普通用户处理方式当然会有不同)。
2.3责任链模式的缺点
1、如果责任链比较长,会有比较大的性能问题; 2、如果责任链比较长,若业务出现问题,比较难定位是哪个处理者的问题。
3、 命令模式
3.1命令模式
命令模式的定义为:将一个请求封装成一个对象,从而可以使用不同的请求将客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。命令模式中通常涉及三类对象的抽象:Receiver,Command,Invoker
只有一个Invoker的命令模式也可以抽象成一个类似的“星形网络”,但与之前介绍的中介者模式不同,单纯的命令模式更像是一个辐射状的结构,由Invoker直接对Receiver传递命令,而一般不反向传递,中介者模式“星形网络”的中心,是个协调者,抽象结节间的信息流全部或者部分是双向的。
另外,命令模式的定义中提到了“撤销和恢复功能”,也给了各位开发人员一个命令模式使用过程中的建议:各个Receiver中可以设计一个回滚接口,支持命令的“撤销”。
3.2命令模式的优点和应用场景
优点: 1、低耦合:调用者和接收者之间没有什么直接关系,二者通过命令中的execute接口联系; 2、扩展性好:新命令很容易加入,也很容易拼出“组合命令”。 应用场景: 1、触发-反馈机制的系统,都可以使用命令模式思想。如基于管道结构的命令系统(如SHELL),可以直接套用命令模式;此外,GUI系统中的操作反馈(如点击、键入等),也可以使用命令模式思想。
3.3命令模式的缺点
1、如果业务场景中命令比较多,那么对应命令类和命令对象的数量也会增加,这样系统会膨胀得很大。
4、中介者模式
4.1中介者模式
中介者模式的定义为:用一个中介对象封装一系列的对象交互。中介者使各对象不需要显式地互相作用,从而使其耦合松散,并可以独立地改变它们之间的交互。
class abstractMediator(): purchase="" sales="" warehouse="" def setPurchase(self,purchase): self.purchase=purchase def setWarehouse(self,warehouse): self.warehouse=warehouse def setSales(self,sales): self.sales=sales def execute(self,content,num): pass class stockMediator(abstractMediator): def execute(self,content,num): print "MEDIATOR:Get Info--%s"%content if content=="buy": self.warehouse.inc(num) self.sales.getNotice("Bought %s"%num) elif content=="increase": self.sales.getNotice("Inc %s"%num) self.purchase.getNotice("Inc %s"%num) elif content=="decrease": self.sales.getNotice("Dec %s"%num) self.purchase.getNotice("Dec %s"%num) elif content=="warning": self.sales.getNotice("Stock is low.%s Left."%num) self.purchase.getNotice("Stock is low. Please Buy More!!! %s Left"%num) elif content=="sell": self.warehouse.dec(num) self.purchase.getNotice("Sold %s"%num) else: pass
4.2中介者模式的优点和应用场景
优点: 1、减少类与类的依赖,降低了类和类之间的耦合; 2、容易扩展规模。 应用场景: 1、设计类图时,出现了网状结构时,可以考虑将类图设计成星型结构,这样就可以使用中介者模式了。如机场调度系统(多个跑道、飞机、指挥塔之间的调度)、路由系统;著名的MVC框架中,其中的C(Controller)就是M(Model)和V(View)的中介者。
4.3中介者模式的缺点
1、中介者本身的复杂性可能会很大,例如,同事类的方法如果很多的话,本例中的execute逻辑会很复杂。
5、模版模式
5.1模版模式
模板模式定义如下:定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定的步骤。子类实现的具体方法叫作基本方法,实现对基本方法高度的框架方法,叫作模板方法。
5.2模板模式的优点和应用
优点: 1、可变的部分可以充分扩展,不变的步骤可以充分封装; 2、提取公共代码,减少冗余代码,便于维护; 3、具体过程可以定制,总体流程方便掌控。 使用场景: 1、某超类的子类中有公有的方法,并且逻辑基本相同,可以使用模板模式。必要时可以使用钩子方法约束其行为。具体如本节例子; 2、比较复杂的算法,可以把核心算法提取出来,周边功能在子类中实现。例如,机器学习中的监督学习算法有很多,如决策树、KNN、SVM等,但机器学习的流程大致相同,都包含输入样本、拟合(fit)、预测等过程,这样就可以把这些过程提取出来,构造模板方法,并通过钩子方法控制流程。
5.3模板模式的缺点
1、模板模式在抽象类中定义了子类的方法,即子类对父类产生了影响,部分影响了代码的可读性。
6、迭代器模式
6.1迭代器与生成器
在python中,迭代器并不用举太多的例子,因为python中的迭代器应用实在太多了(不管是python还是其它很多的编程语言中,实际上迭代器都已经纳入到了常用的库或者包中)。而且在当前,也几乎没有人专门去开发一个迭代器,而是直接去使用list、string、set、dict等python可迭代对象,或者直接使用__iter__和next函数来实现迭代器。
6.2迭代器模式
迭代器模式的定义如下:它提供一种方法,访问一个容器对象中各个元素,而又不需要暴露对象的内部细节。
7、访问者模式
7.1访问者模式
访问者模式的定义如下:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义于作用于这些元素的新操作。
7.2访问者模式的优点和应用
优点: 1、将不同的职责非常明确地分离开来,符合单一职责原则; 2、职责的分开也直接导致扩展非常优良,灵活性非常高,加减元素和访问者都非常容易。 应用场景: 1、要遍历不同的对象,根据对象进行不同的操作的场景;或者一个对象被多个不同对象顺次处理的情况,可以考虑使用访问者模式。除本例外,报表生成器也可以使用访问者模式实现,报表的数据源由多个不同的对象提供,每个对象都是Visitor,报表这个Element顺次Accept各访问者完善并生成对象。
7.3访问者的缺点
1、访问者得知了元素细节,与最小隔离原则相悖; 2、元素变更依旧可能引起Visitor的修改。
8、观察者模式
8.1观察者模式
"""观察者""" class Observer: def update(self): pass class AlarmSensor(Observer): def update(self,action): print "Alarm Got: %s" % action self.runAlarm() def runAlarm(self): print "Alarm Ring..." class WaterSprinker(Observer): def update(self,action): print "Sprinker Got: %s" % action self.runSprinker() def runSprinker(self): print "Spray Water..." class EmergencyDialer(Observer): def update(self,action): print "Dialer Got: %s"%action self.runDialer() def runDialer(self): print "Dial 119..." """被观察者""" class Observed: observers=[] action="" def addObserver(self,observer): self.observers.append(observer) def notifyAll(self): for obs in self.observers: obs.update(self.action) class smokeSensor(Observed): def setAction(self,action): self.action=action def isFire(self): return True
观察者模式也叫发布-订阅模式,其定义如下:定义对象间一种一对多的依赖关系,使得当该对象状态改变时,所有依赖于它的对象都会得到通知,并被自动更新。
观察者模式的通知方式可以通过直接调用等同步方式实现(如函数调用,HTTP接口调用等),也可以通过消息队列异步调用(同步调用指被观察者发布消息后,必须等所有观察者响应结束后才可以进行接下来的操作;异步调用指被观察者发布消息后,即可进行接下来的操作。)。事实上,许多开源的消息队列就直接支持发布-订阅模式,如Zero MQ等。
8.2观察者模式的优点和应用
优点: 1、观察者与被观察者之间是抽象耦合的; 2、可以将许多符合单一职责原则的模块进行触发,也可以很方便地实现广播。 应用场景: 1、消息交换场景。如上述说到的消息队列等; 2、多级触发场景。比如支持中断模式的场景中,一个中断即会引发一连串反应,就可以使用观察者模式。
8.3观察者模式的缺点
1、观察者模式可能会带来整体系统效率的浪费; 2、如果被观察者之间有依赖关系,其逻辑关系的梳理需要费些心思。
9、解释器模式
9.1解释器模式
class PlayContext(): play_text = None class Expression(): def interpret(self, context): if len(context.play_text) == 0: return else: play_segs=context.play_text.split(" ") for play_seg in play_segs: pos=0 for ele in play_seg: if ele.isalpha(): pos+=1 continue break play_chord = play_seg[0:pos] play_value = play_seg[pos:] self.execute(play_chord,play_value) def execute(self,play_key,play_value): pass class NormGuitar(Expression): def execute(self, key, value): print "Normal Guitar Playing--Chord:%s Play Tune:%s"%(key,value)
解释器模式定义如下:给定一种语言,定义它的文法表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。典型的解释器模式中会有终结符和非终结符之说,语法也根据两种终结符,决定语句最终含义。上例中,非终结符就是空格,终结符就是整个句尾。
9.2解释器模式的优点和应用
优点: 1、在语法分析的场景中,具有比较好的扩展性。规则修改和制订比较灵活。 应用场景: 1、若一个问题重复发生,可以考虑使用解释器模式。这点在数据处理和日志处理过程中使用较多,当数据的需求方需要将数据纳为己用时,必须将数据“翻译”成本系统的数据规格;同样的道理,日志分析平台也需要根据不同的日志格式翻译成统一的“语言”。 2、特定语法解释器。如各种解释型语言的解释器,再比如自然语言中基于语法的文本分析等。
9.3解释器模式的缺点
1、解释规则多样化会导致解释器的爆炸; 2、解释器目标比较单一,行为模式比较固定,因而重要的模块中尽量不要使用解释器模式。
10、备忘录模式
10.1备忘录模式
class GameCharacter(): vitality = 0 attack = 0 defense = 0 def displayState(self): print 'Current Values:' print 'Life:%d' % self.vitality print 'Attack:%d' % self.attack print 'Defence:%d' % self.defense def initState(self,vitality,attack,defense): self.vitality = vitality self.attack = attack self.defense = defense def saveState(self): return Memento(self.vitality, self.attack, self.defense) def recoverState(self, memento): self.vitality = memento.vitality self.attack = memento.attack self.defense = memento.defense class FightCharactor(GameCharacter): def fight(self): self.vitality -= random.randint(1,10) class Memento: vitality = 0 attack = 0 defense = 0 def __init__(self, vitality, attack, defense): self.vitality = vitality self.attack = attack self.defense = defense
备忘录模式定义如下:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原来保存的状态。在备忘录模式中,如果要保存的状态多,可以创造一个备忘录管理者角色来管理备忘录。
10.2备忘录模式的应用场景
1、需要保存和恢复数据的相关状态场景。如保存游戏状态的场景;撤销场景,如Ctrl-Z操作;事务回滚的应用。一般情况下事务回滚有两种方式:一是把从恢复点开始的操作都反向执行一遍;二是直接恢复到恢复点的各种状态。两种方式各有优缺点,要结合业务场景,决定使用哪种模式; 2、副本监控场景。备忘录可以当作一个临时的副本监控,实现非实时和准实时的监控。
11、状态模式
11.1状态模式
模拟电梯控制器
"""抽象状态类""" class LiftState: def open(self): pass def close(self): pass def run(self): pass def stop(self): pass """具体状态类""" class OpenState(LiftState): def open(self): print "OPEN:The door is opened..." return self def close(self): print "OPEN:The door start to close..." print "OPEN:The door is closed" return StopState() def run(self): print "OPEN:Run Forbidden." return self def stop(self): print "OPEN:Stop Forbidden." return self class RunState(LiftState): def open(self): print "RUN:Open Forbidden." return self def close(self): print "RUN:Close Forbidden." return self def run(self): print "RUN:The lift is running..." return self def stop(self): print "RUN:The lift start to stop..." print "RUN:The lift stopped..." return StopState() class StopState(LiftState): def open(self): print "STOP:The door is opening..." print "STOP:The door is opened..." return OpenState() def close(self): print "STOP:Close Forbidden" return self def run(self): print "STOP:The lift start to run..." return RunState() def stop(self): print "STOP:The lift is stopped." return self """上下文类""" class Context: lift_state="" def getState(self): return self.lift_state def setState(self,lift_state): self.lift_state=lift_state def open(self): self.setState(self.lift_state.open()) def close(self): self.setState(self.lift_state.close()) def run(self): self.setState(self.lift_state.run()) def stop(self): self.setState(self.lift_state.stop())
状态模式的定义如下:当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。
11.2状态模式的优点和应用
优点: 1、状态模式的优点是结构清晰,相比于if…else…简约了不少; 2、封装性好,外部调用不必知道内部实现细节。 应用场景: 1、行为状态改变的场景。这点在各种控制器中非常常见,同时,逻辑结构为状态转移图的场景中都非常适用。
11.3状态模式的缺点
1、在状态比较多时,子类也会非常多,不便于管理。