快速梳理23种常用的设计模式(下)(结构型)

关注我

我目前是一名后端开发工程师。主要关注后端开发,数据安全,网络爬虫,物联网,边缘计算等方向。

  • Java知识点复习全手册
  • Leetcode算法题解析
  • 剑指offer算法题解析
  • SpringCloud菜鸟入门实战系列
  • SpringBoot菜鸟入门实战系列
  • Python爬虫相关技术文章
  • 后端开发相关技术文章

本文旨在快速梳理常用的设计模式,了解每个模式主要针对的是哪些情况以及其基础特征,每个模式前都有列举出一个或多个可以深入阅读的参考网页,以供读者详细了解其实现。

快速回忆

一、创建型

  • 单例(Singleton)
  • 工厂模式 简单工厂(Simple Factory) 工厂方法(Factory Method) 抽象工厂(Abstract Factory)
  • 生成器(Builder)
  • 原型模式(Prototype)

二、行为型

  • 责任链(Chain Of Responsibility)
  • 命令(Command)
  • 解释器(Interpreter)
  • 迭代器(Iterator)
  • 中介者(Mediator)
  • 备忘录(Memento)
  • 观察者(Observer)
  • 状态(State)
  • 策略(Strategy)
  • 模板方法(Template Method)
  • 访问者(Visitor)

三、结构型

  • 适配器(Adapter)
  • 装饰器(Decorator)
  • 代理模式(Proxy)
  • 外观模式/门面模式(Facade)
  • 桥接模式(Bridge Pattern)
  • 组合模式(Composite)
  • 享元模式(Flyweight)

理念

首先搞清楚一点,设计模式不是高深技术,不是奇淫技巧。设计模式只是一种设计思想,针对不同的业务场景,用不同的方式去设计代码结构,其最最本质的目的是为了解耦,延伸一点的话,还有为了可扩展性和健壮性,但是这都是建立在解耦的基础之上。

高内聚低耦合

高内聚:系统中A、B两个模块进行交互,如果修改了A模块,不影响模块B的工作,那么认为A有足够的内聚。

快速梳理23种常用的设计模式(下)(结构型)

image.png

低耦合:就是A模块与B模块存在依赖关系,那么当B发生改变时,A模块仍然可以正常工作,那么就认为A与B是低耦合的。

快速梳理23种常用的设计模式(下)(结构型)

image.png

结构型

适配器(Adapter)

https://www.jianshu.com/p/93821721bf08

定义

客户类调用适配器的方法时,在适配器类的内部将调用适配者类的方法,而这个过程对客户类是透明的,客户类并不直接访问适配者类。因此,适配器可以使由于接口不兼容而不能交互的类可以一起工作。这就是适配器模式的模式动机。

角色

  • 目标(Target)角色:这就是所期待得到的接口。注意:由于这里讨论的是类适配器模式,因此目标不可以是类。
  • 源(Adapee)角色:现在需要适配的接口。
  • 适配器(Adaper)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。

类适配器

创建新类,继承源类,并实现新接口

对象适配器

创建新类持源类的实例,并实现新接口

类适配器使用对象继承的方式,是静态的定义方式

而对象适配器使用对象组合的方式,是动态组合的方式。

接口适配器

创建新的抽象类实现旧接口方法

总结

建议尽量使用对象适配器的实现方式,多用合成/聚合、少用继承。当然,具体问题具体分析,根据需要来选用实现方式,最适合的才是最好的。

优点

  • 更好的复用性
  • 更好的扩展性

缺点

过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。

装饰模式(Decorator)

给一类对象增加新的功能,装饰方法与具体的内部逻辑无关。

代码

装饰模式与代理模式的区别

装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。

  • 用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。
  • 当我们使用装饰器模 式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

代理模式(Proxy)

详细代码实例:https://www.cnblogs.com/daniels/p/8242592.html

简介

代理模式的定义:代理模式给某一个对象提供一个代理对象并由代理对象控制对原对象的引用。

通俗的来讲代理模式就是我们生活中常见的中介

为什么要用代理模式

  • 中介隔离作用 在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
  • 开闭原则,增加功能 真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。

有哪几种代理模式

静态代理

由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。

代码:静态代理创建代理类

静态代理总结

  • 优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
  • 缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。

动态代理:JDK反射机制(接口代理)

  • 是在程序运行时通过反射机制动态创建
    的。
  • 为需要拦截的接口生成代理对象以实现接口方法拦截功能。

代码:编写动态处理器

代码:编写测试类

动态代理总结

  • 优势:虽然相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。
  • 劣势:只能对接口进行代理

动态代理:CGLIB代理

  • 其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑
  • 但因为采用的是继承,所以不能对final修饰的类进行代理
  • JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

代码见网页

CGLIB代理总结:(与JDK代理区别:)

CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多

  • 所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。
  • 同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。

外观模式/门面模式(Facade)

https://www.cnblogs.com/lthIU/p/5860607.html

快速梳理23种常用的设计模式(下)(结构型)

image.png

快速梳理23种常用的设计模式(下)(结构型)

image.png

简单来说,该模式就是把一些复杂的流程封装成一个接口供给外部用户更简单的使用。这个模式中,涉及到3个角色。

角色

1)门面角色:外观模式的核心。它被客户角色调用,它熟悉子系统的功能。内部根据客户角色的需求预定了几种功能的组合。

2)子系统角色: 实现了子系统的功能。它对客户角色和Facade时未知的。它内部可以有系统内的相互交互,也可以由供外界调用的接口。

3)客户角色: 通过调用Facede来完成要实现的功能。

代码

优点

  • 松散耦合:使得客户端和子系统之间解耦,让子系统内部的模块功能更容易扩展和维护;
  • 简单易用:客户端根本不需要知道子系统内部的实现,或者根本不需要知道子系统内部的构成,它只需要跟Facade类交互即可。
  • 更好的划分访问层次:有些方法是对系统外的,有些方法是系统内部相互交互的使用的。子系统把那些暴露给外部的功能集中到门面中,这样就可以实现客户端的使用,很好的隐藏了子系统内部的细节。

桥接模式(Bridge Pattern)

http://www.cnblogs.com/houleixx/archive/2008/02/23/1078877.html

含义

在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种“多维度的变化”?

如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引入额外的复杂度?这就要使用Bridge模式。

快速梳理23种常用的设计模式(下)(结构型)

在这里插入图片描述

由上图变为下图

快速梳理23种常用的设计模式(下)(结构型)

在这里插入图片描述

代码

组合模式(Composite)

组合模式是为了表示那些层次结构,同时部分和整体也可能是一样的结构,常见的如文件夹或者树.

定义

组合模式定义了如何将容器对象和叶子对象进行递归组合,使得客户在使用的过程中无须进行区分,可以对他们进行一致的处理。

在使用组合模式中需要注意一点也是组合模式最关键的地方:叶子对象和组合对象实现相同的接口。这就是组合模式能够将叶子节点和对象节点进行一致处理的原因。

角色

1.Component :组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件。

2.Leaf:叶子对象。叶子结点没有子结点。

3.Composite:容器对象,定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操作,如增加(add)和删除(remove)等。

适用场景

1、需要表示一个对象整体或部分层次,在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,可以一致地对待它们。

2、让客户能够忽略不同对象层次的变化,客户端可以针对抽象构件编程,无须关心对象层次结构的细节。

享元模式(Flyweight)

https://www.cnblogs.com/chenssy/p/3330555.html

定义

所谓享元模式就是运行共享技术有效地支持大量细粒度对象的复用,所以享元模式要求能够共享的对象必须是细粒度对象。

  • 内部状态:在享元对象内部不随外界环境改变而改变的共享部分。
  • 外部状态:随着环境的改变而改变,不能够共享的状态就是外部状态。

代码

享元工厂类FlyweightFactory:

利用了HashMap保存已经创建的颜色

客户端程序:Client.java

参考

简书大牛:https://www.jianshu.com/nb/10186551

Github:https://github.com/CyC2018/Interview-Notebook/blob/master/notes/设计模式.md

菜鸟网:http://www.runoob.com/design-pattern/design-pattern-tutorial.html

补充:

23种设计模式总结:

https://www.cnblogs.com/tongkey/p/7170826.html

https://www.cnblogs.com/malihe/p/6891920.html


分享到:


相關文章: