设计模式的分类和六大设计原则

设计模式分类

在这里插入图片描述

按目的分类

根据模式是用来完成什么工作来划分,这种方式可分为创建型模式、结构型模式和行为型模式 3 种。

  • 创建型模式
    用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。GoF 中提供了单例、原型、工厂方法、抽象工厂、建造者等 5 种创建型模式。
  • 结构型模式
    用于描述如何将类或对象按某种布局组成更大的结构,GoF 中提供了代理、适配器、桥接、装饰、外观、享元、组合等 7 种结构型模式。
  • 行为型模式
    用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责。GoF 中提供了模板方法、策略、命令、职责链、状态、观察者中介者、迭代器、访问者、备忘录、解释器等 11 种行为型模式

创建型/故事:

一个人“单例模式”根据一个工厂 “工厂方法模式”的原型“原型模式”创建“建造者模式” “单例模式”.出另一个工厂 “抽象工厂”

对象实例化的模式,创建型模式用于解耦对象的实例化过程。
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。

  • 工厂模式(Factory Pattern)
    定义一个创建对象的接口,让子类决定实例化那个类。
  • 抽象工厂模式(Abstract Factory Pattern)
    创建相关或依赖对象的家族,而无需明确指定具体类。
  • 单例模式(Singleton Pattern)
    某个类只能有一个实例,提供一个全局的访问点。
  • 建造者模式(Builder Pattern)
    封装一个复杂对象的构建过程,并可以按步骤构造。
  • 原型模式(Prototype Pattern)
    通过复制现有的实例来创建新的实例。

结构型/故事:

A代替“代理模式”B去相亲,A照着B的样子装扮自己“装饰模式”,穿上上衣,裤子,帽子组成“组合模式”B的外观样子“外观模式”,模仿B的行为来适配B“适配器模式”,在桥上与美女接头“桥接模式”,结构A相中了美女,一起共享“享元模式”了晚餐“。

把类或对象结合在一起形成一个更大的结构。

  • 适配器模式(Adapter Pattern)
    将一个类的方法接口转换成客户希望的另外一个接口。
  • 桥接模式(Bridge Pattern)
    将抽象部分和它的实现部分分离,使它们都可以独立的变化。
  • 组合模式(Composite Pattern)
    将对象组合成树形结构以表示“”部分-整体“”的层次结构。
  • 装饰器模式(Decorator Pattern)
    动态的给对象添加新的功能。
  • 外观模式(门面模式)(Facade Pattern)
    对外提供一个统一的方法,来访问子系统中的一群接口。
  • 代理模式(Proxy Pattern)
    为其他对象提供一个代理以便控制这个对象的访问。
  • 享元模式(Flyweight Pattern)
    通过共享技术来有效的支持大量细粒度的对象。(池化)

行为型/故事:

观察者A“观察者模式” 在观察电台,结果发现访问者“访问者模式”黑客B,A 报告给 Boss,Boss让中介者 C“中介者模式” 来破译这些命令“命令模式”,中介者 C 用各种策略“策略模式”来破译“解释器模式”这个连环的链式“职责链模式”的必须用迭代器“迭代器模式”才能解开这个命令,然后记录“备忘录模式”下来访问者 B 的信息状态“状态模式”,形成模版“模版方法模式”,为后人栽树 。

类和对象如何交互,及划分责任和算法。

  • 责任链模式(Chain of Responsibility Pattern)
    将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
  • 命令模式(Command Pattern)
    将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
  • 解释器模式(Interpreter Pattern)
    给定一个语言,定义它的文法的一种表示,并定义一个解释器。
  • 迭代器模式(Iterator Pattern)
    一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。
  • 中介者模式(Mediator Pattern)
    用一个中介对象来封装一系列的对象交互。
  • 备忘录模式(Memento Pattern)
    在不破坏封装的前提下,保持对象的内部状态。
  • 观察者模式(Observer Pattern)
    对象间的一对多的依赖关系。
  • 状态模式(State Pattern)
    允许一个对象在其对象内部状态改变时改变它的行为。
  • 策略模式(Strategy Pattern)
    定义一系列算法,把他们封装起来,并且使它们可以相互替换。
  • 模板模式(Template Pattern)
    定义一个算法结构,而将一些步骤延迟到子类实现。
  • 访问者模式(Visitor Pattern)
    在不改变数据结构的前提下,增加作用于一组对象元素的新功能。

按作用范围分类

根据模式是主要用于类上还是主要用于对象上来分,这种方式可分为类模式和对象模式两种。

  • 类模式
    用于处理类与子类之间的关系,这些关系通过继承来建立,是静态的,在编译时刻便确定下来了。GoF中的工厂方法、(类)适配器、模板方法、解释器属于该模式。
  • 对象模式
    用于处理对象之间的关系,这些关系可以通过组合或聚合来实现,在运行时刻是可以变化的,更具动态性。GoF 中除了以上 4 种,其他的都是对象模式。

六大原则

总原则:开闭原则

定义

一个软件实体如类、模块或函数应该对扩展开放,对修改关闭。

  • 开:开闭原则
  • 口:接口隔离原则
  • 合:合成复用原则
  • 理:里氏替换原则
  • 最:迪米特法则(最少知道原则)
  • 单:单一职责原则
  • 一:依赖倒转原则

为什么要采用开闭原则:

  • 对测试的影响:通过扩展实现变化,测试只需要对新增类进行单元测试即可,单元测试是孤立的,只需要保证新类提供的方法正确就行。而如果是修改类来实现变化,则该类相应的测试方法也都要随着重构,而且当类很复杂时难免存在遗漏情况。
  • 可以提高复用性:避免以后维护人员为了修改一个微小的缺陷或增加新功能,却要在整个项目中到处查找相关的代码逐一修改。
  • 提高可维护性:开发新功能时,扩展一个类往往比修改一个类更容易。
  • 面向对象开发的要求

当一个软件实体需要扩展的时候,不要去修改原有的代码,而是去扩展原有的代码。其实开闭原则是最基础的一个原则,下面六个原则都是开闭原则的具体形态。

单一职责原则 | Single Responsibility Principle,SRP.

定义 | 每个类应该实现单一的职责

应该有且仅有一个原因引起类的变更。.

优点

  • 类的复杂性降低。类的职责单一,复杂性自然就降低了。
  • 可读性高。
  • 易维护。
  • 变更引起的风险降低。

难点

  • “职责”和“变化原因”都是不可度量的,因项目、环境而异。
  • 过细的划分会引起类的剧增,人为的增加系统的复杂性。

建议

  • 接口的设计一定要做到单一原则,类的设计尽量做到只有一个原因引起变化。
  • 职责的划分需要根据项目和经验来权衡,既要保证职责的单一性,又要尽量避免过细的划分

里氏替换原则 | Liskov Substitution Principle,LSP.

定义 | 只要有父类出现的地方,都可以使用子类来替代。但反过来不一定

所有引用基类的地方都必须能透明地使用其子类的对象。.

优点

  • 代码共享,提高代码的重用性。
  • 提高代码的可扩展性。
  • 提高产品或者项目的开放性。

难点

  • 继承是侵入式的,只要继承,就拥有了父类的属性和方法。
  • 降低代码灵活性,子类拥有了父类的属性和方法,多了一些约束。
  • 增强了耦合性。父类的常量、变量或方法改动时,必须还要考虑子类的修改,可能会有大段代码需要重构。

里氏替换原则四层含义

  • 子类必须完全实现父类的方法
    在类中调用其他类时务必使用父类或接口,如若不能,则说明类的设计已经违背LSP原则。
    如果子类不能完整的实现父类的方法,或者父类的方法在子类中发生畸变,这建议断开父子继承关系,采用依赖、聚集、组合等方式代替继承。
  • 子类可以有自己的特性:即子类出现的地方父类未必可以出现。
  • 覆盖父类的方法时输入参数可以被放大:输入参数类型宽于父类的类型的覆盖范围,例如 hashmap -> map。
  • 覆盖父类的方法时输出参数可以被缩小

依赖倒置原则 | Dependence Inversion Principle,DIP.

定义 | 面向接口编程,依赖于抽象而不依赖于具体

  • 高层模块不应该依赖低层模块,两者都要改依赖其抽象(模块间的依赖通过抽象产生,实现类不发生直接的依赖关系)
  • 抽象不应该依赖细节(接口或者抽象类不依赖实现类)
  • 细节可以依赖抽象(实现类依赖接口或者抽象类)

建议

  • 每个类尽量都有接口或抽象类。
  • 变量的表面类型尽量是接口或抽象类。
  • 任何类都不应该从具体类派生(其实只要不是超过两层的继承都是可以忍受的)。
  • 尽量不要复写基类已实现的方法。
  • 结合里氏替换原则使用。

面向接口编程

接口负责定义 public 属性和方法,并且声明与其它对象的依赖关系,抽象类负责公共构造部分的实现,实现类准确实现业务逻辑,同时在适当的时候对父类进行细化。

接口隔离原则 | Interface Segregation Principle,ISP.

定义 | 每个接口中不存在子类用不到却必须实现的方法

客户端不应该依赖他不需要的接口,类之间的依赖关系应该建立在最小的接口上。.

四层含义

  • 接口尽量要小,不要出现臃肿的接口。
  • 接口要高内聚。
  • 只提供访问者需要的方法:每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。
  • 接口设计是有限度的:接口设计粒度越小,系统越灵活。但是结构会越复杂、开发难度增加,可维护性降低。

建议

  • 一个接口只服务一个子模块或者业务逻辑。
  • 尽量压缩接口内的方法,保证方法都是有用的,避免臃肿。
  • 已经被污染的接口尽量去修改,若变更风险大,则采用适配器模式转化处理。
  • 深入了解业务逻辑,拒绝盲从。

迪克特法则 (最少知道原则) | Least Knowledge Principle,LKP.

定义 | 一个类对自己依赖的类知道的越少越好。

一个对象应该对其他对象有最少的了解(低耦合)。.

三层含义

  • 一个类只与朋友交流,不和陌生类交流,方法尽量不引入类中不存在的对象。
  • 尽量不要对外暴露过多的 public 方法和非静态的 public 变量,尽量内敛。
  • 自己的就是自己的。如果一个方法放在本类中,既不增加类间关系,也对本类不产生负面影响,那就放置在本类中。

总结

  • 迪米特法则的核心观念就是类间解耦,低耦合。
  • 其负面影响就是产生了大量的中转或者跳转类,导致系统复杂性提高,也为维护带来了难度。需要反复权衡,既做到结构清晰,又要高内聚低耦合。
  • 如果一个类需要跳转两次以上才能访问到另一个类,就需要想办法重构了。

合成复用原则 | Composite/Aggregate Reuse Principle,CARP.

定义 | 原则是尽量首先使用合成/聚合的方式,而不是使用继承。

是在一个新的对象里面使用一些已有的对象,使其成为新对象的一部分。新对象通过委派达到复用已有功能的效果。.

优点

使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上。这样类和集成层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。

缺点

通过这种方式复用建造的系统会有较多的对象需要管理;为了能将多个不同的对象作为组合块来使用,必须仔细地对接口进行定义

总结

简单地说:尽量首先使用合成/聚合的方式,而不是使用继承。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值