gof-设计模式

发布时间 2023-05-30 15:07:16作者: 于贰哥

一、什么是设计模式

设计模式(Design pattern) 是解决软件开发某些特定问题而提出的一些解决方案也可以理解成解决问题的一些思路。通过设计模式可以帮助我们增强代码的可重用性、可扩充性、 可维护性、灵活性好。我们使用设计模式最终的目的是实现代码的高内聚和低耦合。

1、创建型模式
对象实例化的模式,创建型模式用于解耦对象的实例化过程。

单例模式:某个类只能有一个实例,提供一个全局的访问点。
工厂方法模式:一个工厂类根据传入的参量决定创建出哪一种产品类的实例。
抽象工厂模式:创建相关或依赖对象的家族,而无需明确指定具体类。
建造者模式:封装一个复杂对象的创建过程,并可以按步骤构造。
原型模式:通过复制现有的实例来创建新的实例。

2、结构型模式
把类或对象结合在一起形成一个更大的结构。

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

3、行为型模式
类和对象如何交互,及划分责任和算法。

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

二、类设计原则

1、单一职责原则
对于一个类,只有一个引起该类变化的原因;该类的职责是唯一的,且这个职责是唯一引起其他类变化的原因。

2、接口隔离原则
客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上。

3、依赖倒转原则
依赖倒转原则是程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。

4、里式代换原则
任何基类可以出现的地方,子类一定可以出现。里氏代换原则是继承复用的基石,只有当衍生类可以替换基类,软件单位的功能不受影响时,基类才能真正的被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

5、开闭原则
(1)对于扩展是开放的(Open for extension)。这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。

(2)对于修改是关闭的(Closed for modification)。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接的库、DLL或者.EXE文件,都无需改动。

6、迪米特法则
迪米特法则又叫做最少知识原则,就是说一个对象应当对其它对象又尽可能少的了解,不和陌生人说话。

7、合成复用原则
合成复用原则要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范。

三、详解

3.1 单例模式:某个类只能有一个实例,提供一个全局的访问点。

实现方式:
懒汉式、饿汉式、饿汉式(双锁)、枚举
使用场景:只能有一个实例的情况。例如,windows任务管理器。

3.2 工厂方法模式:一个工厂类根据传入的参量决定创建出哪一种产品类的实例。

特点:直接返回产品对象
角色:产品接口、产品类、工厂、客户端。产品类实现产品接口,客户端调用工厂类,返回产品实现类。
使用场景:创建过程很复杂、创建过程不需要外界输入参数,会使用工厂模式,如果过程很简单,那么使用构造方法不香么。

3.3 抽象工厂模式:创建相关或依赖对象的家族,而无需明确指定具体类。

特点:返回工厂。所以又称它为:其他工厂的工厂
角色:产品接口、工厂抽象类、产品实现类、工厂实现类、客户端。客户端调用工程实现类得到工厂,再工厂中传入方法得到产品
使用场景:多个工厂、多个产品
问:为啥抽象工厂模式要继承工厂抽象类,而不是实现工厂接口?
答:我们现实中,有华为工厂,小米工厂,长城工厂,吉利工厂等等,那我们是否听说过有一个工厂既是华为工厂又是小米工厂?答案是否定的,因为工厂具有单一性。回到问题本身,java支持单继承、多实现。如果采用实现接口的方式,那么久出现了一个工厂既是华为工厂又是小米工厂这种有悖现实的情况,而选择继承则完美的解决了这个问题。

3.4 建造者模式:封装一个复杂对象的创建过程,并可以按步骤构造。

特点:对象实例是一步一步构建出来的。
角色:产品、抽象建造者、具体建造者、指挥者。
使用场景:必填属性很多,为了代码的可读性和易用性,使用建造者模式;类的属性之间存在一定的依赖关系或者约束条件,为了能够对依赖关系和约束条件进行集中校验,可以使用建造者模式;希望创建不可变对象,也就不能暴露set()方法。可以使用建造者模式,然后私有化构造函数

3.5原型模式:通过复制现有的实例来创建新的实例。

特点:通过复制的方式生产对象实例
角色:java中通过实现cloneable接口,重写clone方法实现
使用场景:类初始化消耗的资源相对较多;new 产生的一个对象需要非常繁琐的过程(数据准备,访问权限等);构造函数比较复杂;循环体中生产大量的对象时
注:
1、深拷贝和浅拷贝都是实现原型模式的方式。
都需要实现cloneable接口并重写clone方法。
深拷贝需要在clone中重新赋值引用类型。
浅拷贝,则无需重新赋值引用类型。
2、A a = new A(),
A a_copy = a.clone(), a和a_copy是两个类,地址不一样,
浅拷贝中,A中引用类型变量地址一样,
深拷贝中,A中引用类型变量地址不一样。
3、克隆会打破单例,因此在单例的系统中,需要禁用克隆。

四、总结

1、不要为了使用设计模式而使用设计模式。有的时候硬编码的效率会比使用设计模式简单且易于维护。
2、对于接手的需求,要多花时间理解业务和需求,然后问自己“这里可能会变得很复杂吗?这里未来3个月多大可能需要扩展?
“同时也不要照着《设计模式》去写代码。你可以将《设计模式》理解为是一本字典。它的内容是没错,但一般只用来做参考。
对于一个模式要不要用,怎么用,要看场景。
3、能用简单代码解决的场景,就没有必要用设计模式