今天开始咱们专题的第八课了。本章节将介绍:三个设计模式,适配器模式、装饰者模式和观察者模式。经过学习适配器模式,能够优雅的解决代码功能的兼容问题。另外有重构需求的人群必定须要掌握装饰者模式。本章节参考资料书籍《Spring 5核心原理》中的第一篇 Spring 内功心法(Spring中经常使用的设计模式)(没有电子档,都是我取其精华并结合本身的理解,一个字一个字手敲出来的,若是以为本文对你有用,请点个推荐)。java
适配器模式(Adapter Pattern)是指将一个类的接口转换成客户指望的另外一个接口,使本来的接口不兼容的类能够一块儿工做,属于结构型设计模式。
适配器适用于如下几种业务场景:
一、已经存在的类,它的方法和需求不匹配(方法结果相同或类似)的状况。
二、适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,因为不一样产品、不一样厂家形成功能相似而接口不相同状况下的解决方案。有点亡羊补牢的感受。生活中也很是的应用场景,例如电源插转换头、手机充电转换头、显示器转接头。json
在中国民用电都是 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电源的接口:app
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; } @Override public int output5V() { int outputAC220V = ac220.outputAC220V(); System.out.println("将220v转换成5v"); return outputAC220V/44; } }
测试代码:ide
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(); } }
输出结果:post
从上面的代码样式能够看出,适配器就是经过增长一个适配器类持有原有提供者的对象,实现了两者的兼容。学习
一、能提升类的透明性和复用,现有的类复用但不须要改变。测试
二、目标类和适配器类解耦,提升程序的扩展性。this
三、在不少业务场景中符合开闭原则。
一、适配器编写过程须要全面考虑,可能会增长系统的复杂性。
二、增长代码阅读难度,下降代码可读性,过多使用适配器会使系统代码变得凌乱。
装饰者模式(Decorator Pattern)是指在不改变原有对象的基础之上,将功能附加到对 象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。 装饰者模式在咱们生活中应用也比较多如给煎饼加鸡蛋;给蛋糕加上一些水果;给房子 装修等,为对象扩展一些额外的职责。装饰者在代码程序中适用于如下场景:
一、用于扩展一个类的功能或给一个类添加附加职责。
二、动态的给一个对象添加功能,这些功能能够再动态的撤销。
来看一个这样的场景,上班族白领其实大多有睡懒觉的习惯,天天早上上班都是踩点,因而不少小伙伴为了多赖一下子床都不吃早餐。那么,也有些小伙伴可能在上班路上碰 到卖煎饼的路边摊,都会顺带一个到公司茶水间吃早餐。卖煎饼的大姐能够给你的煎饼 加鸡蛋,也能够加香肠。
建立煎饼类:
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{ @Override public String getMsg() { return super.getMsg() + "加鸡蛋"; } @Override public 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{ @Override public String getMsg() { return super.getMsg() +"加烤肠"; } @Override public 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{ @Override protected String getMsg() { return "煎饼"; } @Override protected 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(); @Override protected String getMsg() { return this.batterCake.getMsg(); } @Override protected 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); } @Override protected void doSomething() { } @Override public String getMsg() { return super.getMsg() + "加鸡蛋"; } @Override public 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); } @Override protected void doSomething() { } @Override public String getMsg() { return super.getMsg() +"加烤肠"; } @Override public 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()); } }
输出结果:
装饰者模式最本质的特征是讲原有类的附加功能抽离出来,简化原有类的逻辑。经过这样两个案例,咱们能够总结出来,其实抽象的装饰者是无关紧要的,具体能够根据业务模型来选择。
装饰者和适配器模式都是包装模式(Wrapper Pattern),装饰者也是一种特殊的代理模式。
装饰者模式 适配器模式 形式 是一种很是特别的适配器模式 没有层级关系, 装饰器模式有层级关系 定义 装饰者和被装饰者都实现同一个接口, 适配器和被适配者没有必然的联系,通 常是采用继承或代理的形式进行包装 主要目的是为了扩展以后依旧保 留 OOP 关系 关系 知足 is-a 的关系 知足 has-a 的关系 功能 注重覆盖、扩展 注重兼容、转换 设计 前置考虑 后置考虑
一、装饰者是继承的有力补充,比继承灵活,不改变原有对象的状况下动态地给一个对象扩展功能,即插即用。
二、经过使用不一样装饰类以及这些装饰类的排列组合,能够实现不一样效果。
三、装饰者彻底遵照开闭原则。
一、会出现更多的代码,更多的类,增长程序复杂性。
二、动态装饰时,多层装饰时会更复杂。
观察者模式(Observer Pattern)定义了对象之间的一对多依赖,让多个观察者对象同 时监听一个主体对象,当主体对象发生变化时,它的全部依赖者(观察者)都会收到通 知并更新,属于行为型模式。观察者模式有时也叫作发布订阅模式。观察者模式主要用 于在关联行为之间创建一套触发机制的场景。
小伙伴们确定都刷过抖音,遇到喜欢的做品都会点个❥喜欢,咱们经过模拟喜欢这个事件来实践下观察者模式。当你点击喜欢时,会触发两个事件,一个喜欢数量会增长+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不多被用来写桌面程序。以上就是我模拟的事件机制。
一、观察者和被观察者之间创建了一个抽象的耦合。
二、观察者模式支持广播通讯。
一、观察者之间有过多的细节依赖、提升时间消耗及程序的复杂度。 二、使用要得当,要避免循环调用。