适配器模式
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
-
适用场景:
- 系统需要使用现有的类,而此类的接口不符合系统的需要。
- 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。
- 通过接口转换,将一个类插入另一个类系中。
-
优点:
- 可以让任何两个没有关联的类一起运行。
- 提高了类的复用。
- 增加了类的透明度。
- 灵活性好。
-
缺点:
- 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
from abc import abstractmethod, ABCMeta
class OldLogger:
"""
抽象化角色
"""
def debug(self, msg):
print(f"旧的日志器:{msg}")
class NewLogger:
def new_debug(self, msg):
print(f"新的日志器:{msg}")
class AdapterLogger(OldLogger):
def __init__(self, logger: NewLogger):
self.logger = logger
def debug(self, msg):
self.logger.new_debug(msg)
def main(log: OldLogger):
log.debug("调试中")
if __name__ == "__main__":
logger = AdapterLogger(NewLogger())
main(logger)
桥接模式
桥接模式就是把抽象部分与实现部分抽离,使其可以匹配多种组合
说白了就是将俩种不同维度的事物进行任意组合使用,防止产生笛卡尔积
-
为什么要使用桥接模式
商城系统中常见的商品分类,以电脑分类为例,我们可以使用多层继承结构
假设加上一个品牌,荣耀,那么我们就需要再创建一个品牌抽象,实现3个分类。一个两个还好,如果添加100个种类呢?就会发生类爆炸(类贼多),后期维护复杂,违反单一职责原则。
-
桥接模式结构
桥,顾名思义就是用来将河的两岸联系起来的。而此处的桥是用来将两个独立的结构联系起来,而这两个被联系起来的结构可以独立的变化。而实现桥接模式的具体操作就是将抽象化部分与实现化部分分开,取消二者的继承关系,改用组合关系,并用桥连接
- 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用(桥)作为实例属性。
- 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
- 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
- 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。
-
代码示例
from abc import abstractmethod, ABCMeta
class AbstractPen(metaclass=ABCMeta):
"""
抽象化角色
"""
def __init__(self, shape):
self.shape = shape # 对实现化对象的引用
@abstractmethod
def draw(self):
raise NotImplementedError
class GreenPen(AbstractPen):
"""
扩展抽象化角色:绿色笔
"""
def draw(self):
print("我用绿色画笔,", end='')
self.shape.draw()
class RedPen(AbstractPen):
"""
扩展抽象化角色:红色笔
"""
def draw(self):
print("我用红色画笔,", end='')
self.shape.draw()
class AbstractShape(metaclass=ABCMeta):
"""
实现化角色
"""
@abstractmethod
def draw(self):
raise NotImplementedError
class Circle(AbstractShape):
"""
具体实现化角色:圆形
"""
def draw(self):
print("圆形")
class Rectangle(AbstractShape):
"""
具体实现化角色:矩形
"""
def draw(self):
print("矩形")
if __name__ == "__main__":
RedPen(Rectangle()).draw()
-
优点:
- 分离抽象接口及其实现部分。桥接模式使用“对象间的关联关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自维度的变化,也就是说抽象和实现不再在同一个继承层次结构中,而是“子类化”它们,使它们各自都具有自己的子类,以便任何组合子类,从而获得多维度组合对象。
- 在很多情况下,桥接模式可以取代多层继承方案,多层继承方案违背了“单一职责原则”,复用性较差,且类的个数非常多,桥接模式是比多层继承方案更好的解决方法,它极大减少了子类的个数。
- 桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,符合“开闭原则”。
-
缺点:
- 桥接模式的使用会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程。
- 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性,如何正确识别两个独立维度也需要一定的经验积累。
过滤器模式
- 示例代码
class Customer:
def __init__(self, name, gender, age, address):
self.name = name
self.gender = gender
self.age = age
self.address = address
class Filter:
def execute(self, customers):
pass
class GenderFilter(Filter):
def __init__(self, gender):
self.gender = gender
def execute(self, customers):
result = []
for customer in customers:
if customer.gender == self.gender:
result.append(customer)
return result
class AgeFilter(Filter):
def __init__(self, min_age, max_age):
self.min_age = min_age
self.max_age = max_age
def execute(self, customers):
result = []
for customer in customers:
if self.min_age <= customer.age <= self.max_age:
result.append(customer)
return result
class AddressFilter(Filter):
def __init__(self, address):
self.address = address
def execute(self, customers):
result = []
for customer in customers:
if customer.address == self.address:
result.append(customer)
return result
class CustomerDataAnalysis:
def __init__(self, customers):
self.customers = customers
def apply_filter(self, filter_obj):
filtered_customers = filter_obj.execute(self.customers)
return filtered_customers
if __name__ == '__main__':
customers = [
Customer("Tom", "Male", 27, "Beijing"),
Customer("Jerry", "Male", 32, "Shanghai"),
Customer("Lucy", "Female", 24, "Guangzhou"),
Customer("Lily", "Female", 38, "Shenzhen"),
Customer("Jack", "Male", 29, "Beijing"),
]
analysis = CustomerDataAnalysis(customers)
result = analysis.apply_filter(GenderFilter("Male"))
print("Male Customers:")
for customer in result:
print(customer.name)
result = analysis.apply_filter(AgeFilter(25, 35))
print("Customers between 25 and 35 years old:")
for customer in result:
print(customer.name)
result = analysis.apply_filter(AddressFilter("Shanghai"))
print("Customers from Shanghai:")
for customer in result:
print(customer.name)
组合模式
组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
- 使用场景
- 对象的部分-整体层次结构(树形结构,文件件,菜单等)。
- 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
- 优点
- 高层模块调用简单。
- 节点自由增加。
- 缺点
- 在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。
- 代码示例
from abc import ABC, abstractmethod
class Component(ABC):
def __init__(self, name):
self.name = name
@abstractmethod
def operation(self, depth):
pass
@abstractmethod
def add(self, c):
pass
@abstractmethod
def remove(self, c):
pass
@abstractmethod
def get_child(self, index):
pass
class Composite(Component):
def __init__(self, name):
Component.__init__(self, name)
self.children = []
def operation(self, depth):
strtemp = ''
for i in range(depth):
strtemp += strtemp+'-'
print(strtemp+self.name)
for comp in self.children:
comp.operation(depth+2)
def add(self, c):
self.children.append(c)
def remove(self, c):
self.children.remove(c)
def get_child(self, index):
return self.children[index]
class Leaf(Component):
def operation(self, depth):
strtemp = ''
for i in range(depth):
strtemp += strtemp+'-'
print(strtemp+self.name)
def add(self, c):
print('不能添加下级节点!')
def remove(self, c):
print('不能删除下级节点!')
def get_child(self, index):
print('没有下级节点!')
class Client:
@staticmethod
def main():
# 生成树根
root = Composite("root")
# 根上长出2个叶子
root.add(Leaf('leaf A'))
root.add(Leaf('leaf B'))
# 根上长出树枝Composite X
comp = Composite("Composite X")
comp.add(Leaf('leaf XA'))
comp.add(Leaf('leaf XB'))
root.add(comp)
# 根上长出树枝Composite X
comp2 = Composite("Composite XY")
# Composite X长出2个叶子
comp2.add(Leaf('leaf XYA'))
comp2.add(Leaf('leaf XYB'))
root.add(comp2)
# 根上又长出2个叶子,C和D,D没长好,掉了
root.add(Leaf('Leaf C'))
leaf = Leaf("Leaf D")
root.add(leaf)
root.remove(leaf)
# 展示组织
root.operation(1)
if __name__ == '__main__':
Client.main()
装饰器模式
装饰器模式(Decorator Pattern)允许向一个现有的对象扩展新的功能,同时不改变其结构。主要解决直接继承下因功能的不断横向扩展导致子类膨胀的问题,无需考虑子类的维护。
- 使用场景
- 扩展一个类的功能。
- 动态增加功能,动态撤销。
- 装饰器模式的4种角色:
- 抽象构件角色(Component):具体构件类和抽象装饰者类的共同父类。
- 具体构件角色(ConcreteComponent):抽象构件的子类,装饰者类可以给它增加额外的职责。
- 装饰角色(Decorator):抽象构件的子类,具体装饰类的父类,用于给具体构件增加职责,但在子类中实现。
- 具体装饰角色(ConcreteDecorator):每一个具体装饰类都定义了一些新的行为,负责向构件添加新的职责。
- 优点
- 不改动原有代码,动态增加功能。
- 对象间不会相互依赖、松耦合。
- 符合开闭原则,扩展性好,便于维护。
- 缺点
- 装饰器环节过多的话,导致装饰器类膨胀。
- 装饰器层层嵌套比较复杂,可能导致排查问题流程繁琐
- 代码示例
from abc import abstractmethod, ABCMeta
class IMilktea(metaclass=ABCMeta):
"""
抽象构件(Component)角色:奶茶
"""
@abstractmethod
def add_dosing(self): ...
class PearlMilktea(IMilktea):
"""
具体构件(ConcreteComponent)角色:珍珠奶茶
"""
def add_dosing(self):
print("开始制作:珍珠奶茶")
class LemonMilktea(IMilktea):
"""
具体构件(ConcreteComponent)角色:柠檬水奶茶
"""
def add_dosing(self):
print("开始制作:柠檬水")
class Dosing(IMilktea):
"""
装饰(Decorator)角色:配料
"""
def __init__(self, imilk_tea: IMilktea):
self.imilk_tea = imilk_tea
def add_dosing(self):
self.imilk_tea.add_dosing()
class Pearl(Dosing):
"""
具体装饰(ConcreteDecorator)角色
"""
def add_dosing(self):
super(Pearl, self).add_dosing()
print("制作中:加珍珠")
class Mango(Dosing):
"""
具体装饰(ConcreteDecorator)角色
"""
def add_dosing(self):
super(Mango, self).add_dosing()
print("制作中:加芒果")
if __name__ == "__main__":
tea = PearlMilktea()
tea = Pearl(tea)
tea = Mango(tea)
tea.add_dosing()
外观模式
Python 外观模式是一种常见的设计模式,它提供了一种简单的方式来组合多个复杂的子系统,使其对外表现为一个简单的接口。
- 外观模式结构:
- 外观类(Facade Class):提供一个简单的接口,将子系统中的多个复杂类组合起来,对客户端隐藏子系统的实现细节。
- 子系统类(Sub System Classes):包含多个复杂的子系统,实现了外观接口所声明的功能。
- 客户端类(Client Class):通过外观接口访问子系统,与外观类进行交互。
- 代码示例
class SubSystemA:
def method_a(self):
print("SubSystemA method_a() called.")
class SubSystemB:
def method_b(self):
print("SubSystemB method_b() called.")
class SubSystemC:
def method_c(self):
print("SubSystemC method_c() called.")
class Facade:
def __init__(self):
self._subsystem_a = SubSystemA()
self._subsystem_b = SubSystemB()
self._subsystem_c = SubSystemC()
def operation(self):
self._subsystem_a.method_a()
self._subsystem_b.method_b()
self._subsystem_c.method_c()
class Client:
def __init__(self):
self._facade = Facade()
def run(self):
self._facade.operation()
client = Client()
client.run()
享元模式
Python 享元模式是一种优化性的设计模式,它通过共享对象来减少系统中的对象数量,从而提高了系统的性能。该模式的核心思想是将大量的细粒度对象共享,使得系统中实际存在的对象数量极少。
享元模式有两个主要角色:
- 享元工厂类(Flyweight Factory Class):负责创建和管理享元对象,以确保对象的共享。
- 享元对象类(Flyweight Object Class):包含内部状态和外部状态两部分,内部状态存储于对象内部,外部状态则由客户端通过参数传入。
- 代码示例
class Flyweight:
def __init__(self, shared_state):
self._shared_state = shared_state
def operation(self, unique_state):
print(f"Flyweight: Displaying shared ({self._shared_state}) and unique ({unique_state}) state.")
class FlyweightFactory:
def __init__(self):
self._flyweights = {}
def get_flyweight(self, shared_state):
if shared_state not in self._flyweights:
self._flyweights[shared_state] = Flyweight(shared_state)
return self._flyweights[shared_state]
def list_flyweights(self):
print(f"FlyweightFactory: I have {len(self._flyweights)} flyweights:")
for key in self._flyweights.keys():
print(key)
if __name__ == "__main__":
factory = FlyweightFactory()
flyweight1 = factory.get_flyweight("state_1")
flyweight1.operation("unique_1")
flyweight2 = factory.get_flyweight("state_2")
flyweight2.operation("unique_2")
flyweight3 = factory.get_flyweight("state_1")
flyweight3.operation("unique_3")
factory.list_flyweights()
代理模式
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。在代理模式中,我们创建具有现有对象的代理对象,以控制对原有对象的访问。
-
使用场景,按职责来划分,通常有以下使用场景:
- 远程代理。
- 虚拟代理。
- Copy-on-Write 代理。
- 保护(Protect or Access)代理。
- Cache代理。
- 防火墙(Firewall)代理。
- 同步化(Synchronization)代理。
- 智能引用(Smart Reference)代理。
-
优点
1、职责清晰。 2、高扩展性。 3、智能化。
-
缺点
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
- 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
-
代码示例
import abc
class BaseStation(abc.ABC):
@abc.abstractmethod
def create_tickets(self, start, end): ...
class TrainStation(BaseStation):
"""
火车站
"""
def create_tickets(self, start, end):
print(f"打印从{start}-{end}车票一张")
class ProxyPoint(BaseStation):
"""
代售点
"""
def __init__(self):
self.__train_station = TrainStation()
def create_tickets(self, start, end):
print('通过代售点')
self.__train_station.create_tickets(start, end)
if __name__ == '__main__':
proxy = ProxyPoint()
proxy.create_tickets('nanjing', 'beijing')