结构型设计模式
05.静态代理设计模式
01.静态代理模式基础
由来和背景:在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。比如以下场景就适合用代理模式
- 个人,通过中介来找房东发布的租房。
- 乘客,通过火车站来购买铁路局的车票。
- 客户,通过VPN代理来翻墙查阅国外资料。
一句话概括就是:代理模式(Proxy Pattern) :给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
静态代理使用场景:监控、统计、鉴权,日志等场景。附加功能与业务功能解耦,放到代理类统一处理,让程序员只需要关注业务方面的开发!
02.静态代理原理与实现
实现模式
- 首先创建一个接口(代理都是面向接口的),
- 创建具体实现类来实现这个接口,
- 创建一个代理类同样实现这个接口,不同之处在于具体实现类的方法中需要将接口中定义的方法的业务逻辑功能实现,而代理类中的方法只要调用具体类中的对应方法即可,这样我们在需要使用接口中的某个方法的功能时直接调用代理类的方法即可,将具体的实现类隐藏在底层。
举一个实际案例
- 定义用户找房子的需求接口
- 创建用户找房子具体实现类
- 创建一个代理中介,委托中介去找房子
03.静态代理分析
代理模式包含如下角色:
- Subject: 抽象主题角色
- Proxy: 代理主题角色
- RealSubject: 真实主题角色
04.代理模式优势
- 使用静态代理来降低耦合。比如,邮件发送的接口 MailSender,以及一个实现该接口的具体类 RealMailSender。可以在不修改 RealMailSender 类的情况下,为邮件发送操作添加额外的功能。这样,RealMailSender 类和 MailSenderProxy 类之间的耦合度降低,客户端只需要与 MailSender 接口进行交互,而不需要关心具体的实现类。
- 静态代理可以用于保护真实对象的使用权限。在真正需要显示图像时,真实对象才会被加载和显示,从而保护了真实对象的使用权限。
05.静态代理不足
- 静态代理缺乏灵活说明
- 代码复用难,会导致代码庞大
- 难以动态添加功能
- 静态代理无法满足多态性
06.动态代理设计模式
01.为何要动态代理
一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色(委托类),该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。
02.动态代理的概念
动态代理是指:代理类是在运行期间生成的,也就是说 Java 编译完之后并没有实际的 class 文件,而是在运行时动态生成的类字节码,并加载到JVM中。
动态代理定义:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象。
03.动态代理的实现
代理模式的角色分四种:
- 主题接口:Subject,是委托对象和代理对象都共同实现的接口,即代理类的所实现的行为接口。
- 目标对象:ReaSubject,是原对象,也就是被代理的对象。
- 代理对象:Proxy,是代理对象,用来封装真是主题类的代理类。
- 客户端:使用代理类和主题接口完成一些工作。
04.动态代理案例
组成元素:
- 抽象类接口
- 被代理类(具体实现抽象类接口的类)
- 动态代理类,实际调用被代理类的方法和属性
动态代理有两种实现方式:
- 基于接口动态代理。一种常见的动态代理方式,它通过实现Java的Proxy类和InvocationHandler接口来实现。
- 基于类动态代理。另一种常见的动态代理方式,它使用第三方库或框架来实现。在Java中,常用的类动态代理库包括CGLIB。
05.动态代理思想和原理
动态代理实现机制,主要是通过关键类Proxy
和InvocationHandler
来实现的。
- 创建
InvocationHandler
处理器对象,主要作用是通过实现方法,调用从代理类到委托类的分派转发。 - 通过
Proxy
创建代理对象,传入指定 ClassLoader 对象和一组 interface 来创建动态代理类,里面实现是通过反射去动态创建对象。
07.适配器模式设计思想
01.适配器模式基础
什么叫做适配器模式?用来做适配,将不兼容的接口转化成兼容的接口,例子就是 USB 转接头充当适配器,把两种不兼容的接口,通过转接变得可以一起工作。
简单来说,用于事后补救措施,项目代码后期,想让不想关的类,变成可以一起工作。可以作为类结构型模式,也可以作为对象结构型模式。
适配器模式思考?不要强行适配,设计前期就要考虑到兼容性,以减少对适配器依赖!
02.适配器模式实现
适配器模式两种实现方式:
- 类的适配器。通过继承来实现
- 对象的适配器。通过组合来实现
03.适配器模式分析
类适配器是一种结构型设计模式,它允许将一个类的接口转换为另一个客户端所期望的接口。通过继承原始类和实现目标接口,类适配器使得原始类的接口与目标接口兼容。
对象适配器是适配器模式的一种变体,它通过组合(而不是继承)原始类和目标接口来实现适配器功能。在对象适配器中,适配器类持有一个原始类的实例,并实现目标接口,通过调用原始类的方法来实现目标接口的方法。
04.适配器应用解析
类适配器:现有一台电脑只能读取SD卡,而要读取TF卡中的内容的话就需要使用到适配器模式。创建一个读卡器,将TF卡中的内容读取出来。
对象适配器:有一个MediaPlayer接口,它可以播放mp3格式的文件。现在,我们想扩展这个功能,使其也可以播放其他格式的文件,比如vlc和mp4。
05.应用场景分析
一般来说,适配器模式可以看作一种“补偿模式”,用来补救设计上的缺陷。应用这种模式算是“无奈之举”。如果在设计初期,我们就能协调规避接口不兼容的问题,那这种模式就没有应用的机会了。
- 封装有缺陷的接口设计。
- 设计统一多个类的接口。
- 替换依赖的外部系统。
- 兼容老版本接口。
- 适配不同格式的数据。
08.装饰者模式设计思想
01.装饰者模式基础
给一个类或者对象增加行为:1.继承,子类通过重写父类方法修改行为。2.关联机制,将一个类的对象嵌入另一个对象中用于行为拓展。
装饰者模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。
装饰者模式适用于以下场景:
- 动态地给一个对象添加额外的功能,而不需要修改其原始类的代码。
- 需要扩展一个类的功能,但是使用继承会导致类的数量急剧增加。
- 需要在不影响其他对象的情况下,对对象的功能进行动态组合和排列。
02.装饰者模式实现
比如:有一个基本的咖啡对象,可以动态地添加不同的配料(如牛奶和糖)。在不改变对象情况下,对行为进行拓展!
- 咖啡A,加牛奶和半糖
- 咖啡B,加牛奶和全糖
- 咖啡C,加牛奶和多糖
- 咖啡D,不加牛奶和半糖
在装饰模式中的角色有:
- ● 抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
- ● 具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。
- ● 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
- ● 具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。
03.装饰者实例演示
半透明的装饰者模式是介于装饰者模式和适配器模式之间的。适配器模式的用意是改变所考虑的类的接口,也可以通过改写一个或几个方法,或增加新的方法来增强或改变所考虑的类的功能。大多数的装饰者模式实际上是半透明的装饰者模式,这样的装饰者模式也称做半装饰、半适配器模式。
04.装饰器模式场景
装饰者模式在Java语言中的最著名的应用莫过于Java I/O标准库的设计了。
IO流中装饰者模式结构如下所示:
- 抽象构件(Component)角色:由InputStream扮演。这是一个抽象类,为各种子类型提供统一的接口。
- 具体构件(ConcreteComponent)角色:由ByteArrayInputStream、FileInputStream、PipedInputStream、StringBufferInputStream等类扮演。它们实现了抽象构件角色所规定的接口。
- 抽象装饰(Decorator)角色:由FilterInputStream扮演。它实现了InputStream所规定的接口。
- 具体装饰(ConcreteDecorator)角色:由几个类扮演,分别是BufferedInputStream、DataInputStream以及两个不常用到的类LineNumberInputStream、PushbackInputStream。
05.装饰器模式分析
09.外观模式设计思想
01.外观模式基础
外观模式的由来是为了解决软件系统中的复杂性和耦合性问题。在大型软件系统中,各个子系统之间可能存在复杂的依赖关系和交互逻辑,这导致了系统的可维护性和可扩展性变得困难。为了简化客户端与子系统之间的交互,外观模式被引入。
定义是:门面模式为子系统提供一组统一的接口,定义一组高层接口让子系统更易用。
主要解决的问题
- 降低客户端与复杂子系统之间的耦合度。
- 简化客户端对复杂系统的操作,隐藏内部实现细节。
02.外观模式实现
假设有一个电脑系统,包含了多个子系统,如音乐功能、视频功能和联网功能。可以打开或者关闭某功能。
实现方式
- 创建外观类:定义一个类(外观),作为客户端与子系统之间的中介。提供Facade接口,抽象出通用方法打开和关闭。
- 封装子系统操作:外观类将复杂的子系统操作封装成简单的方法。三个子系统类(Subsystem):音乐功能、视频功能和联网功能
比如该案例中:
- 针对音乐,视频,联网抽象出通用功能,定义成接口【打开和关闭】。充分体现出面向对象中抽象的设计思想!
- 针对音乐,视频,联网各个子系统之间的交互,将他们的逻辑封装到外观类中,充分体现出封装的原则!
03.外观实例演示
有个需求:我们知道在UI开发中,可以绘制很多图像。比如可以绘制圆形,绘制矩形,绘制椭圆形,绘制……来达到绘制显示的效果。
05.外观模式分析
外观模式优点:1.简化接口;2.降低耦合;3.提高灵活性;4.方便维护
外观模式缺点:1.当添加新的子类系统,可能需要修改外观类,或者破坏开闭原则;
使用建议:建议1不要通过外观类给子类系统加入新的行为
10.桥接模式设计思想
01.桥接模式基础
桥接模式的由来是为了解决软件系统中的复杂性和耦合性问题。在大型软件系统中,各个子系统之间可能存在复杂的依赖关系和交互逻辑,这导致了系统的可维护性和可扩展性变得困难。为了简化客户端与子系统之间的交互,桥接模式被引入。