本章节将介绍:三个设计模式,适配器模式、装饰者模式和观察者模式。通过学习适配器模式,可以优雅的解决代码功能的兼容问题。另外有重构需求的人群一定需要掌握装饰者模式。本章节参考资料书籍《Spring 5核心原理》中的第一篇 Spring 内功心法(Spring中常用的设计模式)(没有电子档,都是我取其精华并结合自己的理解,一个字一个字手敲出来的,如果觉得本文对你有用,请点个推荐)。

2.适配器模式

2.1适配器模式的应用场景

​ 适配器模式(Adapter Pattern)是指将一个类的接口转换成客户期望的另一个接口,使原本的接口不兼容的类可以一起工作,属于结构型设计模式。
适配器适用于以下几种业务场景:
​ 1、已经存在的类,它的方法和需求不匹配(方法结果相同或相似)的情况。
​ 2、适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案。有点亡羊补牢的感觉。生活中也非常的应用场景,例如电源插转换头、手机充电转换头、显示器转接头。

在中国民用电都是 220V 交流电,但我们手机使用的锂电池使用的 5V 直流电。因此,我 们给手机充电时就需要使用电源适配器来进行转换。下面我们有代码来还原这个生活场 景,创建 AC220 类,表示 220V 交流电

package com.study;/*** @author wangzhongyuan*/
public class AC220 {public int outputAC220V(){int output = 220;System.out.println("提供220V交流电");return output;}
}

创建5V电源的接口:

package com.study;/*** @author wangzhongyuan*/
public interface DC5 {int output5V();
}

创建提供5V电源的适配器:

package com.study;/*** @author wangzhongyuan*/
public class PowerAdapter implements DC5{AC220 ac220;public PowerAdapter(AC220 ac220) {this.ac220 = ac220;}@Overridepublic int output5V() {int outputAC220V = ac220.outputAC220V();System.out.println("将220v转换成5v");return outputAC220V/44;}
}

测试代码:

package com.study;/*** @author wang.zhongyuan* @version 1.0* @date 2020/7/20 11:17 下午*/
public class DemoTest {public static void main(String[] args) {DC5 powerAdapter = new PowerAdapter(new AC220());powerAdapter.output5V();}
}

输出结果:

从上面的代码样式可以看出,适配器就是通过增加一个适配器类持有原有提供者的对象,实现了二者的兼容。

2.2适配模式的优缺点

优点:

1、能提高类的透明性和复用,现有的类复用但不需要改变。

2、目标类和适配器类解耦,提高程序的扩展性。

3、在很多业务场景中符合开闭原则。

缺点:

1、适配器编写过程需要全面考虑,可能会增加系统的复杂性。

2、增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。

3.装饰者模式

3.1装饰者模式的应用场景

​ 装饰者模式(Decorator Pattern)是指在不改变原有对象的基础之上,将功能附加到对 象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。 装饰者模式在我们生活中应用也比较多如给煎饼加鸡蛋;给蛋糕加上一些水果;给房子 装修等,为对象扩展一些额外的职责。装饰者在代码程序中适用于以下场景:

1、用于扩展一个类的功能或给一个类添加附加职责。

2、动态的给一个对象添加功能,这些功能可以再动态的撤销。

3.2代码实现

来看一个这样的场景,上班族白领其实大多有睡懒觉的习惯,每天早上上班都是踩点,于是很多小伙伴为了多赖一会儿床都不吃早餐。那么,也有些小伙伴可能在上班路上碰 到卖煎饼的路边摊,都会顺带一个到公司茶水间吃早餐。卖煎饼的大姐可以给你的煎饼 加鸡蛋,也可以加香肠。

创建煎饼类:

package com.study;/*** @author wang.zhongyuan* @version 1.0* @date 2020/7/20 11:57 下午*/
public class Battercake {public String getMsg(){return "煎饼";}public int getPrice(){return 5;}
}

给煎饼加个蛋:

package com.study;/*** @author wang.zhongyuan* @version 1.0* @date 2020/7/20 11:59 下午*/
public class BattercakeWithEgg extends Battercake{@Overridepublic String getMsg() {return super.getMsg() + "加鸡蛋";}@Overridepublic int getPrice() {return super.getPrice() + 1;}
}

再加个烤肠:

package com.study;/*** @author wang.zhongyuan* @version 1.0* @date 2020/7/21 12:00 上午*/
public class BatterCakeWithEggAndSausage extends BattercakeWithEgg{@Overridepublic String getMsg() {return super.getMsg() +"加烤肠";}@Overridepublic int getPrice() {return super.getPrice() + 3;}
}

测试代码:

package com.study;/*** @author wang.zhongyuan* @version 1.0* @date 2020/7/21 12:01 上午*/
public class DemoTest {public static void main(String[] args) {BatterCakeWithEggAndSausage batterCakeWithEggAndSausage = new BatterCakeWithEggAndSausage();String msg = batterCakeWithEggAndSausage.getMsg();int price = batterCakeWithEggAndSausage.getPrice();System.out.println("买了:"+msg+",价格为:"+price);}
}

输出结果:

运行结果没有问题,但是如果用户需要一个加 2 个鸡蛋加 1 根香肠的煎饼,那么用我们现在的类结构是创建不出来的,也无法自动计算出价格,除非再创建一个类做定制。 如果需求再变,一直加定制显然是不科学的。那么下面我们就用装饰者模式来解决上面 的问题。

创建煎饼抽象类:

package com.study;/*** @author wang.zhongyuan* @version 1.0* @date 2020/7/21 12:18 上午*/
public abstract class AbstractBatterCake {protected abstract String getMsg();protected abstract int getPrice();
}

创建基础套餐煎饼:

package com.study;/*** @author wang.zhongyuan* @version 1.0* @date 2020/7/21 12:19 上午*/
public class BatterCakeWithBase extends AbstractBatterCake{@Overrideprotected String getMsg() {return "煎饼";}@Overrideprotected int getPrice() {return 5;}
}

创建额外套餐的抽象装饰类:

package com.study;/*** @author wang.zhongyuan* @version 1.0* @date 2020/7/21 12:21 上午*/
public abstract class BatterCakeDecorator extends AbstractBatterCake{AbstractBatterCake batterCake;public BatterCakeDecorator(AbstractBatterCake batterCake) {this.batterCake = batterCake;}protected abstract void doSomething();@Overrideprotected String getMsg() {return this.batterCake.getMsg();}@Overrideprotected int getPrice() {return this.batterCake.getPrice();}
}

创建加鸡蛋套餐:

package com.study;/*** @author wang.zhongyuan* @version 1.0* @date 2020/7/20 11:59 下午*/
public class BattercakeWithEgg extends BatterCakeDecorator{public BattercakeWithEgg(AbstractBatterCake batterCake) {super(batterCake);}@Overrideprotected void doSomething() {}@Overridepublic String getMsg() {return super.getMsg() + "加鸡蛋";}@Overridepublic int getPrice() {return super.getPrice() + 1;}
}

创建加烤肠套餐:

package com.study;/*** @author wang.zhongyuan* @version 1.0* @date 2020/7/21 12:00 上午*/
public class BatterCakeWithSausage extends BatterCakeDecorator{public BatterCakeWithSausage(AbstractBatterCake batterCake) {super(batterCake);}@Overrideprotected void doSomething() {}@Overridepublic String getMsg() {return super.getMsg() +"加烤肠";}@Overridepublic int getPrice() {return super.getPrice() + 3;}
}

测试代码:

package com.study;/*** @author wang.zhongyuan* @version 1.0* @date 2020/7/21 12:01 上午*/
public class DemoTest {public static void main(String[] args) {AbstractBatterCake batterCake;batterCake = new BatterCakeWithBase();batterCake = new BattercakeWithEgg(batterCake);batterCake = new BatterCakeWithSausage(batterCake);//再加个鸡蛋batterCake = new BattercakeWithEgg(batterCake);System.out.println(batterCake.getMsg()+",价格:"+batterCake.getPrice());}
}

输出结果:

装饰者模式最本质的特征是讲原有类的附加功能抽离出来,简化原有类的逻辑。通过这样两个案例,我们可以总结出来,其实抽象的装饰者是可有可无的,具体可以根据业务模型来选择。

3.3装饰者模式与适配器模式对比

装饰者和适配器模式都是包装模式(Wrapper Pattern),装饰者也是一种特殊的代理模式。

     装饰者模式                                                           适配器模式
形式 是一种非常特别的适配器模式 没有层级关系,     装饰器模式有层级关系
定义 装饰者和被装饰者都实现同一个接口,                 适配器和被适配者没有必然的联系,通 常是采用继承或代理的形式进行包装 主要目的是为了扩展之后依旧保 留 OOP 关系
关系 满足 is-a 的关系                                               满足 has-a 的关系
功能 注重覆盖、扩展                                                      注重兼容、转换
设计 前置考虑                                                                 后置考虑

3.4装饰模式的优缺点:

优点:

1、装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用。

2、通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。

3、装饰者完全遵守开闭原则。

缺点:

1、会出现更多的代码,更多的类,增加程序复杂性。

2、动态装饰时,多层装饰时会更复杂。

4.观察者模式

4.1观察者模式的应用场景

​ 观察者模式(Observer Pattern)定义了对象之间的一对多依赖,让多个观察者对象同 时监听一个主体对象,当主体对象发生变化时,它的所有依赖者(观察者)都会收到通 知并更新,属于行为型模式。观察者模式有时也叫做发布订阅模式。观察者模式主要用 于在关联行为之间建立一套触发机制的场景。

4.2代码实现

​ 小伙伴们肯定都刷过抖音,遇到喜欢的作品都会点个❥喜欢,我们通过模拟喜欢这个事件来实践下观察者模式。当你点击喜欢时,会触发两个事件,一个喜欢数量会增加+1,二是作者会受到消息。你可能会想到MQ,异步队列等,其实JDK本身就提供这样的API。

创建事件模型类,用于区分什么样的事件,观察者可以根据不同事件类型做不同处理:

package com.study.demo4;/*** 事件模型* @Author wangzhongyuan* @Date 2020/7/21 11:28* @Version 1.0**/
public enum EventModel {LIKE_EVENT("喜欢事件",1,null),MCOMENT_ENVET("评论事件",2,null);private String message;private int type;private Object date;EventModel(String message, int type, Object date) {this.message = message;this.type = type;this.date = date;}}

创建事件类,被观察者对象,调用方可以向该对象中传送事件:

package com.study.demo4;import java.util.Observable;/*** 事件总线(被观察者)* @Author wangzhongyuan* @Date 2020/7/21 11:24* @Version 1.0**/
public class EventBus extends Observable{/*** 被观察者方法* @param eventModel*/public void postEvent(EventModel eventModel){System.out.println("推送事件");setChanged();//通知观察者notifyObservers(eventModel);}
}

创建不同业务的观察者:

package com.study.demo4;import java.util.Observable;
import java.util.Observer;/*** 观察者1** @Author 19054253* @Date 2020/7/21 11:32* @Version 1.0**/
public class OneObserver implements Observer {public void update(Observable o, Object arg) {System.out.println("观察1,监听到了事件,触发:给作者推送消息!");}
}
package com.study.demo4;import java.util.Observable;
import java.util.Observer;/*** 观察者2** @Author 19054253* @Date 2020/7/21 11:48* @Version 1.0**/
public class TwoObserver implements Observer {public void update(Observable o, Object arg) {System.out.println("观察2,监听到了事件,触发:喜欢总数+1");}
}

调用测试:

package com.study.demo4;/*** @Author wangzhongyuan* @Date 2020/7/21 11:35* @Version 1.0**/
public class DemoTest {public static void main(String[] args) {//被观察者EventBus eventBus = new EventBus();//观察者OneObserver oneObserver = new OneObserver();TwoObserver twoObserver = new TwoObserver();//监听观察者eventBus.addObserver(oneObserver);eventBus.addObserver(twoObserver);//被观察者触发事件eventBus.postEvent(EventModel.LIKE_EVENT);}
}

输出结果:

从上面代码可以看出来,观察者模式的本质就是,被观察者对象持有观察者对象的引用,由被观察者去通知了观察者去做了某件事。JDK源码中,观察者模式也应用非常多。例如java.awt.Event就是观察者模式的一种,只不过Java很少被用来写桌面程序。以上就是我模拟的事件机制。

4.3观察模式的优缺点

优点:

1、观察者和被观察者之间建立了一个抽象的耦合。
2、观察者模式支持广播通信。

缺点:

1、观察者之间有过多的细节依赖、提高时间消耗及程序的复杂度。
2、使用要得当,要避免循环调用。

往期专题:

大型Java进阶专题(一) 软件架构设计原则(上)

大型Java进阶专题(三) 软件架构设计原则(下)

大型Java进阶专题(四) 设计模式之工厂模式

大型Java进阶专题(五) 设计模式之单例模式

Java进阶专题(六) 设计模式之代理模式

Java进阶专题(七) 设计模式之委派模式与策略模式

Java进阶专题(八) 设计模式之适配器模式、装饰者模式、观察者模式相关推荐

  1. Java进阶专题(七) 设计模式之委派模式与策略模式

    一.前言 今天开始我们专题的第七课了.本章节将介绍:你写的代码中是否觉得很臃肿,程序中有大量的if...else,想优化代码,精简程序逻辑,提升代码的可读性,这章节将介绍如何通过委派模式.策略模式让你 ...

  2. 设计模式学习笔记——装饰(Decorator)模式

    设计模式学习笔记--装饰(Decorator)模式 @(设计模式)[设计模式, 装饰模式, decorator] 设计模式学习笔记装饰Decorator模式 基本介绍 装饰案例 类图 实现代码 Dis ...

  3. 设计模式之【装饰器模式】

    和表妹去喝奶茶 表妹:哥啊,我想喝奶茶. 我:走啊,去哪里喝? 表妹:走,我带你去,我经常去的那家,不但好喝,还可以自由搭配很多小料.我每次都是不同的搭配,换着喝,嘻嘻. 我:你倒是挺会喝的嘛~ 你看 ...

  4. 设计模式之适配器模式、委派模式、访问者模式、工厂模式、桥接模式(双维度扩展)

    设计模式之适配器模式.委派模式.访问者模式.工厂模式.观察者-发布订阅模式 设计模式分类: 适配器模式(Adapter Pattern) 定义 使用场景 代码实现 写法一:类适配器 写法二:对象适配器 ...

  5. Java进阶 23种设计模式 详解+应用+实例代码

    文章目录 前言 设计模式六大原则 1.单一原则 (1) 概念 (2) 优点 2.开闭原则 (1) 概念 3.里氏替换原则 (1) 概念 4.依赖倒置原则 (1) 概念 (2) 作用 5.接口隔离原则 ...

  6. Java设计模式12:装饰器模式

    装饰器模式 装饰器模式又称为包装(Wrapper)模式.装饰器模式以多客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰器模式的结构 通常给对象添加功能,要么直接修改对象添加相应的功能, ...

  7. java中装饰器_Java设计模式12:装饰器模式

    装饰器模式 装饰器模式又称为包装(Wrapper)模式.装饰器模式以多客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰器模式的结构 通常给对象添加功能,要么直接修改对象添加相应的功能, ...

  8. JAVA设计模式初探之装饰者模式

    这个模式花费了挺长时间,开始有点难理解,其实就是 定义:动态给一个对象添加一些额外的职责,就象在墙上刷油漆.使用Decorator模式相比用生成子类方式达到功能的扩充显得更为灵活. 设计初衷:通常可以 ...

  9. 通俗易通,值得收藏的 java 设计模式实战,装饰者模式 之 你不用改变,就让你的能力变强了

    文章目录 什么是装饰者模式 装饰者模式中的角色 抽象构件(Component)角色 具体构件(Concrete Component)角色 装饰(Decorator)角色 具体装饰(Concrete D ...

最新文章

  1. ISP PIPLINE (九_2) Denoise 之 time domain denoise
  2. Struts2标签库常用标签(转)
  3. 升腾威讯怎么恢复集群_PB级大规模Elasticsearch集群运维与调优实践
  4. GB2312,GBK,UTF-8的关系
  5. mySQL教程 第7章 存储过程和函数
  6. Tensorflow学习——导入数据
  7. 知识图谱的概念、应用与构建
  8. Ubuntu硬盘安装
  9. 安工大计算机学院肖维民,安工大路由器实验报告.docx
  10. 了区块链开放平台baas_区块链开放平台 BaaS 系统开发,区块链智能合约撰写服务...
  11. 第16课 火眼金睛——人脸识别
  12. 怎样在Apple Silicon M1 Mac上引导到恢复模式
  13. python-从视频中提取音频+将音频和视频合并
  14. 修改已经上线的小程序名称
  15. c++对8位灰度图进行二值化处理
  16. 计算机管理没有指定运行,如何限制电脑只运行一个软件?只打开指定软件?
  17. micro SD(TF)卡详解
  18. Centos 7 关于阿里云 epel源的使用
  19. 手机数控模拟器安卓版_数控机床模拟器手机版下载-数控机床模拟器(CNC Simulator)安卓版v1.1.4 - 比克尔下载...
  20. Jenkins 流水线 获取git 分支列表_基于Jenkins的DevOps流水线实践教程|2020全新制作|端到端研发效能提升...

热门文章

  1. Mybatis框架引入依赖
  2. 用html语言写一个个人信息表(练习使用html标签)
  3. CF716B Complete the Word
  4. 10个密信(MeSince)使用技巧,玩转邮件加密!
  5. c语言怎么用二维数组表示坐标,C语言二维数组几种常用的表示方法
  6. 数据仓库DW、ODS、DM及其区别总结
  7. 什么是土壤水势传感器
  8. 3D修复版《泰坦尼克》2012上映 纪念沉没百年
  9. 央视春晚,一副好牌,却被百度打的稀烂!
  10. 使用map全量替换html代码中图片scr的值