Python设计模式
一、设计模式与面向对象
1.1 设计模式
软件设计阶段针对反复出现的问题提出的统一解决方案。每类问题的解决方案都被系统的命名为XX设计模式。
1.2 面向对象
按照一件事的组成元素分别制作进行组装。这些元素分为属性与接口。
1.2.1 三大特性
封装:将对象封装为类,将对象数据操作封装为函数。封装分为类内/类外,私有/公有
继承:父类与子类,表示对象与对象之间的关系
多态:子类重写父类函数,C++中声明为virutal
。python本身是多态语言,只管实现,不用关注细节。
接口:抽象类,规定继承抽象类的实现行为。接口掌握着所有子类方法的规则,开发中直接阅读接口(抽象类)即可明白其余类方法,方便开发。
from abc import ABCMeta, abstractmethod
# abstract class
class Payment(metaclass=ABCMeta):
# is overrider in
@abstractmethod
def pay(self, money): # 接口:规定了A_Pay、B_Pay必须实现这个方法。多态的体现之一就是接口在不同子类中的多种实现,这种性质为多态性。
pass
class A_Pay(Payment): # 封装:将对象A的行为及属性封装为类
# overriders
def pay(self, money):
print("A 支付 %d" % money)
class B_Pay(Payment): # 继承Payment这个接口(抽象类)
# overriders
def pay(self, money):
print("B 支付 %d" % money)
p = A_Pay()
n = B_Pay()
p.pay(100)
n.pay(100)
1.2.2 设计原则
开放封闭原则:软件实体应对修改关闭,对扩展开放。
里氏替换原则:子类与父类的同名方法在形参、返回值、逻辑必须一致。
依赖倒置原则:上层不应该依赖下层,两者都应依赖抽象(接口),即面向接口编程。
接口隔离原则:使用多个专门的接口,避免使用单一的总接口,即避免依赖那些不需要的接口。适用于类。
单一职责原则:一个类只负责一项职责。
1.3 面向过程
按照制作一件事物的顺序进行开发。
二、 设计模式分类
创建型模式:工厂方法模式、抽象工厂模式、创建者模式、原型模式、单例模式
结构型模式:适配器模式、桥模式、组合模式、装饰模式、外观模式、享元模式、代理模式
行为型模式:解释器模式、责任链模式、命令模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、访问者模式、模板方法模式
三、简单工厂模式
3.1 内容
不直接向客户端暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品类的实例。简化上层调用的统一接口。
3.2 代码实现
from abc import ABCMeta, abstractmethod
# abstract class
class Payment(metaclass=ABCMeta):
@abstractmethod # 有抽象方法的类为抽象类
def pay(self, money):
pass
class AliPay(Payment):
def pay(self, money):
print("支付宝 支付 %d" % money)
class WeChatPay(Payment):
def pay(self, money):
print("微信 支付 %d" % money)
# 工厂类
class PayFactory:
# 工厂函数
def creat_payment(self,method):
if method == 'AliPay':
return AliPay()
elif method == 'WeChatPay':
return WeChatPay()
else:
raise TypeError("No such payment named %s" % method)
pf = PayFactory() # 实例化工厂
p = pf.creat_payment('AliPay') #制造支付方式
p.pay(100)
3.3 优缺点
优点:客户端调用的接口唯一。
缺点:有新的产品加入时需要修改工厂类,违反开放封闭原则。
3.4 工厂方法模式
将简单工厂拆开,一个工厂只生产一种产品。给工厂方法定义抽象基类约束工厂方法实现。
from abc import ABCMeta, abstractmethod
# abstract class
class Payment(metaclass=ABCMeta):
@abstractmethod # 有抽象方法的类为抽象类
def pay(self, money):
pass
class AliPay(Payment):
def pay(self, money):
print("支付宝 支付 %d" % money)
class WeChatPay(Payment):
def pay(self, money):
print("微信 支付 %d" % money)
# 工厂接口抽象基类
class PayBasicInterFaceFactory(metaclass=ABCMeta):
@abstractmethod
def creat_payment(self):
raise NotImplementedError
# 工厂类
class AliPayFactory(PayBasicInterFaceFactory):
# 工厂函数
def creat_payment(self):
return AliPay()
# 工厂类
class WeChatPayFactory(PayBasicInterFaceFactory):
# 工厂函数
def creat_payment(self):
return WeChatPay()
ali_pf = AliPayFactory()
p = ali_pf.creat_payment()
p.pay(100)
we_pf = WeChatPayFactory()
p = we_pf.creat_payment()
p.pay(100)
3.5 工厂模式和工厂函数的区别
工厂函数是一个方法,工厂模式是一种软件架构设计理念,工厂函数是根据客户输入返回不同产品的方法, 即生产产品。
工厂函数和工厂模式都是为了方便的生成产品实例。
四、抽象工厂模式
4.1 内容
把对象得实例化放在最后,先做组件再统一保存。组件和工厂都需要抽象层制定规则。
1、给各组件定义抽象基类
2、给各工厂定义抽象基类
3、定义一个产品类,产品的各配件由工厂生产
4、定义制作函数,传入工厂并返回产品。
4.2 代码实现
from abc import ABCMeta, abstractmethod
# ------ 抽象产品----------
class PhoneShell(metaclass=ABCMeta):
@abstractmethod
def show_shell(self):
pass
# --------具体产品----------
class SmallShell(PhoneShell):
def show_shell(self):
print("小手机壳")
class BigShell(PhoneShell):
def show_shell(self):
print("大手机壳")
# --------抽象工厂---------
class PhoneFactory(metaclass=ABCMeta):
@abstractmethod
def make_Shell(self):
pass
# ---------具体工厂-----------------
class XiaoMiPhoneFactory(PhoneFactory):
def make_Shell(self):
return BigShell()
class HuaWeiPhoneFactory(PhoneFactory):
def make_Shell(self):
return SmallShell()
# -------开始制作----------------
class Phone:
def __init__(self,shell):
self.shell = shell
def show_info(self):
print('手机信息:')
self.shell.show_shell()
def make_phone(factory):
shell = factory.make_Shell() # 工厂制作手机壳
return Phone(shell) # 制作好的手机壳保存到手机信息中
p1 = make_phone(HuaWeiPhoneFactory())
p1.show_info()
4.3 优缺点
优点:客户端实现简单,只需要传入不同的工厂即可。将组件各自封装为类,注重组件分别制造后保存。
缺点:需要定义非常多的类,添加修改麻烦。
五、建造者模式
5.1 内容
把产品实例化放在工厂中,一步步装配。
5.2 代码实现
from abc import ABCMeta, abstractmethod
class Player:
def __init__(self, face, arm, leg):
self.face = face
self.arm = arm
self.leg = leg
def __str__(self):
return '%s,%s,%s' % (self.face, self.arm, self.leg)
# 抽象基类:工厂方法接口
class PlayerBuilder(metaclass=ABCMeta):
@abstractmethod
def build_face(self):
pass
@abstractmethod
def build_arm(self):
pass
@abstractmethod
def build_leg(self):
pass
# 产品一
class SexyGileBuilder(PlayerBuilder):
def __init__(self):
self.player = Player("", "", "")
def build_face(self):
self.player.face = '瓜子脸'
def build_arm(self):
self.player.arm = '细胳膊'
def build_leg(self):
self.player.leg = '大长腿'
# 制定组装顺序:相同得流程传入不同产品类产生不同得产品
class PlayerDirect:
def buildPlayer(self, builder):
builder.build_face() # 先组装脸
builder.build_leg() # 再组装腿
builder.build_arm() # 后组装胳膊
return builder.player
builder = SexyGileBuilder()
direct = PlayerDirect()
p = direct.buildPlayer(builder)
print(p)
4.3 优缺点
优点:隐藏了产品得内部结构与装配顺序。支持组件的分别制造,不对组件分别创建类,但更加注重产品的装配顺序。
缺点:需要定义非常多的类,添加修改麻烦。
六、单例模式
6.1 内容
保证一个类只有一个实例。
6.2 代码实现
class Singleton:
# new中
def __new__(cls, *args, **kwargs):
# 用类属性确保类只有一个实例
if not hasattr(cls,"_instance"): # object的属性,用反射查找cls是否有私有属性_instance
cls._instance = super(Singleton,cls).__new__(cls) # 调用object类方法开辟一个空间
return cls._instance
class Myclass(Singleton):
def __init__(self,num):
self.num = num
print('%s create success!' % self.num)
a = Myclass(10) # self.num 已经被创建
b = Myclass(20) # self.num 已经被创建,不会再创建
print(a.num,b.num)
6.3 优缺点
优点:对实例进行把控,便于管理。
缺点:单例相当于全局变量,但可以防止对象被污染。
七、适配器模式
7.1 内容
将一个类的接口装换成另一个接口。使不同的接口可以兼容。
7.2 代码实现
from abc import ABCMeta, abstractmethod
# abstract class
class Payment(metaclass=ABCMeta):
@abstractmethod # 有抽象方法的类为抽象类
def pay(self, money):
pass
class AliPay(Payment):
def pay(self, money):
print("支付宝 支付 %d" % money)
class WeChatPay(Payment):
def pay(self, money):
print("微信 支付 %d" % money)
class MastercardPay:
def cost(self,money): # 名字不一样,如何之前一样调用pay(self, money)实现呢?
print("信用卡 支付 %d" % money)
# 继承实现,客户端后续统一调用MastercardPay_Adapter类
class MastercardPay_Adapter1(Payment,MastercardPay):
def pay(self, money):
self.cost(money) # 即实现接口统一
# 组合实现:可以包含多个类
class MastercardPay_Adapter2:
def __init__(self,payment):
self.payment = payment
def pay(self, money):
# 如果有多个不同的类,可以使用类名,if elif进行区分即可
self.payment.cost(money)
7.3 优缺点
优点:实现接口的统一。
缺点:减少了程序的易读性,但是如果本身就是两个不同的项目合并,适配器是最有效的结构模式。
八、桥模式
8.1 内容
将一个事物的两个维度分离,使其都可以独立的变化。
8.2 代码实现
反面教材:每多一种类型,就要定义一个类
class GreenColor():
pass
class GreenRectangle():
pass
class RedColor():
pass
class RedRectangle():
pass
正面教材:形状和颜色分离,可以自由组合
from abc import abstractmethod, ABCMeta
class Color(metaclass=ABCMeta):
@abstractmethod
def paint(self, shape):
pass
class Shape(metaclass=ABCMeta):
# 将形状和颜色进行组合,先有形状后填颜色,因此是Shape包含Color
def __init__(self, color):
self.color = color
@abstractmethod
def draw(self):
pass
class Rectangle(Shape):
name = '长方形'
def draw(self):
self.color.paint(self)
class Circle(Shape):
name = '圆形'
def draw(self):
self.color.paint(self)
class Red(Color):
def paint(self, shape):
print('红色的%s' % shape.name)
class Green(Color):
def paint(self, shape):
print('绿色的%s' % shape.name)
shape = Rectangle(Red()) # Red()新建一个实例给Self.Color
shape.draw()
8.3 优缺点
优点:松耦合,两个维度可以独立变化
缺点:底层理解比较困难
九、组合模式
9.1 内容
将对象组成树形结构以表示“部分-整体”的层级结构,例如两个点是线的叶子节点,数条线是图形的叶子节点。
9.2 代码实现
from abc import abstractmethod,ABCMeta
# 接口:抽象组件
class Graphic(metaclass=ABCMeta):
@abstractmethod
def draw(self):
pass
class Point(Graphic):
def __init__(self,x,y):
self.x = x
self.y = y
def __str__(self):
return '点(%s,%s)' % (self.x,self.y)
def draw(self):
print(str(self))
class Line(Graphic):
def __init__(self,p1,p2):
self.p1 = p1
self.p2 = p2
def __str__(self):
return '线段[%s,%s]' % (self.p1,self.p2)
def draw(self):
print(str(self))
# realize a complicated object
class Picture(Graphic):
def __init__(self,iterable):
self.children = [] #record child node
for g in iterable:
self.add(g)
def add(self,graphic):
self.children.append(graphic)
def draw(self):
print('complicated graphic')
for g in self.children:
g.draw()
print('complicated graphic')
l1 = Line(Point(3,4),Point(6,7))
l2 = Line(Point(1,5),Point(2,8))
pict1 = Picture([l1,l2])
pict1.draw()
9.3 优缺点
优点:叶子节点和父节点的关系清楚明了,适用于"部分-整体"关系明确的对象
缺点:抽象组件改动后,整体改动较大
十、外观模式
10.1 内容
为子系统的一组接口提供一个一致的界面,即定义了一个高级类来管理使用子类。
10.2 代码实现
# 子类:子接口
class CPU:
def run(self):
print('CPU start run.')
def runstop(self):
print('CPU stop run.')
# 子类:子接口
class Disk:
def run(self):
print('Disk start run.')
def runstop(self):
print('Disk stop run.')
# 高级类:统一管理子类,用户只需要调用此类便可以管理disk与CPU;此高级类称为外观
class Computer:
def __init__(self):
self.cpu = CPU()
self.disk = Disk()
def run(self):
self.cpu.run()
self.disk.run()
def stop(self):
self.disk.runstop()
self.cpu.runstop()
computer = Computer()
computer.run()
computer.stop()
10.3 优缺点
优点:降低了代码的耦合,客户端需要操作的代码少;对于底层安全。
缺点:隐藏了子类的动作,不利于发现子类的逻辑错误。
十一、代理模式
11.1 内容
为其他对象提供一个中介来控制对于这个对象的访问。适用于远程代理(远程对象)、虚代理(对象很大)、保护代理(访问权限有限)。
11.2 代码实现:
from abc import abstractmethod, ABCMeta
class Object(metaclass=ABCMeta):
@abstractmethod
def get_content(self):
pass
@abstractmethod
def set_content(self):
pass
class RealObject(Object):
def __init__(self,filename):
self.filename = filename
f = open(self.filename,mode='r')
self.content = f.read()
f.close()
def get_content(self):
print('读取文件内容')
return self.content
def set_content(self,new_content):
print('写入文件内容')
f = open(self.filename,mode='w')
f.write(new_content)
f.close()
# 文件很大时候使用虚代理
class VirtualProxy(Object):
def __init__(self,filename):
self.filename = filename # 只保存名称,不打开,不占用内存
self.sub_obj = None
def get_content(self):
if not self.sub_obj:
self.sub_obj = RealObject(self.filename) #开始占用内存建立对象
return self.sub_obj.get_content()
def set_content(self,new_content):
if not self.sub_obj: # 没有实例化先实例化
self.sub_obj = RealObject(self.filename) # 开始占用内存建立对象
return self.sub_obj.set_content(new_content)
# 权限有限制的时候保护代理
class ProtectProxy(Object):
def __init__(self,filename):
self.filename = filename # 只保存名称,不打开,不占用内存
self.sub_obj = RealObject(self.filename)
# 读权限开放
def get_content(self):
return self.sub_obj.get_content()
# 写权限异常
def set_content(self,new_content):
return PermissionError # 权限异常
obj = VirtualProxy("Proxy.txt") # 不创建对象
obj.get_content() # 此时会才创建RealObject对象
print(obj.sub_obj)
obj.set_content('123456') # 此时会才创建RealObject对象
print(obj.get_content())
11.3 代码实现
优点:利用中介保护了对象的安全,通过操作中介也不用关注对象的动作。
缺点:
十二、责任链模式
12.1 内容
使多个对象都有机会处理请求,将这些对象连成链,并沿着这条链传递请求,知道有一个对象处理它为止。
12.2 代码实现
from abc import abstractmethod, ABCMeta
# 每个请求处理者都应该有处理函数
class Handler(metaclass=ABCMeta):
@abstractmethod
def handle_leave(self, days):
pass
class GeneralManager(Handler):
def handle_leave(self, days):
if days <= 10:
print("总经理准假 %d 天" % days)
else:
print('你还是辞职吧')
class DepartManager(Handler):
def __init__(self):
self.next = GeneralManager()
def handle_leave(self, days):
if days <= 3:
print("部门经理准假 %d 天" % days)
else:
print('转至总经理处理--')
self.next.handle_leave(days)
de = DepartManager()
de.handle_leave(8)
12.3 优缺点
优点:支持多个对象处理同一个请求,客户端无需知道哪一个最终处理。降低耦合度。
缺点:封闭了处理过程。
十三、观察者模式
13.1 内容
定义一对多的依赖关系,一个对象状态改变时,所有的依赖对象都将得到更新。又称“发布-订阅”模式。
13.2 代码实现
from abc import abstractmethod, ABCMeta
# 订阅者
class Subscriber(metaclass=ABCMeta):
@abstractmethod
def DataUpdate(self, noticeMsg): # 接收到Notice报文后需要更新数据
pass
# 发布者
class Publiser(metaclass=ABCMeta):
@abstractmethod
def ServiceBind(self, subscriber): # 接收到Notice报文后需要更新数据
pass
@abstractmethod
def SeviceRelease(self, subscriber):
pass
@abstractmethod
# noitice报文发送给所有的订阅者
def SendNotice(self, data):
pass
# 定义Notice报文
class NoticeMessage():
def __init__(self,data = 0):
self.__data = data # notice报文数据场,私有属性
@property # Getter
def GetData(self):
return self.__data
@GetData.setter # 报文的属性不可以被外来的接口更改
def SetData(self,data):
self.__data = data
# 实际的订阅者:下挂ECU
class SlaverECU(Subscriber):
def __init__(self, name = None,data = 0):
self.name = name
self.data = data
def DataUpdate(self, noticeMsg):
self.data = noticeMsg.GetData # 更新自己得到的数据
# 实际的发布者:网关
class GateWay(Publiser):
def __init__(self):
self.subscriber = [] # 保存自己的订阅者
self.noticeMsg = NoticeMessage()
# 服务绑定
def ServiceBind(self, subscriber):
print("Bind %s success" % subscriber.name)
self.subscriber.append(subscriber)
# 服务释放
def SeviceRelease(self, subscriber):
print("Release %s success" % subscriber.name)
self.subscriber.remove(subscriber)
# noitice报文发送给所有的订阅者
def SendNotice(self, data):
self.noticeMsg.SetData = data # 更新notice报文数据并发送
for obj in self.subscriber:
obj.DataUpdate(self.noticeMsg)
ECU1 = SlaverECU("CCU")
ECU2 = SlaverECU("SCU")
GW = GateWay()
print("ECU1 data = %d" % ECU1.data)
print("ECU2 data = %d" % ECU2.data)
# 订阅者绑定
GW.ServiceBind(ECU1)
GW.ServiceBind(ECU2)
# 数据更新
GW.SendNotice(555)
print("ECU1 data = %d" % ECU1.data)
print("ECU2 data = %d" % ECU2.data)
# 订阅者释放
GW.SeviceRelease(ECU1)
GW.SeviceRelease(ECU2)
13.3 优缺点
优点:支持广播通信,发布者数据更新无需再关注订阅者。目标和对象的耦合最低
缺点:多个服务时,对于芯片性能要求较高。
十四、策略模式
14.1 内容
定义一系列的算法,把它们分别封装并使其互相可替换。使得算法可独立于客户端进行变化。
14.2 代码实现
from abc import abstractmethod, ABCMeta
class Strategy(metaclass=ABCMeta):
@abstractmethod
def execute(self, data):
pass
class FastStrategy(Strategy):
def execute(self, data):
print("使用快速测试略处理数据: %s" % data)
class SlowStrategy(Strategy):
def execute(self, data):
print("使用慢速测试略处理数据: %s" % data)
# 封装策略:将所有的数据和策略集中处理
class Context():
def __init__(self, strategy, data):
self.data = data
self.strategy = strategy
def set_strategy(self, strategy):
self.strategy = strategy
def do_strategy(self):
self.strategy.execute(self.data)
data = "aiosyudiqhirjhiuy"
fs = FastStrategy()
ls = SlowStrategy()
context = Context(fs, data)
context.do_strategy()
# 切换策略
context.set_strategy(ls)
context.do_strategy()
14.3 优缺点
优点:定义了一系列可以重用的算法和切换行为,消除了一些条件语句
缺点:客户端必须了解各种策略才可以准确切换
十五、模板方法模式
15.1 内容
定义一个操作中算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
15.2 代码实现
from abc import abstractmethod, ABCMeta
import time
class Windows(metaclass=ABCMeta):
@abstractmethod
def start(self):
pass
@abstractmethod
def repaint(self):
pass
@abstractmethod
def stop(self): # 具体制定变的东西
pass
# 这个在抽象类的具体方法就是模板方法,也就是不变的东西
def run(self):
while True:
try:
self.repaint()
time.sleep(1)
except KeyboardInterrupt:
break
self.stop()
class MyWindows(Windows):
def __init__(self,msg):
self.msg =msg
def start(self):
print('start run')
def repaint(self):
print('message: %s' % self.msg)
def stop(self):
print('stop run')
# 不需要再实现run
# def run(self):
# while True:
# try:
# self.repaint()
# time.sleep(1)
# except KeyboardInterrupt:
# break
# self.stop()
MyWindows("12456").run()
15.3 优缺点
优点:一次性实现不变的部分,避免代码重复;控制子类扩展,即满足抽象类要求才可以拓展。
缺点:需要客户端提前了解抽象类的具体内容
总结
尽可能的避免代码重用
尽可能的使用抽象类规定开发规则
尽可能的去降低客户端的开发难度
尽可能的确保底层安全
本文来自博客园,作者:{张一默},转载请注明原文链接:https://www.cnblogs.com/YiMo9929/p/17909795.html