装饰者模式(Decorator Pattern)

在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。例如给房子装修,添加家具,给煎饼果子加鸡蛋加香肠

适用场景

  • 用于扩展一个类的功能或给一个类添加附加职责。
  • 动态的给一个对象添加功能,这些功能可以再动态的撤销。

例如给煎饼果子加鸡蛋加香肠,最简单的方法就是对煎饼果子的方法进行继承,对原有的方法进行增强,但是单纯的继承会存在问题,必须在原有的类上进行继承,无法灵活的加蛋和肠,所以此时可用装饰者模式。
image.png

  1. public static void main(String[] args) {
  2. Battercake battercake;
  3. // 新建煎饼果子对象
  4. battercake = new BaseBattercake();
  5. // 新建实现类,将基础的煎饼果子通过构造方法带入实现类中,调用煎饼果子的方法
  6. battercake = new BattercakeEggDecorator(battercake);
  7. // 新建实现类,将加了蛋的煎饼果子通过构造方法带入实现类中,调用他们的方法,+个蛋
  8. battercake = new BattercakeEggDecorator(battercake);
  9. // .....
  10. battercake = new BattercakeSausageDecorator(battercake);
  11. System.out.println("购买信息:" + battercake.getMsg() + ",购买价格:" + battercake.getPrice());
  12. }

在这里装饰者模式设计还挺有意思,BattercakeDecorator装饰类通过构造方法去调用Battercake的方法,通过使用BattercakeEggDecorator、BattercakeSausageDecorator增强类去加强Battercake的方法(感觉增强方法有点类似套娃)

这里装饰者模式类似静态代理,但两者概念又有所不同,装饰者模式增强方法后本质依旧还是原来的对象,例如煎饼果子加了蛋和肠后,依然还是煎饼果子,而代理模式增强方法后可能完成变成另外一个对象(不是很明白doge)

装饰者模式的优缺点

优点:

  • 装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用。
  • 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。
  • 装饰者完全遵守开闭原则。

缺点:

  • 会出现更多的代码,更多的类,增加程序复杂性。
  • 动态装饰时,多层装饰时会更复杂。

观察者模式(Observer Pattern)

定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主体对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新,属于行为型模式。观察者模式有时也叫做发布订阅模式。观察者模式主要用
于在关联行为之间建立一套触发机制的场景。多在消息通知等方面使用,比如微信朋友圈动态通知,邮件通知、广播通知、桌面程序的事件响应。

是学习示例中,有展示JDK自带的观察者(Observer)和被观察者(Observable),也可自行实现监听事件。

  1. // 监听器,它就是观察者的桥梁
  2. public class EventListener {
  3. /**
  4. * JDK 底层的 Lisenter 通常也是这样来设计的
  5. */
  6. protected Map<String, Event> events = new HashMap<String, Event>();
  7. /**
  8. * 事件名称和一个目标对象来触发事件
  9. */
  10. public void addListener(String eventType, Object target) {
  11. try {
  12. this.addListener(eventType, target, target.getClass().getMethod("on" + toUpperFirstCase(eventType), Event.class));
  13. } catch (Exception e) {
  14. e.printStackTrace();
  15. }
  16. }
  17. public void addListener(String eventType, Object target, Method callback) {
  18. // 注册事件
  19. events.put(eventType, new Event(target, callback));
  20. }
  21. /**
  22. * 触发,只要有动作就触发
  23. */
  24. private void trigger(Event event) {
  25. event.setSource(this);
  26. event.setTime(System.currentTimeMillis());
  27. try {
  28. //发起回调
  29. if (event.getCallback() != null) {
  30. //用反射调用它的回调函数
  31. event.getCallback().invoke(event.getTarget(), event);
  32. }
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. /**
  38. * 逻辑处理的私有方法,首字母大写
  39. */
  40. private String toUpperFirstCase(String str) {
  41. char[] chars = str.toCharArray();
  42. chars[0] -= 32;
  43. return String.valueOf(chars);
  44. }
  45. /**
  46. * 判断是否被监听,确认是否触发
  47. */
  48. protected void trigger(String trigger) {
  49. if (!this.events.containsKey(trigger)) {
  50. return;
  51. }
  52. trigger(this.events.get(trigger).setTrigger(trigger));
  53. }
  54. }
  1. public class Mouse extends EventListener {
  2. public void click() {
  3. System.out.println("调用单击方法");
  4. this.trigger(MouseEventType.ON_CLICK);
  5. }
  6. public void doubleClick() {
  7. System.out.println("调用双击方法");
  8. this.trigger(MouseEventType.ON_DOUBLE_CLICK);
  9. }
  10. public void up() {
  11. System.out.println("调用弹起方法");
  12. this.trigger(MouseEventType.ON_UP);
  13. }
  14. public void down() {
  15. System.out.println("调用按下方法");
  16. this.trigger(MouseEventType.ON_DOWN);
  17. }
  18. public void move() {
  19. System.out.println("调用移动方法");
  20. this.trigger(MouseEventType.ON_MOVE);
  21. }
  22. public void wheel() {
  23. System.out.println("调用滚动方法");
  24. this.trigger(MouseEventType.ON_WHEEL);
  25. }
  26. public void over() {
  27. System.out.println("调用悬停方法");
  28. this.trigger(MouseEventType.ON_OVER);
  29. }
  30. public void blur() {
  31. System.out.println("调用获焦方法");
  32. this.trigger(MouseEventType.ON_BLUR);
  33. }
  34. public void focus() {
  35. System.out.println("调用失焦方法");
  36. this.trigger(MouseEventType.ON_FOCUS);
  37. }
  38. }
  1. //监听器的一种包装, 标准事件源格式的定义
  2. @Data
  3. public class Event {
  4. /**
  5. * 事件源,事件是由谁发起的保存起来
  6. */
  7. private Object source;
  8. /**
  9. * 事件触发,要通知谁
  10. */
  11. private Object target;
  12. /**
  13. * 事件触发,要做什么动作,回调
  14. */
  15. private Method callback;
  16. /**
  17. * 事件的名称,触发的是什么事件
  18. */
  19. private String trigger;
  20. /**
  21. * 事件触发的时间
  22. */
  23. private long time;
  24. public Event(Object target, Method callback) {
  25. this.target = target;
  26. this.callback = callback;
  27. }
  28. @Override
  29. public String toString() {
  30. return "Event{" + "\n" + "\tsource=" + source.getClass() + ",\n" + "\ttarget=" + target.getClass() + ",\n" + "\tcallback=" + callback + ",\n" + "\ttrigger='" + trigger + "',\n" + "\ttime=" + time + "'\n" + '}';
  31. }
  32. public Event setTrigger(String trigger) {
  33. this.trigger = trigger;
  34. return this;
  35. }
  36. }
  1. public static void main(String[] args) {
  2. MouseEventCallback callback = new MouseEventCallback();
  3. // 注册事件
  4. Mouse mouse = new Mouse();
  5. mouse.addListener(MouseEventType.ON_CLICK, callback);
  6. mouse.addListener(MouseEventType.ON_MOVE, callback);
  7. mouse.addListener(MouseEventType.ON_WHEEL, callback);
  8. mouse.addListener(MouseEventType.ON_OVER, callback);
  9. // 触发事件
  10. mouse.click();
  11. mouse.blur();
  12. }

监听执行流程

  1. 创建触发事件后执行的方法对象
  2. 注册事件,将执行方法名和方法对象加入监听对象中,此时事件通过Event将触发条件(MouseEventType)、执行对象(MouseEventCallback),执行对象具体某个方法存入在Event中。
  3. 当事件mouse触发click时,判断出是被监听状态,则执行对应MouseEventType.ON_CLICK 的Event中的方法onClick(Event e)
  4. 当事件触发blur时,判断出是未被监听状态,则不进行任何操作

观察者模式的优缺点

优点:

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

缺点:

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