设计模式速通--基于head first设计模式

设计模式速览

Ⅰ.OO设计原则

六大原则总览(SOLID)

开闭原则
一个软件实体,如类、模块和函数应该对扩展开放,对修改关闭。
单一职责原则(Single Responsibility Principle)
一个类应该只有一个发生变化的原因。(只干一件事)
里氏替换原则(Liskov Substitution Principle)
所有引用基类的地方必须能透明地使用其子类的对象。
依赖倒置原则(Dependence Inversion Principle)
上层模块不应该依赖底层模块,它们都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
接口隔离原则(Interface Segregation Principle)
客户端不应该依赖它不需要的接口。类间的依赖关系应该建立在最小的接口上。
迪米特法则(最少知道原则)(Law of Demeter)
只与你的直接朋友交谈,不跟“陌生人”说话。(有关系就意味着产生依赖)
合成复用原则(Composite Reuse Principle)
尽量使用对象组合/聚合,而不是继承关系达到软件复用的目的。

1.面向接口(策略模式)

概括:在oop中使用接口去拓展某个类而非使用继承
详解:在定义一个类时把不变的成员用继承去实现,而对于易变的成员应该用接口去实现多组合。
书面语:策略模式定义了算法族(也就是一系列接口行为),分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户

例子:
如下图定义了小黄鸭类,现在有新的需求需要给小黄鸭增加新的功能。如果这些功能(在业务范畴上)不是原本就属于小黄鸭类的,只是希望拓展小黄鸭类,那么就应该使用接口去实现
小黄鸭+火箭接口 = 火箭鸭
小黄鸭+飞行接口 = 飞行鸭
小黄鸭+火箭接口+飞行接口 = 智能鸭
并且接口的好处是可以服务更多的其他类而不产生成员继承混乱的问题

策略模式

2.发布与订阅(观察者模式)

概括:如果一个对象的数据改变会立即引起多个其他对象的反应,那么就应该使用观察者模式
详解:回到上述语境中,我们把第一个对象叫做主题对象(subject),其他对象叫做观察者对象(observer),整个流程可以用下面例子来解释。观察者和主题之间是松耦合的。
书面语:观察者模式定义了对象之间的一对多依赖关系,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新

实现订阅及更新的流程:
1.获取CurrentSubject对象,简称cs
2.在cs注册一个CurrentObserver(简称co)
3.cs内部数据更新时会触发通知行为,发送给co
4.co内部更新数据行为被触发,进行数据更新

观察者模式

注意:如果观察者之间对通知次序有要求的,观察者模式是无法做到的

3.装饰者模式

概括:当我们希望给原始类增强一些内容(内容的组合很多,且不确定如何组合),那么就可以使用装饰器装起来
详解:假设给定一个原始类,我们现在需要给他增强,而且原始类和增强是一对多的关系,那么装饰器模式很有用,因为装饰器和原始类型是继承相同的接口,也就是说装饰器可以套装饰器
书面语:装饰器模式动态的将责任附加到对象身上。若要拓展功能,修饰者提供比继承更有弹性的替代方案

举例:
假设下图是一个鸡尾酒的程序设计图
假设
Component就是 酒水接口
ConcreteComponent 是 伏特加 作为基酒
ConcreteDecoratorA 是 冰红茶 作为调制酒/水
ConcreteDecoratorB 是 白兰地 作为调制酒/水
那么一杯龙舌兰少女鸡尾酒(瞎掰的)就是
ConcreteDecoratorB(ConcreteDecoratorA(ConcreteComponent())) //伪代码
这就是一个对基酒进行装饰得到鸡尾酒的例子

装饰器

注意:装饰者模式只是对原始对象的拓展,无法做到对其他装饰器的窥探,并且装饰器不能依赖具体的组件,否则可能会出错

4.工厂模式

概述:在实例化一系列继承自某个接口产品时使用工厂模式去实现产品的获取是很有用的,这样可以把实现接口和实例化对象的过程解耦
详情:工厂接口不会实现产品的创建,而是交由子类去实现,后面的增加或减少产品只和具体实现类有关,但注意的是子类只提供创建的方式,具体是否创建是调用者的事情
书面语:工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类

例子:
提一嘴依赖倒置原则 --依赖抽象,不依赖具体
这里Creator的factoryMethod的返回值是依赖Product,也就是依赖抽象

工厂模式

抽象工厂模式

概述:其实抽象工厂和工厂模式是一样的,工厂模式包含抽象工厂
详情:抽象工厂更多的是在强调产品家族
书面语:抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类

抽象工厂

5.单例模式

概述:单例模式和全局访问对象功能是一样的,好处就是可以延迟实例化(不需要的话就不会创建)
详情:如果需要一个全局唯一的资源,相比较静态资源,单例模式更好,因为延迟实例化相当于性能优化
书面语:单例模式确保一个类只有一个实例,并提供一个全局访问点

//这个示例没有加锁,多线程会出问题
public class Singleton{
    private static Singleton uniqueInstance;

    private Singleton(){ }

    public static Singleton getInstance(){
        if(uniqueInstance == null){
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}

6.命令模式

概述:在调用者和一系列目标行为存在潜在调用关系时,应该使用命令模式去整合
详情:假如调用者和目标行为是多对多的关系,那么紧耦合的调用关系会给后续的维护带来大量问题
书面语:命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作

命令模式还有更多的用途:工作队列、线程池、日程安排...

命令模式

7.适配器模式

概述:遇到接口不兼容并且不想或不能修改接口的情况下可以使用适配器模式(为了处理特殊情况的设计模式,不建议大量使用,不得已而为之)
详情:适配器模式分为类适配器,对象适配器,接口适配器;类适配器实现的方式可以概况为继承,对象适配器实现的方式可以概括为持有对象,接口适配器实现的方式可以概括为实现接口
书面语:适配器模式将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间

类适配器一般会同时继承目标类和被适配类,但有些语言不允许多重继承,所以要继承目标接口和适配类

类适配器

对象适配器是继承目标接口和持有被适配对象,并重写方法

对象适配器

接口适配器是持有被适配抽象成员,实现目标接口

接口适配器

8.外观模式

概述:当我们面临一群庞大且复杂的接口时可以用一个接口来统一简化
详情:外观模式可以使客户和子系统之间解耦,让子系统内部模块功能更容易拓展和维护,客户端不需要知道子系统内部实现和内部构成。
书面语:外观模式提供一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用

外观和适配器同样可以包装类,但外观的意图是简化接口,适配器的意图是转换成不同的接口

9.模板方法模式

概述:如果完成一件事情有一系列固定的步骤,但步骤的具体细节要细微的不同,那么可以使用模板方法
详解:模板抽象类实现了模板方法,模板方法对抽象的操作方法进行了编排,抽象操作方法留到子类实现
书面语:模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不在改变算法结构的情况下,重新定义算法中的某些步骤

模板方法是好莱坞原则的一种表现:
决策权放在高层模块,避免依赖腐败(循环依赖,高层依赖底层,底层依赖高层)

模板方法

10.迭代器模式

概述:不同的聚合对象需要不同的迭代方式,最好的实现方法是把迭代功能交由迭代器实现
详解:迭代器模式让我们能游走于聚合内的每一个元素,而又不暴露其内部的表示。把游走的任务放在迭代器,而不是聚合上。这样简化了聚合的接口和实现,也让责任各得其所
书面语:迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示

根据单一职责原则,“一个类应该只有一个引起改变的原因”,这也是管理聚合和迭代功能不要写在同一个类的原因

image

11.组合模式(部分-整体模式)

概述:当用户需要对单个对象和组合对象具有一致访问性,并且对象具有层次结构时可以使用组合模式
详解:组合模式模糊了简单元素和复杂元素的概念,让客户可以用处理简单元素的方式来处理复杂元素,从而使客户和复杂元素解耦
补充概念:简单元素是叶节点(内部没有子节点),复杂元素是枝节点(一般叫节点就可以了,内部有多个子节点)
书面语:组合模式允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合

组合模式实际上是以牺牲单一职责原则来换取客户的透明性(一致对待)
组合模式不仅要管理组合层次,有些还要执行一些操作(比如菜单案例,节点需要管理层次,还有菜单操作)
组合模式可以和迭代器一起使用,叫组合迭代器
实现组合迭代器最简单的方式是使用递归(深度优先)
如果菜单项没有子项可遍历,那么有两种返回选择
1.createIterator返回null,但是客户要进行代码判空
2.返回一个特殊迭代器,这个迭代器的hasNext永远返回false (空迭代器)
一般返回空迭代器比较好,这也算是一个小技巧

组合模式

12.状态模式

概述:一个拥有状态的context对象,在不同状态下有着不同的行为
详解:状态模式和策略模式很像,策略模式是(让代码具有弹性)扩展对象行为,状态模式强调选择一个最合适的策略对象(或行为)
书面语:状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类

状态模式

13.代理模式

概括:一些不太适合直接管理的对象,可以使用代理模式
详解:创建代表对象,让代表对象控制某对象的访问,被代理对象可以是远程对象、创建开销大的对象或需要安全控制的对象
书面语:代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问

代理模式有很多种,比如
远程代理:被代理对象不在本地环境,代理对象通过网络转发控制被代理对象,客户则负责操控代理对象
虚拟对象:大开销对象往往会延迟创建,在对象创建前和创建时,代理会负责接收请求,待对象创建完成,代理会把请求委托给对象
还有一些其他代理,请自行了解:智能引用代理,缓存代理,同步代理,复杂隐藏代理,写入时复制代理

代理模式

14.复合模式

概括:多个模式复合使用
详解:
书面语:

15.设计模式

概括:上述内容都是基于Head First设计模式这本书的解读,这篇章来理解设计模式
个人理解:选择设计模式的第一目标是解决特定问题(需求),第二目标是在管理成本(潜在需求变更导致的修改代码量)和控制成本(过多的设计导致性能低下)之间选择一个能达到成本最低的平衡点
书面语:模式是在某种情景(context)下,针对某问题的某种解决方案

补充

16.原型模式

概述:通过复制现有实例来创建新的实例,无需知道相应类的信息
详解:拷贝分为浅拷贝和深拷贝,浅拷贝只复制对象的引用(共用引用对象);深拷贝完全复制对象(两个独立引用的对象)

原型模式可以解决构建复杂对象的资源消耗问题,能再某些场景中提升构建对象的效率;还有一个重要的用途就是保护性拷贝,可以通过返回一个拷贝对象的形式,实现只读的限制。

17.桥接模式

概述:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
详解:桥接模式通常适用于以下场景
1.当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。
2.当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。
3.当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时

posted on 2023-03-08 23:58  GOUCCI  阅读(89)  评论(0)    收藏  举报

导航