飞道的博客

一句话介绍面向对象设计原则与23种设计模式

360人阅读  评论(0)

设计模式对于软件的设计和架构是非常重要的一部分,这篇文章的目的在于帮助初学者更好的理解每一个设计模式的基本思想,并不能从根本上替代这门课程的学习。在学习设计模式的时候,每种设计模式的特点、优点、使用场景、UML图、代码实现都是比较重要的知识点。

设计模式是由面向对象的设计原则引出的,因此我们从面向对象设计原则开始,依次介绍其基本思想。

一、面向对象设计原则

面向对象设计原则告诉我们在面向对象编程中,什么样的设计才是好的设计。在当今互联网的应用中,需求是会持续更改的,或者我们的产品是从原型依次将功能进行扩展的,因此我们的产品在设计的时候必须对扩展是友好的。这也是这一部分的核心思想:让我们在扩展功能时尽可能地方便。

  1. 单一职责原则 (Single Responsibility Principle): 一个类应该只有一个变化的原因,在设计的时候要将不同的职责封装到不同的类中。(这条原则保证了面向对象设计的高内聚低耦合,让无关的功能放在不同的类中,达到解耦和增强内聚性的目标)
  2. 开放-封闭原则 (Open-Closed Principle): 类、模块、函数应该是可以扩展的,但是是不可修改的,主要依赖接口、继承和多态来实现
  3. 里氏替换原则 (Liskov Substitution Principle): 任何基类出现的地方,子类一定可以出现,当子类可以完全替代基类的时候,使用继承才是合理的
  4. 接口分离原则 (Interface Segreagation Principle): 接口应该尽可能是原子的、内聚的,它提供的是独立的服务;实现接口的时候不应该要强迫实现以后不会用的抽象方法
  5. 依赖倒置原则 (Dependence Inversion Principle): 高层模块不应该依赖于底层模块,两者应该依赖于共同的接口
  6. 组合/聚合复用原则 (Composite/Aggregate Reuse Principle): 多用组合和聚合,少用继承
  7. 最小知识原则 (Principle of Least Knowledge): 每个类应该对其他类保留有限的知识,也就是不要让太多类耦合在一起

将上面的思想进行总结,可以归纳出一下几点:

  • 多使用聚合和组合,尽量避免使用继承,除非子类可以完全替代基类
  • 在设计类或接口的时候,要将相关的功能放在一起,并避免将不同的功能放在一起;对于其他类的方法的调用,要尽可能地少,以此来实现高内聚低耦合的设计
  • 高层模块不应该依赖底层模块,两者应该实现共同的接口

然而,这样的原则在设计上对我们的帮助还是微乎其微,因此诞生了设计模式,来帮助我们进行类的设计。

二、设计模式

设计模式一共分为三种,行为型设计模式(Behavioral Pattern)、构造型设计模式(Creational Pattern)、结构型设计模式(Structural Pattern)。

行为型设计模式:

  1. 观察者模式 (Observer Pattern):当一个类频繁的发生变化,可以设计一个实体类接口(Subject)和一个观察者接口(Observer),并对其进行实现,来进行解耦;Subject接口及其子类管理观察者,Observer接口在观察到发生变化后对Subject接口的子类进行更新
  2. 策略模式 (Strategy Pattern):通过让一个类与一种或多种算法的接口进行聚合,通过多态来实现算法的更换 (例如打游戏的难度选项,通过实现一个接口,来实现不同难度下敌人的分布和等级情况)
  3. 命令模式 (Command Pattern):将一个请求封装到一个对象中,并实现参数化、请求排队、请求日志、请求撤销与恢复的功能 (例如MySQL的事务)
  4. 模板方法 (Template Method):准备一个抽象类,搭建起算法的框架,将一部分共有的功能实现,并将其他部分定义为不同的抽象方法,来逼迫子类实现不同的抽象方法,以此来实现不同的逻辑 (我要制作一杯饮料,过程都是烧热水,加一种东西[抽象方法],然后倒入热水。在实现抽象方法的时候我可以实现加茶叶包或者速溶咖啡粉)
  5. 迭代器模式 (Iterator Method):提供一种不暴露底层实现的,可以以此访问聚合对象中元素的方法 (例如Java里ArrayList的iterator或Python的迭代器)
  6. 状态模式 (State Pattern):一个对象在内部状态改变的时候会改变其行为,就像改变了这个类一样。我们定义状态的接口,并通过实现类来实现每个状态下对象具体的行为。不同的状态可以用享元模式来减少内存消耗
  7. 责任链模式 (Chain of Responsibility):避免请求发送者和接收者之间的耦合,让请求在一条责任链上传递,直到传递到一个可以处理开请求的类 (例如办某项手续,却被告知要找另一个部门的人,最终找到正确的部门才能办理手续)
  8. 中介者模式 (Mediator Pattern):封装了两个对象之间相互交互的方法,减少两个对象的耦合,并让其中一个方法发生变化时,不会立即影响到其他对象
  9. 备忘录模式 (Memento Pattern):将一个对象的状态保存起来,在需要的时候再次还原到原来的状态并使用 (例如训练深度学习模型的时候,我们可以将训练好的模型保存起来,在使用的时候直接加载,无须再次训练)
  10. 访问者模式 (Visitor Pattern):当一个对象内部有许多其他的对象聚合,我们对其访问时不希望污染这些类,可以实现一个访问者类,来访问需要的参数 (例如公司年底对员工进行评价,对程序员的评价是代码量,对产品经理的评价是KPI,我们可以设计一个员工接口,并分别实现程序员类Engineer和产品经理类Manager;设计一个访问者类Visitor,来分别实现对Engineer类和Manager类的visit方法,来完成对不同标准的评价)
  11. 解释器模式 (Interpreter Pattern):给定一个语言之后,解释器模式定义一种可以解释该语言的文法,并提供一个解释器,客户可以用这个解释器来解释该语言的句子(例如编程语言的编译器)

构造型设计模式:

  1. 工厂方法 (Factory Method):定义一个接口来实现某个对象的实例化过程,但让接口的子类来定义如何实例化 (例如一个披萨,每个披萨制作过程相似,但上面放的料不同,因此我们可以定义一个制作披萨的接口,并根据不同的披萨种类来实现该接口)
  2. 抽象工厂方法 (Abstract Factory Method):为一组聚合或组合的对象创建一个接口,而不需要这些对象提供具体实现类 (工厂需要造A、B两种轿车,需要轮胎、车门、引擎、车辆框架等零件,我们实现A、B轿车两种工厂,并在底层根据辆车的不同来对这些类进行实例化)
  3. 单例模式 (Singleton Pattern):一个类只能实例化一个对象,我们只能获取一个实例化后的对象的引用 (例如线程池,我们只需要一个对象)
  4. 建造者模式 (Builder Pattern):将产品的结构和产品的零件建造过程对客户端隐藏起来,把对建造过程进行指挥的责任和具体建造者零件的责任分割开来,达到责任划分和封装的目的 (例如RTF文件可以转换为word文件、pdf文件或Markdown文件,在未来可能需要添加其他转换的方式。我们实现每一种转换方式的方法类,并在一个类中进行聚合)
  5. 原型模式 (Prototype Pattern):创建一个原型接口,实例化的时候重写其clone方法;实例化该对象的时候,直接获取该原型对象的复制

结构性设计模式:

  1. 装饰器模式 (Decorator Pattern):通过继承一个实现类的接口,来在该实现类的基础上添加职责/属性/功能
  2. 适配器模式 (Adapter Pattern):将一个接口与另一个不兼容的接口之间添加一个类,使得两者变得兼容 (通常是方法的函数变得兼容)
  3. 门面模式 (Facade Pattern):一个系统内与用户有关的接口可能过多,将这些接口聚合,为用户提供一个统一的使用接口
  4. 组合模式 (Composite Pattern):将一个有层次结构的对象用树结构来表示,通常使用二叉树 (使用二叉树也可以表示多叉树) (例如餐厅的菜单,首先分为肉菜、素材、汤、主食、甜点,每一项下面可以有其他具体的菜肴,可以用树结构来实现)
  5. 代理模式 (Proxy Pattern):为某一个对象创建一个代理对象,由代理对象来控制原对象的引用 (例如我们有一台打印机可以进行打印操作,可是我们想通过日志文件记录打印机每次操作是否正常,因此我建立一个打印机的代理对象,把打印机作为一个属性,并实现上传日志的操作)
  6. 享元模式 (Flyweight Pattern):一些相同且需要大量重复的对象,会占据大量的内存空间。我们将其存储在哈希表中,每次输入对应的键,来获取一个对象的拷贝 (例如游戏中刷经验的小怪,实际上每次杀死的都是一个小怪的复制)
  7. 桥接模式 (Bridge Pattern):将类的抽象部分与现实部分分离开,使其可以独立的变化;或将功能层次结构与实现层次结构分离 (例如我要设计的类是带颜色[黑、白、灰]的一种形状[圆形、三角形、正方形],于其我将每一种图形的每一种颜色都实现[需要实现9个类],我可以新建一个对象,把图形和颜色当作两个属性,并进行任意组合[需要实现1个类])

注:有一些设计模式使用的比较少,工厂方法、单例模式、代理模式、装饰器模式、适配器模式、模板方法、策略模式、观察者模式是比较重要的设计模式,需要重点掌握

三、几个易混淆的设计模式

  1. 装饰器模式和代理模式的区别
    代理模式偏向于将一个类本身没有能力做的事,需要其他类来干涉才能做成的事,偏向于对对象的控制;装饰器模式偏向于功能的扩展,一个类的装饰器仍然属于该类范畴本身。例如一个婴儿刚生下来不会吃饭,可以设计一个父母的类作为代理;当到达一定年龄后,可以自己吃饭了,这时候不仅要自己吃饭,还要自己收拾盘子,可以设计一个装饰器进行功能的扩展
  2. 工厂方法和抽象工厂方法的区别
    工厂方法只能有一种抽象产品,一个具体工厂类只能实例化一种具体产品类;抽象工厂方法可以有多种抽象产品,一个具体工厂类可以实例化多种具体产品类
  3. 状态模式和策略模式的区别
    状态模式是多个状态对应多种不同的行为,策略模式是一种状态对应不同的行为
  4. 享元模式和原型模式的区别
    两种模式都是为了减少内存消耗,对某些反复使用的类进行拷贝;享元模式可以通过哈希表访问多种不相关的类的对象,原型模式定义了一个圆形接口,可以实现该接口来获得不同子类,并获取其复制

参考资料

[1] 学校设计模式课程课件
[2] 单一职责原则
[3] 桥接模式
[4] 访问者模式一篇就够了
[5] 代理模式和装饰器模式的区别


转载:https://blog.csdn.net/Kayaobi/article/details/106037677
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场