零、面向对象设计原则
1. 单一原则
单一的类,实现单一的职责
2. 开闭原则
对扩展开放,对修改关闭
3. 里氏代换原则
不要破坏继承体系,父类对象出现的地方都可以使用子类对象替换,而程序的执行结果不变。
4. 依赖倒转原则
抽象不应该依赖细节,细节应该依赖于抽象。
5. 接口隔离原则
接口设计要精简单一,类间的依赖关系应该建立在最小的接口上。
6. 迪米特法则
降低耦合, 一个类应该尽量少的了解其它类的信息
7. 组合/聚合复用原则
聚合表示整体与部分的关系,表示"含有",整体由部分组合而成,部分可以脱离整体作为一个独立的个体存在。组合则是一种更强的聚合,部分组成整体,而且不可分割,部分不能脱离整体而单独存在。
- 子类是超类的一个特殊种类,而不是超类的一个角色,也就是区分"Has-A"和"Is-A".只有"Is-A"关系才符合继承关系,"Has-A"关系应当使用聚合来描述。
- 永远不会出现需要将子类换成另外一个类的子类的情况。如果不能肯定将来是否会变成另外一个子类的话,就不要使用继承。
- 子类具有扩展超类的责任,而不是具有置换掉或注销掉超类的责任。如果一个子类需要大量的置换掉超类的行为,那么这个类就不应该是这个超类的子类。
一、创建型模式->介绍对象创建
1. 工厂模式
两种模式可以用于以下场景
a. 追踪对象的创建
b. 对象的创建与使用进行解耦
c. 优化应用的性能和资源占用
1.1 工厂方法
工厂方法设计模式负责单一一种类对象的创建
通过不同的输入参数获得不同的连接对象
在日常开发中,我们多数情况下是需要和数据库进行交互的,那我们就可以使用工厂方法对数据库的交互进行处理!在下方的例子中,我们可能会有三种数据库进行连接MySQL
,PgSQL
,SQLServer
。那么我们在连接数据库部分加入一个工厂方法,用户通过不同的输入参数获得不同的连接对象。
# DesignDocs/src/factory_factory.py
from abc import ABCMeta, abstractmethod
class ConnectError(Exception):
pass
class ConnectFactory(metaclass=ABCMeta):
@abstractmethod
def connect(self):
raise
class MySQLConnect(ConnectFactory):
def connect(self):
return "MySQL连接obj"
class PgSQLConnect(ConnectFactory):
def connect(self):
return "PgSQL连接obj"
class SQLServerConnect(ConnectFactory):
def connect(self):
return "SQLServer连接obj"
def connect_factory(db_type):
if db_type == "MySQL":
return MySQLConnect()
if db_type == "PgSQL":
return PgSQLConnect()
if db_type == "SQLServer":
return SQLServerConnect()
raise ConnectError("数据库连接异常")
if __name__ == "__main__":
conn = connect_factory("MySQL")
print(conn.connect())
conn = connect_factory("PgSQL")
print(conn.connect())
conn = connect_factory("SQLServer")
print(conn.connect())
conn = connect_factory("redis")
print(conn.connect())
1.2 抽象工厂
可以理解为是工厂方法的工厂方法,源于工厂方法,本质还是实现同属一种种类的诸多工厂方法的工厂方法
抽象工厂模式是抽象方法的一种泛化,可以理解为它是一组工厂方法
在下面的例子中,我们将模拟一个数据库存储服务商,我们有一堆数据库服务,同时我们有两个简单工厂,分别帮助咱们创建两种不同费用的数据库实例给到客户端。当然,我们还有一个抽象工厂模型。在这里需要客户端告知我们他将为这个服务付费多少以及需要的数据库实例类型,我们将通过中抽象模型进行数据库实例的建立及分发。
# DesignDocs/src/abstract_factory.py
from abc import ABCMeta, abstractmethod
class ConnectError(Exception):
pass
class ConnectFactory(metaclass=ABCMeta):
@abstractmethod
def connect(self):
raise
class MySQLCommunityConnect(ConnectFactory):
def connect(self):
return "MySQL社区版连接obj"
class MySQLEnterpriseConnect(ConnectFactory):
def connect(self):
return "MySQL企业版连接obj"
class PgSQLCommunityConnect(ConnectFactory):
def connect(self):
return "PgSQL社区版连接obj"
class PgSQLEnterpriseConnect(ConnectFactory):
def connect(self):
return "PgSQL企业版连接obj"
class SQLServerCommunityConnect(ConnectFactory):
def connect(self):
return "SQLServer社区版连接obj"
class SQLServerEnterpriseConnect(ConnectFactory):
def connect(self):
return "SQLServer企业版连接obj"
def connect_community_factory(db_type):
if db_type == "MySQL":
return MySQLCommunityConnect()
if db_type == "PgSQL":
return SQLServerCommunityConnect()
if db_type == "SQLServer":
return SQLServerCommunityConnect()
raise ConnectError("数据库连接异常")
def connect_enterprise_factory(db_type):
if db_type == "MySQL":
return MySQLEnterpriseConnect()
if db_type == "PgSQL":
return SQLServerEnterpriseConnect()
if db_type == "SQLServer":
return SQLServerEnterpriseConnect()
raise ConnectError("数据库连接异常")
# 抽象工厂集合
def connect_factory(price, db_type):
if price > 0:
return connect_enterprise_factory(db_type)
return connect_community_factory(db_type)
if __name__ == "__main__":
# 通过预算,我们给这个客户端分配不同的数据库服务器
conn = connect_factory(0, "MySQL")
print(conn.connect())
conn = connect_factory(100, "PgSQL")
print(conn.connect())
conn = connect_factory(0, "SQLServer")
print(conn.connect())
2. 建造者模式
该模式在以下场景较为适用
a. 若您需要创建一个复杂对象(多步骤,步骤之间还有一定顺序)
b. 一个对象有不同的表现,希望将对象的构造(这里可以理解为初始传参)和表现解耦
该模式中,有两个参与者:建造者builder和指挥者director。建造者负责复杂对象的各个组成部分
建造者可能需要多个步骤来完成对象的创建
在下面的例子中,我们模拟了一个客户端获取数据库实例对象的过程。整个过程中,客户端只需要告诉我们他需要哪一家的云服务器就可以了,对于指挥者director而言,他需要监督建造者builder按照一定的流程进行建造。而建造者亦是按照一定的规范去设置建造过程中的内容,一步步的完成构建工作。客户端的操作变得及其简单化。思路回过头来,我们这里是建造者模式,不过也是创建型的一种,目的就是为了创建对象,在这里我们可以很清楚的感受到创建对象的过程变得简单多了。
# DesignDocs/src/factory_builder.py
from abc import ABCMeta, abstractmethod
class ConnectFactory(metaclass=ABCMeta):
@abstractmethod
def connect(self):
raise
class MySQLEnterpriseConnect(ConnectFactory):
def connect(self):
return "MySQL企业版连接obj"
# 约束建造者
class Builder(metaclass=ABCMeta):
# 帮客户购买机器
@abstractmethod
def buy(self):
raise
# 帮客户开机
@abstractmethod
def open(self):
raise
# 帮客户设置机器磁盘大小
@abstractmethod
def disk(self):
raise
# 帮客户安装数据库软件
@abstractmethod
def install(self):
raise
# 客户获取数据库实例
@abstractmethod
def db(self):
raise
class BaseBuilder(Builder):
def __init__(self):
self._name = "客服"
def buy(self):
print(f"{self._name}帮助客户下单了一台数据库服务器")
def open(self):
print(f"{self._name}将服务器进行了开机")
def disk(self):
print(f"{self._name}将服务器设置为40GB")
def install(self):
print(f"{self._name}将数据库安装进入了服务器")
def db(self):
return MySQLEnterpriseConnect().connect()
class Alibuilder(BaseBuilder):
def __init__(self):
self._name = "阿里云客服"
def disk(self):
print(f"{self._name}将服务器设置为38GB")
class Huaweibuilder(BaseBuilder):
def __init__(self):
self._name = "华为云客服"
def disk(self):
print(f"{self._name}将服务器设置为39GB")
class DBDirector():
# 数据库指挥者
def __init__(self):
self.builder = None
def construct_builder(self, builder: Builder):
self.builder = builder
self.builder.buy()
self.builder.open()
self.builder.disk()
self.builder.install()
def db(self):
return self.builder.db()
if __name__ == "__main__":
# user_choice = "alibuilder"
user_choice = "huaweibuilder"
if user_choice == "alibuilder":
builder = Alibuilder()
else:
builder = Huaweibuilder()
# 指挥者指挥建造者进行数据库实例的构建
instance = DBDirector()
instance.construct_builder(builder)
conn = instance.db()
print("客户端获取连接对象:", conn)
3. 原型模式
该模式中,将帮助我们创建一个对象的克隆
python语言中,对象的克隆是一个内置的特性,在python中变量就是标签
。
# DesignDocs/src/factory_prototype.py
import copy
class User():
def __init__(self, id, name, age, gender):
self.id = id
self.name = name
self.age = age
self.gender = gender
def __str__(self):
return "%s-%s-%s-%s" % (self.id, self.name, self.age, self.gender)
class Prototype():
def __init__(self):
self.obj = dict()
def register(self, identify, obj):
self.obj[identify] = obj
def unregister(self, identify):
del self.obj[identify]
def clone(self, identify, **attrs):
found_obj = self.obj.get(identify)
if not found_obj:
raise
obj = copy.deepcopy(found_obj)
obj.__dict__.update(attrs)
return obj
if __name__ == "__main__":
data01 = User("1", "张三", 20, "男")
print(data01)
prototype = Prototype()
identify = "data001"
prototype.register(identify, data01)
data02 = prototype.clone(identify, **{
"id": 2,
"name": "李四",
"age": 22,
"gender": "男",
})
print(data01, data02, id(data01), id(data02))
二、结构型模式->介绍不同对象之间的关系
1. 适配器模式
该模式中,帮助我们实验两个不兼容接口之间的兼容
比如iPhone手机,国行、港版、美版、欧版手机的充电接口都是Lightning
,但是不同区域会有不同的标准,这就导致我们在使用iPhone过程中需要使用不同的充电器来为iPhone供电,为了解决问题,我们可以定义一个适配器为手机充电,例如下面的例子:
# DesignDocs/src/struct_adapter.py
class Charge:
"""标准充电器"""
def start(self):
print(f"标准充电器, 为iPhone充电中")
class ChineseCharge:
"""国行充电器"""
def charge5V(self):
print("国行5v充电器,为iPhone充电中")
class OtherCharge:
"""其他充电器"""
def super_charge(self):
print("其他超级快充充电器,为iPhone充电中")
class AdapterCharge:
"""转换器,为充电器适配"""
def __init__(self, charge, *args, **kwargs):
self.__charge = charge(*args, **kwargs)
if isinstance(self.__charge, Charge):
self.__charge.do_charge = self.__charge.start
if isinstance(self.__charge, ChineseCharge):
self.__charge.do_charge = self.__charge.charge5V
if isinstance(self.__charge, OtherCharge):
self.__charge.do_charge = self.__charge.super_charge
def execute(self, *args, **kwargs):
self.__charge.do_charge(*args, **kwargs)
if __name__ == "__main__":
adapter1 = AdapterCharge(Charge)
adapter1.execute()
adapter2 = AdapterCharge(ChineseCharge)
adapter2.execute()
adapter3 = AdapterCharge(OtherCharge)
adapter3.execute()
2. 修饰器模式
该模式中,能够支持动态的扩展对象功能
# DesignDocs/src/struct_decorator.py
import time
import functools
def memoize(f):
cache_dict = {}
@functools.wraps(f)
def wrapper(*args):
if args not in cache_dict:
cache_dict[args] = f(*args)
return cache_dict[args]
return wrapper
@memoize # 语法糖 对于一些其他语言,可以这样 fibo = memoize(fibo)
def fibo(n):
if n < 2:
return 1
return fibo(n-1) + fibo(n-2)
if __name__ == "__main__":
t1 = time.time()
ret = fibo(40)
print(f"ret: {ret} use time: {time.time() - t1}")
3. 外观模式
外观设计模式有助于隐藏系统的内部复杂性,并通过一个简化的接口向客户端暴露必要的部分
# DesignDocs/src/struct_facade.py
from abc import ABCMeta, abstractmethod
class Component(metaclass=ABCMeta):
# 通用零部件
@abstractmethod
def __init__(slef):
# 初始化
pass
@abstractmethod
def boot(self):
# 启动
pass
@abstractmethod
def stop(self):
# 停止
pass
class Engine(Component):
def __init__(self):
self.name = "本田发动机"
def boot(self):
print(f"{self.name} 启动中")
def stop(self):
print(f"{self.name} 已停止")
class Electrical(Component):
def __init__(self):
self.name = "汽车电器件"
def boot(self):
print(f"{self.name} 启动中")
def stop(self):
print(f"{self.name} 已停止")
class Display(Component):
def __init__(self):
self.name = "汽车中控显示器"
def boot(self):
print(f"{self.name} 启动中")
def stop(self):
print(f"{self.name} 已停止")
class UserOperator:
def __init__(self):
self.engine = Engine()
self.electrical = Electrical()
self.display = Display()
def start(self):
self.engine.boot()
self.electrical.boot()
self.display.boot()
def stop(self):
self.display.stop()
self.electrical.stop()
self.engine.stop()
def stop_display(self):
self.display.stop()
if __name__ == "__main__":
op = UserOperator()
op.start() # 点火
op.stop_display() # 关闭中控显示器
# 小车行驶.....
op.stop() #熄火
4. 享元模式
享元就是一个包含状态独立的不可变(又称固有的)数据的共享对象 (关注的是共享对象数据)
若想要享元模式有效应该满足下面的条件:
应用需要使用大量的对象。
对象太多,存储/渲染它们的代价太大。一旦移除对象中的可变状态(因为在需要之时,应该由客户端代码显式地传递给享元),多组不同的对象可被相对更少的共享对象所替代。
对象ID对于应用不重要。对象共享会造成ID比较的失败,所以不能依赖对象ID(那些在客户端代码看来不同的对象,最终具有相同的ID)。
# DesignDocs/src/struct_flyweight.py
from abc import ABCMeta
class Car(metaclass=ABCMeta):
name = "car"
class MiniCar(Car):
name = "MiniCar"
class BusCar(Car):
name = "BusCar"
class BigCar(Car):
name = "BigCar"
class CreateCar:
pool = dict()
def __init__(self, car: Car):
self.car = car
def __new__(cls, car: Car, *args, **kwargs):
_car = cls.pool.get(car.name)
if not _car:
_car = object.__new__(cls)
cls.pool[car.name] = _car
return _car
def numberid(self):
return id(self.car)
if __name__ == "__main__":
mini_car = MiniCar()
bus_car = BusCar()
big_car = BigCar()
# 对象已创建,即使不停创建的实例,都是基于此对象进行创建
for _ in range(10):
c1 = CreateCar(mini_car)
print(f"{c1.car.name} car id {c1.numberid()}")
for _ in range(10):
c2 = CreateCar(bus_car)
print(f"{c2.car.name} car id {c2.numberid()}")
for _ in range(10):
c3 = CreateCar(big_car)
print(f"{c3.car.name} car id {c3.numberid()}")
for _ in range(10):
c4 = CreateCar(mini_car)
print(f"{c4.car.name} car id {c4.numberid()}")
5. 模型-视图-控制器模式
模型是核心的部分,代表着应用的信息本源,包含和管理(业务)逻辑、数据、状态以及应用的规则。
视图是模型的可视化表现。视图的例子有,计算机图形用户界面、计算机终端的文本输出、智能手机的应用图形界面、PDF文档、饼图和柱状图等。视图只是展示数据,并不处理数据。
控制器是模型与视图之间的链接/粘附。模型与视图之间的所有通信都通过控制器进行。
# DesignDocs/src/struct_mvc.py
class Model:
def __init__(self):
self.data = []
def get(self):
return self.data
def add(self, data):
self.data.append(data)
return self.data
class View:
def show(self, data):
for i in data:
print(f"view: {i}")
class Controller:
def __init__(self):
self.model = Model()
self.view = View()
def run(self):
while True:
i = input("please input to view! input q exit:\n")
i = i.rstrip("\n")
if i == "q":
break
self.model.add(i)
data = self.model.get()
self.view.show(data)
if __name__ == "__main__":
c = Controller()
c.run()
6. 代理模式
四种不同的知名代理类型
远程代理:实际存在于不同地址空间(例如,某个网络服务器)的对象在本地的代理者。
虚拟代理:用于懒初始化,将一个大计算量对象的创建延迟到真正需要的时候进行。
保护/防护代理:控制对敏感对象的访问。
智能(引用)代理:在对象被访问时执行额外的动作。此类代理的例子包括引用计数和线程安全检查。
三、行为型模式->介绍对象之间通信的关系
1. 责任链模式
责任链(Chain of Responsibility)模式用于让多个对象来处理单个请求时,或者用于预先不知道应该由哪个对象(来自某个对象链)来处理某个特定请求时。其原则如下所示。
(1) 存在一个对象链(链表、树或任何其他便捷的数据结构)。
(2) 我们一开始将请求发送给链中的第一个对象。
(3) 对象决定其是否要处理该请求。
(4) 对象将请求转发给下一个对象。
(5) 重复该过程,直到到达链尾。