设计模式
什么是设计模式
设计模式(Design Pattern)是软件开发中解决特定问题的经过验证的解决方案。它们是针对特定问题的可重用的、经过测试的解决方案,可以帮助开发人员编写可维护、可扩展和高效的代码。设计模式通常分为创建型、结构型和行为型三大类,每种模式都有其特定的应用场景和优点。共 23 种设计模式。
设计模式分类
设计模式分为三大类:
- 创建型模式:单例模式、工厂模式、建造者模式、原型模式
- 结构型模式:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式
- 行为型模式:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
OPP 七大原则
单一职责原则(Single Responsibility Principle, SRP)
- 定义:一个类只应该有一个职责(即只负责一件事情),并且这个职责应该由类自身完全封装。
- 目的:减少类的复杂性,使得每个类更容易理解、维护和修改。当需求变化时,只会影响到该类中的单一职责。
- 示例:假设你有一个 Report 类,它同时负责生成报告和打印报告。根据单一职责原则,这个类应该拆分为两个类:ReportGenerator 和 ReportPrinter,每个类各自负责单一的任务。
开放封闭原则(Open/Closed Principle, OCP)
- 定义:软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。
- 目的:通过继承或接口的方式扩展系统功能,而不修改现有代码,避免引入新的 bug。
- 示例:如果你有一个 Shape 基类,Circle 和 Square 类继承自 Shape,那么要添加新的形状时,你应该只需要创建一个新的子类,而不需要修改 Shape 基类或其他子类。
里氏替换原则(Liskov Substitution Principle, LSP)
- 定义:子类对象必须能够替换掉其基类对象,并且保证程序的逻辑保持不变。
- 目的:确保继承体系中的父类和子类之间是可互换的,从而保证程序的健壮性和稳定性。
- 示例:如果有一个父类 Bird,它有一个方法 fly(),但 Penguin 类(企鹅)继承了 Bird 却不能飞,这就违反了里氏替换原则。正确的做法是创建一个 FlyingBird 类,让 Penguin 继承自 Bird 而不是 FlyingBird。
接口隔离原则(Interface Segregation Principle, ISP)
- 定义:一个类不应该依赖它不需要的接口。换句话说,应当将庞大的接口拆分为更小的、更加具体的接口,这样类可以只实现它需要的接口。
- 目的:减少代码之间的耦合,使得代码更灵活,避免实现不必要的接口方法。
- 示例:假设你有一个 Worker 接口,包含 work() 和 eat() 方法,但对于机器人类,它只需要实现 work() 而不需要 eat()。此时可以将 Worker 接口拆分为 Worker 和 Eater 两个接口,这样机器人类只需要实现 Worker 接口。
依赖倒置原则(Dependency Inversion Principle, DIP)
- 定义:高层模块不应该依赖于低层模块,二者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
- 目的:通过引入抽象(接口或抽象类),减少模块之间的耦合,提高系统的灵活性和可维护性。
- 示例:在一个汽车类中,不应该直接依赖一个具体的 Engine 实现类,而是应该依赖于一个 Engine 接口,这样可以在不修改汽车类的情况下更换不同类型的引擎。
迪米特法则(Law of Demeter, LoD)
- 定义:一个对象应该对其他对象有尽可能少的了解。也叫作“最少知道原则”。
- 目的:减少类之间的耦合性,增强代码的可维护性和灵活性。
- 示例:在一个 Car 类中,如果 Car 需要使用 Engine 对象的某个方法,而这个方法又依赖 Cylinder 对象,Car 不应该直接调用 Cylinder 的方法,而是应该通过 Engine 来间接调用。
合成复用原则(Composite Reuse Principle, CRP)
- 定义:优先使用对象组合,而不是继承来达到代码复用的目的。
- 目的:通过组合多个小的对象来构建复杂的功能,而不是通过继承来扩展类的功能,从而保持类的灵活性和可维护性。
- 示例:如果你有一个 Rectangle 类和一个 Window 类,想让 Window 具有 Rectangle 的功能,应该优先考虑在 Window 中组合一个 Rectangle 对象,而不是让 Window 继承自 Rectangle。