装饰器模式:

  装饰器模式(Decorator Pattern)(包装)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

何时使用:在不想增加很多子类的情况下扩展类。

如何解决:将具体功能职责划分,同时继承装饰者模式。

应用实例: 1、孙悟空有 72 变,当他变成"庙宇"后,他的根本还是一只猴子,但是他又有了庙宇的功能。 2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。

优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

缺点:多层装饰比较复杂。

使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。

  由于装饰者模式是一种特殊的适配器模式的变种,这里沿用适配器模式中用到的例子,及登陆业务的拓展,第三方登陆的案例进行演示。

  先定义一个service模拟线上已定的稳定到不可变的登录注册业务的接口跟实现:

public interface ISigninService {//注册方法public ResultMsg regist(String username,String password);// 登录的方法public ResultMsg login(String username,String password);
}public class SiginService implements ISigninService {/*** 注册方法* @param username* @param password* @return*/public ResultMsg regist(String username, String password){return  new ResultMsg("200","注册成功",new Member());}/*** 登录的方法* @param username* @param password* @return*/public ResultMsg login(String username,String password){return null;}
}

  其实涉及的 Menber 人员实体类及 ResultMsg 返回信息如下:

public class Member {private String username;private String password;// 唯一编号private String mid;// 具体信息private String info;//为节省篇幅,这里删除了set跟get方法
}public class ResultMsg {//返回编码private String code;// 返回信息private String msg;//返回数据private Object data;public ResultMsg(String code, String msg, Object data) {this.code = code;this.msg = msg;this.data = data;}//为节省篇幅,这里删除了set跟get方法
}

  对于适配器模式更注重的是兼容,所以采取的方案是对原有固定的业务实现类进行一个拓展继承,这样既保留了原来的业务,也拓展了新业务,适配者和被适配者没有必然的层级联系,

通常采用代理或者继承形式进行包装。

  而在装饰者模式中注重的是覆盖、扩展。比如Spring中的 IO流的包装,所采用的就是这个模式。装饰者和被装饰者都要实现同一个接口,主要目的是为了扩展,依旧保留OOP关系(同宗同源)。

  所以这里采用装饰者模式的话,需要装饰器类也去实现 ISigninService 接口,我这里先拓展一下接口实现,再定义一个装饰器类去实现新的接口:

public interface ISigninForThirdService extends ISigninService {public ResultMsg loginForQQ(String openId);public ResultMsg loginForWechat(String openId);public ResultMsg loginForTelphone(String telphone,String code);public ResultMsg loginForRegist(String username,String password);}

  装饰器类:

public class SigninForThirdService implements ISigninForThirdService {private ISigninService service;public SigninForThirdService(ISigninService service){this.service = service;}@Overridepublic ResultMsg regist(String username, String password) {return service.regist(username,password);}@Overridepublic ResultMsg login(String username, String password) {return service.login(username,password);}public ResultMsg loginForQQ(String openId){//1、openId是全局唯一,我们可以把它当做是一个用户名(加长)//2、密码默认为QQ_EMPTY//3、注册(在原有系统里面创建一个用户)//4、调用原来的登录方法return loginForRegist(openId,null);}public ResultMsg loginForWechat(String openId){return null;}public ResultMsg loginForTelphone(String telphone,String code){return null;}public ResultMsg loginForRegist(String username,String password){this.regist(username,null);return this.login(username,null);}}

  测试:运行以下代码即可以实现不影响既定业务的情况下进行业务的拓展。

public class SigginTest {public static void main(String[] args) {//原来的功能依旧对外开放,依旧保留//新的功能同样的也可以使用ISigninForThirdService signinForThirdService = new SigninForThirdService(new SiginService());signinForThirdService.loginForQQ("xxssdsd");}
}

观察者模式:

  当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。

意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。

缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

应用实例: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。3.Spring 中 Observer 模式常用的地方是 Listener 的实现。如 ApplicationListener。

使用场景:

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
  • 一个对象必须通知其他对象,而并不知道这些对象是谁。
  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

  当一个用户下单完支付成功之后,客服MM就应当通知仓库发货,姑且把客服MM当成一个观察者(Observer)的角色,就利用这么一个场景去做一个演示,基于这个案例,我们需要一个监听器类(EventLisenter),可以提供注册事件及触发事件的方法,还需要一个事件类(Event)。然后需要有一个事件源类,即由谁去触发这个支付的动作继而触发观察者事件,我们这里即收银台名为这里用Subject 类表示。

  先来看事件类的定义:

public class Event {//事件源private Object source;//通知目标private Object target;//回调private Method callback;//触发private String trigger;//触发时间private long time;public Event(Object target, Method callback) {this.target = target;this.callback = callback;}// 重写一下toString 一边输出观察@Overridepublic String toString() {return "Event{" +"\n\tsource=" + source + ",\n" +"\ttarget=" + target + ",\n" +"\tcallback=" + callback + ",\n" +"\ttrigger='" + trigger + '\'' + "\n" +'}';}public Object getSource() { return source;}public Object getTarget() {return target;}public void setTarget(Object target) {this.target = target;}public Method getCallback() {return callback;}public void setCallback(Method callback) {this.callback = callback;}public String getTrigger() {return trigger;}public long getTime() { return time;}Event setTime(long time) {this.time = time;return this;}Event setSource(Object source) {this.source = source;return this;}Event setTrigger(String trigger) {this.trigger = trigger;return this;}
}

  然后定义事件监听器类:

public class EventLisenter {//Map相当于是一个注册器protected Map<Enum,Event> events = new HashMap<Enum,Event>();public void addLisenter(Enum eventType, Object target, Method callback){//注册事件//用反射调用这个方法events.put(eventType,new Event(target,callback));}private void trigger(Event e){e.setSource(this);//设置事件源e.setTime(System.currentTimeMillis());try {//反射调用该方法e.getCallback().invoke(e.getTarget(),e);} catch (Exception e1) {e1.printStackTrace();}}protected void trigger(Enum call){// 看是否注册了该事件if(!this.events.containsKey(call)){ return ;}//注册则触发trigger(this.events.get(call).setTrigger(call.toString()));}
}

  这个的事件的类型用一个枚举类来表示:

public enum SubjectEventType {ON_PAY,//支付事件ON_CANCEL;//取消订单事件
}

  事件源收银台Subject类通过继承监听器类来实现实现的触发:

public class Subject extends EventLisenter {//通常的话,采用动态代理来实现监控,避免了代码侵入public void pay(){System.out.println("调用支付的方法");//触发事件trigger(SubjectEventType.ON_PAY);}public void cancel(){System.out.println("调用取消订单的方法");trigger(SubjectEventType.ON_CANCEL);}
}

  最后来看看观察者的定义:

public class Observer {public void advice(Event e){System.out.println("===触发"+e.getTrigger()+"事件,打印日志===\n" + e);}
}

  测试:运行一下代码可以发现事件触发观察者方法执行:

public class ObserverTest {public static void main(String[] args) {try{//观察者Observer observer = new Observer();//观察者的具体动作Method advice = Observer.class.getMethod("advice", new Class<?>[]{Event.class});//收银台调用了支付或者取消订单Subject subject = new Subject();subject.addLisenter(SubjectEventType.ON_PAY,observer,advice);// 具体的执行方法可以自己定义,触发不同事件执行不同的方法,也可以统一处理
//            subject.addLisenter(SubjectEventType.ON_CANCEL,observer,advice);subject.pay();
//            subject.cancel();}catch (Exception e){e.printStackTrace();}}
}

  

设计模式之装饰者模式及观察者模式相关推荐

  1. Java进阶专题(八) 设计模式之适配器模式、装饰者模式、观察者模式

    本章节将介绍:三个设计模式,适配器模式.装饰者模式和观察者模式.通过学习适配器模式,可以优雅的解决代码功能的兼容问题.另外有重构需求的人群一定需要掌握装饰者模式.本章节参考资料书籍<Spring ...

  2. 5分钟读懂设计模式(2)---装饰者模式

    每当我们买了新房子之后,相信绝大部分人都会进行装修,给房子增加一些其他新的物品.不过,无论如何装修,这个房子还是这个房子,最本质的东西并没有变,有的只是我们通过装修的方式,给这个房子增加了一些额外的功 ...

  3. Java设计模式(装饰者模式-组合模式-外观模式-享元模式)

    Java设计模式Ⅳ 1.装饰者模式 1.1 装饰者模式概述 1.2 代码理解 2.组合模式 2.1 组合模式概述 2.2 代码理解 3.外观模式 3.1 外观模式概述 3.2 代码理解 4.享元模式 ...

  4. 前端也要学系列:设计模式之装饰者模式

    什么是装饰者模式 今天我们来讲另外一个非常实用的设计模式:装饰者模式.这个名字听上去有些莫名其妙,不着急,我们先来记住它的一个别名:包装器模式. 我们记着这两个名字来开始今天的文章. 首先还是上< ...

  5. 设计模式 之 装饰者模式

    2019独角兽企业重金招聘Python工程师标准>>> 设计模式 之 装饰者模式 装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能.它是通过创建一个包装对 ...

  6. 【设计模式】装饰者模式 ( 概念 | 适用场景 | 优缺点 | 与继承对比 | 定义流程 | 运行机制 | 案例分析 )

    文章目录 I . 装饰者模式概念 II . 装饰者模式适用场景 III . 装饰者模式优缺点 IV . 装饰者模式与继承对比 V . 装饰者模式相关设计模式 VI . 装饰者模式四个相关类 VII . ...

  7. 设计模式学习----装饰器模式

    这两天本来是自在学习java collection Framework的Fail Fast底层机制,看到核心的部分时,突然意识到设计模式的问题,上大学到现在我还没有真正理解过设计模式的概念,于是用了大 ...

  8. JavaScript设计模式之发布-订阅模式(观察者模式)-Part1

    <JavaScript设计模式与开发实践>读书笔记. 发布-订阅模式又叫观察者模式,它定义了对象之间的一种一对多的依赖关系.当一个对象的状态发生改变时,所有依赖它的对象都将得到通知. 例如 ...

  9. 【设计模式】装饰器模式的使用

    问题来源 我们在进行软件系统设计的时候,有一些业务(如下图,一些通用的非功能性需求)是多个模块都需要的,是跨越模块的.把它们放到什么地方呢? 最简单的办法就是把这些通用模块的接口写好,让程序员在实现业 ...

最新文章

  1. [Google Guava] 1.3-常见Object方法
  2. Linux C动态链接库实现一个插件例子
  3. 成功解决pycharm 没有菜单栏
  4. javaScript学习之正则表达式初探
  5. VSCode开发.NETCore项目入门(1)设置中文语言环境
  6. ipvs,ipvsadm的安装及使用
  7. 格力电器Q3净利润73.37亿元 同比下降12.32%
  8. mysql openrowset_SQL的OPENROWSET开启和使用方法
  9. 互联网巨头的“搜索”暗战
  10. Apache+Php+Mysql配置
  11. 网页选项卡应用4-12
  12. Linux考试题(带答案)
  13. 如何管理和组织一个机器学习项目
  14. 基于微信小程序的停车位预约系统设计与实现毕业设计毕设开题报告
  15. python离线语音识别_python语音识别模块
  16. 如何运用python做一个有音乐的二维码_怎样制作一个背景音乐和与文字一起的二维码?...
  17. Constructing Narrative Event Evolutionary Graph for Script Event Prediction
  18. java中的jsp是什么?
  19. ubuntu18与win10双系统引导修复
  20. Win11安卓子系统无法启动怎么办?安卓子系统启用虚拟机平台教程(确保在可选的Windows功能中启用虚拟机平台)

热门文章

  1. 武器后坐力——Unity随手记(2021.2.2)
  2. 导出dhcp服务器配置信息的命令,用于导出和导入 DHCP 范围的 Netsh 实用工具 - Windows Server | Microsoft Docs...
  3. Hive的基本操作之表分区
  4. linux 安装git环境变量配置
  5. git不是内部或外部命令,但是配置了环境变量也不管用
  6. 【动态规划】游艇租用问题(c++)
  7. 线程-interrupt方法详解
  8. Vulkan_片元着色器特效1(体积雾)
  9. 70%以上业务由H5开发,手机QQ Hybrid 的架构如何优化演进?
  10. 计算机专业是绩点重要,东北大学13级绩点排名-计算机专业