博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
装饰器和门面设计模式介绍
阅读量:3964 次
发布时间:2019-05-24

本文共 7914 字,大约阅读时间需要 26 分钟。

装饰器模式

参考文章:

《设计模式之美》——极客时间·王争

一、概念

我们有一个文件读取功能,可以读取磁盘上的文本类文件、音乐类文件以及视频类文件。

后来,我们觉得读取速度很慢,与磁盘交互太多,想将现有的读取功能增强,增加缓冲区。

FileRead功能(Old) FileRead功能(New)
在这里插入图片描述 在这里插入图片描述

这种改进措施有很大弊端,复用性很低,需要改进。

改进后结构图 在这里插入图片描述
装饰器模式结构图(简约版) 在这里插入图片描述

装饰器设计模式:通过创建一个包装对象,也就是装饰来包裹真实的对象,从而增强一个类的功能

角色说明:

1、Component 是统一接口,也是装饰类和被装饰类的基本类型

2、ConreteComponent 是具体实现类,也是被装饰类,本身就是个具有一些功能的完整类

3、ConcreteDecorator是具体的装饰产品类,实现了Component接口的同时还在内部维护了一个Component实例,通过构造函数初始化,每一种装饰产品都具有特定的装饰效果,可以通过构造器来声明装饰哪种类型Component,从而对其装饰。

//ReadFile抽象类public abstract class FileRead {
public abstract void read();}//读取文本文件类public class FileTextRead extends FileRead {
String name = "文本"; @Override public void read() {
System.out.println(name + "文件直接读取....."); }}//读取音乐文件类public class FileMusicRead extends FileRead {
String name = "音乐"; @Override public void read() {
System.out.println(name + "文件直接读取......."); }}//读取视频文件类public class FileVedioRead extends FileRead {
String name = "视频"; @Override public void read() {
System.out.println(name + "文件直接读取,......"); }}/*** 缓冲类,同样继承ReadFile类,但是里面创建了父类对象,可以传入其三个子类对象* 实现了三个子类各自功能的同时还增加了缓冲机制*/public class BufferdFileRead extends FileRead {
private FileRead fileRead; //这里会实现多态 public BufferdFileRead(FileRead fileRead) {
this.fileRead = fileRead; } @Override public void read() {
//这个read方法是缓冲类自己的read //这个read是传入的FileRead类对象或者其子类对象的read方法 fileRead.read(); System.out.println("缓存。。。。。"); }}//测试public class test {
public static void main(String[] args) {
FileTextRead fileTextRead = new FileTextRead(); FileMusicRead fileMusicRead = new FileMusicRead(); FileVedioRead fileVedioRead = new FileVedioRead(); new BufferdFileRead(fileTextRead).read(); new BufferdFileRead(fileMusicRead).read(); new BufferdFileRead(fileVedioRead).read(); }}//执行结果文本文件直接读取.....缓存。。。。。音乐文件直接读取.......缓存。。。。。视频文件直接读取,......缓存。。。。。

二、jdk的IO流

InputStream输入流 在这里插入图片描述
标准装饰器模式结构 在这里插入图片描述

为何我们需要一个抽象装饰器类?

public class BufferedInputStream extends InputStream {
/** * The input stream to be filtered. */ protected volatile InputStream in; /** * 构造方法 */ protected FilterInputStream(InputStream in) {
this.in = in; } /** * 装饰代码,增强功能 */ public void f() {
//功能增强代码 in.f(); } /** * 其他不需要增强的方法,重新调用一下InputStream中的方法,否则无法将最终读取数据的任务委派给传递进来的InputStream对象 */ public void function() {
in.fiunction(); }}

为了避免对不需要增强功能的方法的代码进行重复书写,javaIO抽象出了一个装饰器父类FilterInputStream,所有的装饰器类都继承它,这样,装饰器只需要实现它需要增强的方法即可,其他只需要实现父类的默认实现。

> public class FilterInputStream extends InputStream {
> /**> * The input stream to be filtered.> */> protected volatile InputStream in;> > /**> * 构造方法> */> protected FilterInputStream(InputStream in) {
> this.in = in;> }> > public int read() throws IOException {
> return in.read();> }> > public int read(byte b[]) throws IOException {
> return read(b, 0, b.length);> }> > public int read(byte b[], int off, int len) throws IOException {
> return in.read(b, off, len);> }> > public long skip(long n) throws IOException {
> return in.skip(n);> }> > public int available() throws IOException {
> return in.available();> }> > public void close() throws IOException {
> in.close();> }> > public synchronized void mark(int readlimit) {
> in.mark(readlimit);> }> > public synchronized void reset() throws IOException {
> in.reset();> }> > public boolean markSupported() {
> return in.markSupported();> }> }

三、装饰器模式拓展

装饰模式所包含的 4 个角色不是任何时候都要存在的,在有些应用环境下模式是可以简化的。

装饰类和具体的装饰产品类合并 只有一个具体实现类而没有统一的抽象接口
在这里插入图片描述 在这里插入图片描述

四、优缺点

优点:装饰类和被装饰类可以独立发展,不会相互耦合,不必改变原类文件和继承的情况下,动态地扩展、增强一个对象的功能。原始类对调用方是透明的,可以根据情况灵活使用。

缺点:多层装饰比较复杂

BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(new File("D:/Java/Blog/Demo.txt")));

五、对比

代理、适配器、桥接、装饰,这四个模式有何区别

这四种模式都是比较常用的结构型设计模式,代码结构也是很类似,都是二次封装原始类。它们的主要区别就在于使用目的、要解决的问题以及使用的场景不同。

public interface CommonService {
/** 方法 */ void function();} public class AServiceImpl implements CommonService {
/** A的方法实现 */ @Override public void function() {
System.out.println("执行A的方法"); }}public class BServiceImpl implements CommonService {
/** 实现的公共接口 */ private CommonService commonService; /** 构造方法 */ public BServiceImpl(CommonService commonService) {
this.commonService = commonService; } /** 执行B的方法 */ @Override public void function() {
System.out.println("执行B所需要的其他代码......."); commonService.function(); }}

1、代理模式:不改变原始类接口的条件下,为原始类定义一个代理类,主要目的是控制访问,附加其他功能,而非增强功能,对调用者而言是不可见的。

2、桥接模式:目的是将接口部分和实现部分分离,从而让他们解耦,可以较为容易、相对独立的进行改变和拓展

3、装饰器模式:不改变原始接口类的条件下,对原始类的原有功能进行增强

4、适配器模式:提供跟原始类不同的接口,适配了两种具有不同接口(API)的对象,可以一同工作

代理模式 在这里插入图片描述 Client是房东,Proxy是蛋壳,target是租户
桥接模式 在这里插入图片描述 目的是将接口部分和实现部分分离,从而让他们解耦,可以较为容易、相对独立的进行改变和拓展;定义了一个(即两个接口之间的关系),然后通过每个接口的多个实现的不同组合达到其灵活性的目的,即先关系后组合;组合多个维度变化的接口,比如多个品牌电脑和多个电脑的功能
装饰器模式 在这里插入图片描述 Target租户自己,自己冒冷天,骑车、花钱打车到处乱转找房子,自己协商,自己考察房子情况和业主情况,自己交水电,自己办网,自己打扫。。。。
适配器模式 在这里插入图片描述 提供跟原始类不同的接口,适配了两种具有不同接口(API)的对象,可以一同工作;你提供接口A,但是客户需要的是接口B,那么我们就需要将A变成B

外观模式

一、概念

在这里插入图片描述 在这里插入图片描述
把医院作为一个子系统,按照部门职能,这个系统可以划分为挂号、门诊、划价、化验、收费、取药等。看病的病人要与这些部门打交道,就如同一个子系统的客户端与一个子系统的各个类打交道一样,不是一件容易的事情 医院可以设置一个接待员的位置,由接待员负责代为挂号、划价、缴费、取药等。这个接待员就是门面模式的体现,病人只接触接待员,由接待员与各个部门打交道

外观(Facade)模式又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,相对于外部应用程序,这样会大大降低应用程序的复杂度,提高了程序的可维护性。

二、模式结构和角色

  1. 外观(Facade)角色:为多个子系统对外提供一个共同的接口。
  2. 子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。
  3. 客户(Client)角色:通过一个外观角色访问各个子系统的功能。
外观模式结构
在这里插入图片描述

三、代码案例

package facade;public class FacadePattern{
public static void main(String[] args) {
Facade f=new Facade(); f.method(); }}//外观角色class Facade{
private SubSystem01 obj1=new SubSystem01(); private SubSystem02 obj2=new SubSystem02(); private SubSystem03 obj3=new SubSystem03(); public void method() {
obj1.method1(); obj2.method2(); obj3.method3(); }}//子系统角色class SubSystem01{
public void method1() {
System.out.println("子系统01的method1()被调用!"); } }//子系统角色class SubSystem02{
public void method2() {
System.out.println("子系统02的method2()被调用!"); } }//子系统角色class SubSystem03{
public void method3() {
System.out.println("子系统03的method3()被调用!"); } }

子系统01的method1()被调用!

子系统02的method2()被调用!
子系统03的method3()被调用!

四、应用场景

在日常编码工作中,我们都在有意无意的大量使用外观模式。只要是高层模块需要调度多个子系统(2个以上的类对象),我们都会自觉地创建一个新的类封装这些子系统,提供精简的接口,让高层模块可以更加容易地间接调用这些子系统的功能。尤其是现阶段各种第三方SDK、开源类库,很大概率都会使用外观模式。

通常在以下情况下可以考虑使用外观模式。

  1. 解决分布式事务问题:连表多表操作,提供接口,执行两个sql,注释为事务注释

    /**     * 事务:批量入从表,同时修改主表状态为同步中     * @param policyFollows 从表数据     * @param policy 主表     */    @Override    @Transactional(rollbackFor = Exception.class)    public void insertPolicyFollowsAndUpdateParentStatus(List
    policyFollows, UpdatePolicy updatePolicy) {
    /*批量插入从表*/ scheduledTaskManager.insertUpdatePolicyFollows(policyFollows); /*设置主表状态*/ updatePolicy.setSyncStatus(Byte.valueOf(SyncStatusEnum.SYNCING.getCode())); updatePolicy.setRemark(SyncStatusEnum.SYNCING.getDesc()); updateParentStatus(updatePolicy); }
  2. 解决性能问题,将多个调用接口替换为一个门面接口,减少网络通信成本

  3. 解决易用性问题,当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问,隐藏系统的复杂性,并将其分离,提高子系统的独立性和可移植性,比如linux的复杂内核和shell命令

五、优缺点

外观(Facade)模式是“迪米特法则”的典型应用,它有以下主要优点

  1. 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
  2. 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
  3. 降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。

缺点:

​ 增加新的子系统可能需要修改外观类或客户端的源代码,在一定程度上违背了“开闭原则”

外观模式拓展:引入抽象外观类,则在一定程度上解决“开闭原则”问题

拓展结构
在这里插入图片描述

六、与适配器的区别

两者共同点是都将不好用的接口适配成好用的接口。

不同点:

适配器是做接口转换,解决的是原接口和目标接口不匹配的问题。

门面模式做接口整合,解决的是多接口调用带来的问题

转载地址:http://ytgzi.baihongyu.com/

你可能感兴趣的文章
DB2 CHNGPGS_THRES 参数
查看>>
DB2安全性概述
查看>>
DB2 用户管理
查看>>
DB2 脚本
查看>>
DB2 锁升级失败将引起死锁
查看>>
遇到问题该如何解决
查看>>
[第21课] 二项分布3
查看>>
[第22课] 二项分布4
查看>>
Pandas 筛选数据
查看>>
Pandas 复合索引
查看>>
[第23课] 期望值E(X)
查看>>
[第24课] 二项分布的期望值
查看>>
Pandas 处理 NaN
查看>>
Pandas 分组统计
查看>>
Pandas 多 DataFrame联接
查看>>
Sybase 系列文章目录
查看>>
SQLServer
查看>>
Hibernate 通过 Hibernate 访问数据库
查看>>
java面试题
查看>>
消息队列相关(MQ)
查看>>