文章目录

  • 结构型模式(中)
    • 一、适配器模式
      • 1.1 定义
      • 1.2 优点和使用场景
      • 1.3 实现
        • 1.3.1 目标接口(Target)
        • 1.3.2 适配者角色(Adaptee)
        • 1.3.3 适配器角色(Adapter)
        • 1.3.4 测试
    • 二、装饰器模式
      • 2.1 定义
      • 2.2 优点和使用场景
      • 2.3 实现
        • 2.3.1 接口
        • 2.3.2 实现类
        • 2.3.3 装饰器类
        • 2.3.4 测试
    • 三、小结
      • 3.1 装饰器和代理
      • 3.2 适配器和代理

结构型模式(中)

  • 结构型模式共7种,如下所示,本文介绍适配器模式和装饰器模式。
  • 代理模式
  • 适配器模式
  • 装饰器模式
  • 外观模式
  • 桥接模式
  • 组合模式
  • 享元模式

一、适配器模式

1.1 定义

  • 适配器模式用于2个不兼容之间的接口的桥梁,尤其是涉及到不同的对接的时候,将一个接口转换为客户端锁期望的接口。比如我们期望的接口是T,那么
    我们目前有的接口可能是X,可能是Y,未来还可能是Z等等,为了保持良好的扩展性,我们提供一个适配器xt来将X转换为目标T接口,使用适配器yt将Y接口转换
    为目标T接口,比较典型的是支付接口,项目内的支付接口定义为MyPay,但是需要调用不同的第三方支付接口,支付宝,微信,以后可能还有银联,甚至其他接口。
    使用适配器模式,未来扩展的时候,只需要添加新的适配器就行了。

1.2 优点和使用场景

  • 优点:便于扩展
  • 缺点:增加了一定的复杂性
  • 场景:双方接口不便修改时,通过适配器适配接口,比如接入第三方组件

1.3 实现

  • 如果目标接口是T(Target),已有的适配者角色接口是X(Adaptee),适配者是XT(Adapter),那么XT需要实现T接口,因为它对外要提供给客户端使用,客户端期望的是T接口,
    另一方面XT内部要持有适配者(Adaptee)实例,因为它真正的还是要去调用Adaptee的接口

  • 类关系图:

1.3.1 目标接口(Target)

  • 目标接口是我的一个支付接口;
/*** 适配器中的目标接口,客户端期望得到的接口*/
public interface MyPay {void pay(int money);
}

1.3.2 适配者角色(Adaptee)

  • 适配者角色,被适配的接口,比如第三方接口;这里代表2个第三方支付接口,为了简便没有使用接口,直接使用类,如果是接口的话,思想是一样的,但是稍微复杂点
public class AliPay {public void aliPay(int money) {System.out.println("aliPay pay...  " + money);}
}public class WeChatPay {public void weChatPay(int money) {System.out.println("weChatPay pay...  "+ money);}}

1.3.3 适配器角色(Adapter)

  • Adapter实现目标接口,因为它对外要提供给客户端使用
  • Adapter内部持有Adaptee实例,因为它真正的还是要去调用Adaptee的接口
public class AliPayAdapter extends AliPay implements MyPay {private AliPay aliPay;public AliPayAdapter() {this.aliPay = new AliPay();}@Overridepublic void pay(int money) {System.out.println("调用第三方支付接口...");aliPay.aliPay(money);}
}public class WeChatPayAdapter extends WeChatPay implements MyPay {private WeChatPay weChatPay;public WeChatPayAdapter() {this.weChatPay = new WeChatPay();}@Overridepublic void pay(int money) {System.out.println("调用第三方支付接口...");weChatPay.weChatPay(money);}
}

1.3.4 测试

public class AdapterTest {public static void main(String[] args) {MyPay myPay = new AliPayAdapter();myPay.pay(100);MyPay myPay1 = new WeChatPayAdapter();myPay1.pay(100);}
}
  • 输出:
调用第三方支付接口...
aliPay pay...  100
调用第三方支付接口...
weChatPay pay...  100
  • 示例中我们通过调用自己的目标接口,成功得调用到了不同的第三方接口,而且如果有第三种支付方式,可以再扩展一个适配器角色即可,扩展会很方便。

二、装饰器模式

2.1 定义

  • 装饰器模式用于给类添加特定的功能。给类添加特定的功能实现方式包括继承和装饰,继承是静态的,不便于用户自己控制方式和时机,而且如果新功
    能存在多种组合,继承将使得类变得冗余臃肿,无法维护,但是装饰器模式会很灵活,可以按照自己的意愿添加新功能。

2.2 优点和使用场景

  • 优点:灵活,且添加的功能可以任意组合。
  • 场景:用于给核心类添加附加功能的场景。比如MyBatis中的缓存模块,使用装饰器模式给缓存添加了很多附加功能,比如添加FIFO功能,阻塞功
    能,日志功能,LRU功能等,或者JDK中的IO流。

2.3 实现

  • 下面场景是:有一个游戏,普通玩家只能有 登陆->玩游戏->退出这3个核心功能。在此基础上,增加了Vip玩家,Vip玩家登陆后会增加积分,在线时长达标后也会再次追加积分。
    另外有黑钻玩家,黑钻玩家玩DNF的时候经验翻倍,还有绿钻玩家,绿钻玩家玩QQ音乐的时候可以免费听歌,后面三种玩家都是在核心功能上添加了一定的附加功能,而且用户可
    能有组合,比如黑钻Vip,那么登陆要加积分,玩DNF还要经验加倍,我们来看如何实现。

2.3.1 接口

  • 接口,定义的是核心功能接口
public interface Player {void login();void play(String gameName);void loginOut();
}

2.3.2 实现类

  • 实现类实现了基本的主体功能
public class NormalPlayer implements Player {@Overridepublic void login() {System.out.println("登陆...");}@Overridepublic void play(String gameName) {System.out.println("玩: " + gameName);}@Overridepublic void loginOut() {System.out.println("退出...");}
}

2.3.3 装饰器类

  • 根据需要添加的功能,给基本实现类添对应的功能
  • Vip用户,登陆送积分,在线时长达标后追加送积分
public class VipPlayer implements Player {private final Player delegate;public VipPlayer(Player delegate) {this.delegate = delegate;}private long loginTime;@Overridepublic void login() {delegate.login();loginTime = System.currentTimeMillis();System.out.println("VipPlayer 登陆后增加5个积分...");}@Overridepublic void play(String gameName) {delegate.play(gameName);}@Overridepublic void loginOut() {if (System.currentTimeMillis() - loginTime > 200) {System.out.println("VipPlayer 在线时长达标,赠送5个积分...");}delegate.loginOut();}
}
  • 黑钻用户,玩DNF经验翻倍
public class BlackDiamondPlayer implements Player {private final Player delegate;public BlackDiamondPlayer(Player delegate) {this.delegate = delegate;}private long loginTime;@Overridepublic void login() {delegate.login();}@Overridepublic void play(String gameName) {delegate.play(gameName);if ("DNF".equals(gameName)) {System.out.println("玩游戏经验值翻倍....");}}@Overridepublic void loginOut() {delegate.loginOut();}
}
  • 蓝钻用户,玩QQ音乐免费听歌
public class BlueDiamondPlayer implements Player {private final Player delegate;public BlueDiamondPlayer(Player delegate) {this.delegate = delegate;}private long loginTime;@Overridepublic void login() {delegate.login();}@Overridepublic void play(String gameName) {delegate.play(gameName);if ("QQMusic".equals(gameName)) {System.out.println("玩QQ音乐免费听歌....");}}@Overridepublic void loginOut() {delegate.loginOut();}
}

2.3.4 测试

  • 测试不同类型的用户
public class DecratorTest {public static void main(String[] args) throws InterruptedException {System.out.println("普通玩家:");Player player = new NormalPlayer();player.login();player.play("英雄联盟");player.loginOut();System.out.println("\n" + "Vip玩家:");Player vipPlayer = new VipPlayer(player);vipPlayer.login();vipPlayer.play("DNF");Thread.sleep(300);vipPlayer.loginOut();System.out.println("\n" + "蓝钻玩家:");Player blueDiamondPlayer = new BlueDiamondPlayer(player);blueDiamondPlayer.login();blueDiamondPlayer.play("QQMusic");blueDiamondPlayer.loginOut();System.out.println("\n" + "黑钻玩家:");Player blackDiamondPlayer = new BlackDiamondPlayer(player);blackDiamondPlayer.login();blackDiamondPlayer.play("DNF");blackDiamondPlayer.loginOut();System.out.println("\n" + "黑钻Vip玩家:");Player blackVip = new BlackDiamondPlayer(new VipPlayer(new NormalPlayer()));blackVip.login();blackVip.play("DNF");Thread.sleep(300);blackVip.loginOut();System.out.println("\n" + "超级玩家:");Player superPlayer = new BlueDiamondPlayer(new BlackDiamondPlayer(new VipPlayer(new NormalPlayer())));superPlayer.login();superPlayer.play("DNF");superPlayer.play("QQMusic");Thread.sleep(300);superPlayer.loginOut();}
}
  • 输出:
普通玩家:
登陆...
玩: 英雄联盟
退出...Vip玩家:
登陆...
VipPlayer 登陆后增加5个积分...
玩: DNF
VipPlayer 在线时长达标,赠送5个积分...
退出...蓝钻玩家:
登陆...
玩: QQMusic
玩QQ音乐免费听歌....
退出...黑钻玩家:
登陆...
玩: DNF
玩游戏经验值翻倍....
退出...黑钻Vip玩家:
登陆...
VipPlayer 登陆后增加5个积分...
玩: DNF
玩游戏经验值翻倍....
VipPlayer 在线时长达标,赠送5个积分...
退出...超级玩家:
登陆...
VipPlayer 登陆后增加5个积分...
玩: DNF
玩游戏经验值翻倍....
玩: QQMusic
玩QQ音乐免费听歌....
VipPlayer 在线时长达标,赠送5个积分...
退出...
  • 我们看到,各种不同的用户类型的输出都符合我们的预期,由其是组合的类型,后面两种组合类型,我们看最后一种用户的构造方式,有么有觉得很熟悉,JDK中的IO流就是典型的装饰器模式的应用。由此
    我们看到,通过装饰器模式,我给主要业务添加附加业务的时候很方便,而且可以任意组合搭配。
new BlueDiamondPlayer(new BlackDiamondPlayer(new VipPlayer(new NormalPlayer())));
new BufferedReader(new FileReader(new File("test.txt")));

三、小结

3.1 装饰器和代理

  • 从上面的例子我们可以看到,装饰器模式的扩展性很强,比如你有ABCD四个附加功能,使用装饰器可以很轻松的添加到主要功能上去,还可以组合搭配,使用代理就不方便,代理模式主要是对某个类增强
    一些功能,一般是前后拦截,具体阅读[13-Mybatis源码和设计模式-4(缓存模块和装饰器模式)] 到时候会有深刻体验。

3.2 适配器和代理

  • 前者主要是用于接口对接,第三方接口的对接,比如双方的接口都不好改变,但是对于的功能调用需要对接,就引入一个适配器,而且扩展也很方便。后者核心还在在代理增强。

  • 关于适配器模式,这里只是简单的给出了适配器模式的使用示例,在MyBatis源码的日志模块中有适配器的经典使用,可以阅读 [11-Mybatis源码和设计模式-2(日志模块和适配器模式)]
  • 关于装饰器模式,这里只是简单的给出了装饰器模式的使用示例,在MyBatis源码的缓存模块中有装饰器的经典使用,可以阅读 [13-Mybatis源码和设计模式-4(缓存模块和装饰器模式)]

07-结构型模式(中)相关推荐

  1. java面试题31:结构型模式中最体现扩展性的模式是()

    java面试题31:结构型模式中最体现扩展性的模式是() A:装饰模式 B:合成模式 C:桥接模式 D:适配器 蒙蔽树上蒙蔽果,蒙蔽树下你和我 结构型模式是描述如何将类对象结合在一起,形成一个更大的结 ...

  2. 结构型模式中最能体现扩展性模式的是?

    设计模式分为三大类: 创建型设计模式:单例模式,工厂方法模式,简单工厂模式,建造者模式. 原型模式 结构型设计模式:适配器模式,***模式,AOP.装饰器模式. 行为型设计模式:观察者模.板方法模式 ...

  3. Java23种设计模式——11.结构型模式之享元模式

    Java中除去有设计原则之外,还有23中设计模式. 这些模式都是前辈们一点一点积累下来,一直在改进,一直在优化的,而这些设计模式可以解决一些特定的问题. 并且在这些模式中,可以说是将语言的使用体现的淋 ...

  4. 设计模式——结构型模式之代理模式和适配器模式(类比+图解,从无到有,一文看懂几种模式的区别)

    设计模式 系列文章: 一.创建型模式--工厂模式 二.创建型模式--单例模式.原型模式 三.创建型模式--建造者模式 四.结构型模式--装饰者模式 五.结构型模式--代理模式.适配器模式 文章目录 设 ...

  5. Java中的23个设计模式 【结构型模式】 中的 【享元模式】

    文章目录 设计模式分类 享元模式(FlyWeight) 介绍 案例--围棋软件设计 享元模式实现 开发中的应用场景 优缺点 优点 缺点 代码 抽象享元类 具体享元类,设置内部状态 非共享享元类,设置外 ...

  6. 设计模式初识(三)结构型模式(Structural Pattern)

    为什么要使用结构型模式 结构型模式关注点在于"如何组合对象/类",更关注类之间的组合关系: 类结构型模式关心类的组合,由多个类可以组合成一个更大的(继承): 对象结构型模式关心类与 ...

  7. 【设计模式】三大类:创建型模式、结构型模式、行为型模式

    1 创建型模式 本人理解:以C++为例,创建对象时,用关键字new来创建(实例化)对象.用一个函数或类将new的过程封装起来,就是创建型模式. <设计模式>中的描述: 创建型模式抽象了实例 ...

  8. DesignPattern(四)结构型模式(下)

    上篇链接   https://www.cnblogs.com/qixinbo/p/9023764.html 继续介绍最后三种结构型模式 外观模式 外观模式,也称作 "门面"模式,在 ...

  9. 8、设计模式-结构型模式-适配器模式

    适配器模式 在软件开发中,有时也存在类似这种不兼容的情况 引入一个称之为适配器的角色来协调这些存在不兼容的结构 这种设计方案即为适配器模式 在适配器模式中引入了一个被称为适配器(Adapter)的包装 ...

最新文章

  1. 当对象或对象属性为空时,如何安全给对象或对象属性添加默认值
  2. 前台开发之HTML定义语义化
  3. 【django】 F 和 Q 对象
  4. python如何测试仪器_如何测试pytest设备本身?
  5. 在没有插件的情况下为Chrome设置Proxy
  6. python调用canape_基于CCP协议利用CANape进行电控单元标定
  7. android post请求时报415错误,post请求传递JSON数据类型(415错误解决)
  8. 第十五周项目2 - 大数据集上排序算法性能的体验
  9. 使用wps将excel中的行转列(转置)
  10. 知行合一 中国古代小说鉴赏 大作业
  11. 计算机远程控制安全吗,远程控制电脑安全吗?
  12. Notelife for Mac(笔记管理工具)
  13. matlab中clc、clear、clear all、clf、close、close all命令
  14. 【PAT】PAT那些破事
  15. 安装redhat系统步骤图解_RedHat Linux系统U盘安装图文教程 | Visant-i 行客
  16. lavarvel框架路由
  17. VC++ 在任务栏图标上显示进度条效果
  18. 清除 WindowsOffice KMS激活
  19. 计算机里面不显示光驱位硬盘,悲剧了!win10下新加的光驱位机械硬盘无法识别...
  20. 实验一 视频/图像信号处理实验

热门文章

  1. 华为S2700 交换机详细配置
  2. 服务器维护 测试化验加工费,你问我答测试化验加工费=外协费?这个理解有问题...
  3. nodejs 在Win10下面的环境变量的配置
  4. 小白自学笔记——JAVA基础 2.2变量
  5. 站在三岔路口的全面屏手机,最终会走向何方?
  6. 保研清华的计算机专业的情侣,学霸情侣双双保研清华令人羡慕,但是你知道保研是怎么回事吗?...
  7. SSM毕设项目摄影工作室管理0cp91(java+VUE+Mybatis+Maven+Mysql)
  8. 易语言添加ctrl c键,易语言怎样设置监视的热键为ctrl+1键?
  9. 邮件助手、监控邮件上报电脑截图、网课监控助手
  10. 小程序input提交后如何清空输入框数据:小程序与Vue的数据绑定方式