设计模式的分类与七大原则
一、设计模式的分类
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
二、设计模式的七大原则
1️⃣ 开闭原则
(1) 概念
- 定义:一个软件实体(如类、模块、函数)应该对扩展开放,对修改关闭
- 用抽象构建框架,用实现扩展细节
- 优点:提高软件系统的可复用性及可维护性
- 开闭原则是所有原则的基础
(2) 开闭原则Coding
① 声明接口ICourse
from abc import ABC, abstractmethod
class ICourse(ABC):
@abstractmethod
def get_id(self):
pass
@abstractmethod
def get_name(self):
pass
@abstractmethod
def get_price(self):
pass
② 声明接口实现类
class JavaCourse(ICourse):
def __init__(self, course_id, name, price):
self.id = course_id
self.name = name
self.price = price
def get_id(self):
return self.id
def get_name(self):
return self.name
def get_price(self):
return self.price
③ 声明新的类来继承接口实现类
class JavaDiscountCourse(JavaCourse):
def __init__(self, course_id, name, price):
super().__init__(course_id, name, price)
def get_discount_price(self):
return self.get_price() * 0.8
④ 编写测试代码
def test():
course = JavaDiscountCourse(96, "Java开发", 348.0)
print(f"课程ID:{course.get_id()} 课程名称:{course.get_name()} 课程原价:{course.get_price()} 课程折后价格:{course.get_discount_price()}元")
if __name__ == "__main__":
test()
2️⃣ 依赖倒置原则
(1) 概念
- 定义:高层模块不应该依赖于低层模块,两者都应该依赖于抽象
- 抽象不应该依赖细节,细节应该依赖抽象
- 针对接口编程,不要针对实现编程
- 优点:可以降低类间的耦合性,提高系统稳定性,提高代码可读性和可维护性,可降低修改程序所造成的风险
(2) 依赖倒置原则Coding
① 声明接口
from abc import ABC, abstractmethod
class ICourse(ABC):
@abstractmethod
def study_course(self):
pass
② 创建多个实现类(JavaCourse、FECourse、PythonCourse)
class JavaCourse(ICourse):
def study_course(self):
print("学生在学习Java课程")
class FECourse(ICourse):
def study_course(self):
print("学生在学习FE课程")
class PythonCourse(ICourse):
def study_course(self):
print("学生在学习Python课程")
③ 编写学生类
class Student:
def __init__(self):
self.course = None
# 开放set方法实现注入
def set_course(self, course):
self.course = course
# 核心方法
def study_course(self):
self.course.study_course()
④ 编写测试方法
def test():
student = Student()
student.set_course(JavaCourse())
student.study_course()
student.set_course(FECourse())
student.study_course()
if __name__ == "__main__":
test()
3️⃣ 单一职责原则
(1) 概念
- 定义:不要存在多于一个导致类变更的原因
- 一个类、接口、方法只负责一项职责
- 降低类的复杂性,提高类的可读性,提高系统的可维护性,降低变更引起的风险
(2) 单一职责原则Coding
① 创建获取课程内容的接口和管理课程的接口
# 获取课程内容接口
from abc import ABC, abstractmethod
class ICourseContent(ABC):
@abstractmethod
def get_course_name(self):
pass
@abstractmethod
def get_course_video(self):
pass
# 课程管理接口
class ICourseManager(ABC):
@abstractmethod
def study_course(self):
pass
@abstractmethod
def refund_course(self):
pass
② 创建实现类实现两个接口
class CourseImpl(ICourseManager, ICourseContent):
def study_course(self):
pass
def refund_course(self):
pass
def get_course_name(self):
return None
def get_course_video(self):
return bytes()
④ 其实也可以通过类来实现单一职责的设计,但是使用类来实现单一职责如果控制不当会使类的数量爆炸,所以不推荐使用类来实现单一职责推荐使用接口来实现,毕竟类对于接口是可以多实现的嘛,也比较容易扩展。
4️⃣ 接口隔离原则
(1) 概念
- 定义:用多个专门的接口,而不使用单一的总接口,客户端不应该依赖于不需要的接口
- 一个类对一个类的依赖,应该建立在最小的接口上
- 建立单一接口,不要建立庞大臃肿的接口
- 尽量细化接口,接口中的方法尽量少
- 在使用接口隔离原则的时候一定要适度(如果控制不好,会提升程序的复杂性)
- 优点:符合高内聚、低耦合的设计思想,从而使得类具有很好的可读性、可扩展性和可维护性
(2) 接口隔离原则Coding
① 创建多个不同的接口
from abc import ABC, abstractmethod
class IEatAnimalAction(ABC):
@abstractmethod
def eat(self):
pass
class IFlyAnimalAction(ABC):
@abstractmethod
def fly(self):
pass
class ISwimAnimalAction(ABC):
@abstractmethod
def swim(self):
pass
② 创建具体的实现类通过细粒度的实现组合来实现接口
class Dog(ISwimAnimalAction, IEatAnimalAction):
def eat(self):
pass
def swim(self):
pass
④ 接口隔离原则与单一职责原则的区别
单一职责原则:强调的是接口、类、方法的职责是单一的,这里强调的是职责,也就是说在一个接口中只要职责是单一的有多个方法也可以(例如游泳、仰泳、自由泳等);另外单一职责原则约束是对接口、类、方法的约束针对的是程序中的实现和细节。
接口隔离原则:注重的是对接口依赖的隔离,针对的是对程序框架的构建。
5️⃣ 迪米特原则(最少知道原则)
(1) 概念
- 定义:一个对象应该对其他对象保持最少的了解,又叫最少知道原则
- 尽量降低类与类之间的耦合
- 优点:降低类之间的耦合
- 强调只和朋友交流,不和陌生人说话
- 朋友:出现在成员变量、方法的输入、输出参数中的类称为成员朋友类,而出现在方法体内部的类不属于朋友类
(2) 迪米特原则Coding
① 创建Boss对象
class Boss:
def command_check_number(self, team_leader):
team_leader.check_number_of_courses()
② 创建Boss的"朋友"TeamLeader
class TeamLeader:
def check_number_of_courses(self):
course_list = []
for i in range(20):
course_list.append(Course())
print(f"在线课程的数量是:{len(course_list)}")
③ 创建TeamLeader的朋友Course
class Course:
pass
④ 编写测试类
def test():
boss = Boss()
team_leader = TeamLeader()
boss.command_check_number(team_leader)
if __name__ == "__main__":
test()
6️⃣ 里氏替换原则
(1) 概念
- 定义:如果对每一个类型为T1的对象O1,都有类型为T2的对象O2,使得以T1定义的所有程序P在所有的对象O1都替换成O2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。
- 定义扩展:一个软件实体如果适用一个父类的话,那一定适用于其子类,所有引用父类的地方必须能透明的使用其子类的对象,子类对象能够替换父类对象,而程序逻辑不变。
- 引申意义:子类可以扩展父类的功能,但不能改变父类原有的功能
- 含义1:子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
- 含义2:子类中可以增加自己特有的方法
- 含义3:当子类的方法重载父类的方法时,方法的前置条件(即方法的输入/入参)要比父类方法的输入参数更宽松
- 含义4:当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的输出/返回值)要比父类更严格或相等
- 优点1:约束继承泛滥,开闭原则的一种体现
- 优点2:加强程序的健壮性,同时变更时也可以做到非常好的兼容性提高程序的维护性和扩展性,降低需求变更时引入的风险
(2) 里氏替换原则Coding
由于里氏替换原则定义比较多,这里我们仅以含义3为参照来进行Coding,其他含义大家可以对照着文字解释进行尝试;在实际的工作中大家可以根据实际的环境来尽量满足里氏替换原则。
① 定义Base类
class Base:
def method(self, data):
print("父类被执行")
② 定义Child来实现Base
class Child(Base):
def method(self, data):
print("子类方法被执行")
③ 编写测试类
def test():
child = Child()
data = {"key": "value"}
child.method(data)
if __name__ == "__main__":
test()
7️⃣ 合成复用原则
(1) 概念
- 定义:尽量使用对象组合/聚合,而不是继承关系达到软件复用的目的(聚合has-A和组合contains-A)
- 优点:可以使系统更加灵活,降低类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少
(2) 合成复用原则Coding
① 创建抽象基类
from abc import ABC, abstractmethod
class DBConnection(ABC):
@abstractmethod
def get_connection(self):
pass
② 创建实现类来继承抽象基类
class MySQLConnection(DBConnection):
def get_connection(self):
return "MySQL数据库连接"
class PostgreSQLConnection(DBConnection):
def get_connection(self):
return "PostgreSQL数据库连接"
③ 创建业务应用
class ProductDao:
def __init__(self):
self.db_connection = None
def set_db_connection(self, db_connection):
self.db_connection = db_connection
def add_product(self):
conn = self.db_connection.get_connection()
print(f"使用{conn}增加产品")
④ 创建测试类
def test():
product_dao = ProductDao()
product_dao.set_db_connection(PostgreSQLConnection())
product_dao.add_product()
if __name__ == "__main__":
test()
合成复用原则是满足开闭原则以及里氏替换原则的。
您已阅读完《设计模式(共2篇)》专题的第 1 篇。请继续阅读该专题下面的文章: