1.课程目标
1.掌握装饰者模式的特征和应用场景。
2.掌握装饰者模式和适配器模式的根本区别。
3.观察者模式在源码中的应用及实现原理。
4.了解装饰者模式和观察者模式的优缺点。
2.装饰者模式
2.1装饰者模式的定义
装饰者模式(Decorator Pattern)是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(拓展原有对象的功能)。
属于结构型模式
2.2装饰者模式的适用场景
1.用于拓展一个类的功能或给一个类添加附加职责。
2.动态的给一个对象添加功能,这些功能可以再动态的撤销
2.3 Demo案例
买煎饼案例
v1:传统方案
@Data
public class Battercake {
public String getMsg (){
return "一个煎饼";
}
public int getPrice(){
return 5;
}
}
/**
* @PackageName: com.raven.pattern.decorator.battercake.v1
* @ClassName: BattercakeWithEgg
* @Blame: raven
* @Date: 2021-08-07 18:52
* @Description: 一个煎饼加一个鸡蛋
*/
public class BattercakeWithEgg extends Battercake{
@Override
public String getMsg() {
return super.getMsg() + "+ 一个鸡蛋";
}
@Override
public int getPrice() {
return super.getPrice() + 1;
}
}
/**
* @PackageName: com.raven.pattern.decorator.battercake.v1
* @ClassName: BattercakeWithSausage
* @Blame: raven
* @Date: 2021-08-07 18:54
* @Description: 一个煎饼+一个火腿
*/
public class BattercakeWithSausage extends Battercake {
@Override
public String getMsg() {
return super.getMsg() + "+ 一个火腿";
}
@Override
public int getPrice() {
return super.getPrice() + 2;
}
}
package com.raven.pattern.decorator.battercake.v1;
/**
* @PackageName: com.raven.pattern.decorator.battercake.v1
* @ClassName: BattercakeTest
* @Blame: raven
* @Date: 2021-08-07 18:55
* @Description:测试类 传统方式对原有类"Battercake" 进行拓展,需要不断的创建新的类
* 当需求发生轻微变更时就需要在创建新的类,来满足现有需求
*/
public class BattercakeTest {
public static void main(String[] args) {
Battercake battercake = new Battercake();
System.out.println("买" + battercake.getMsg() + "一共需要" + battercake.getPrice() + "元");
battercake = new BattercakeWithEgg();
System.out.println("买" + battercake.getMsg() + "一共需要" + battercake.getPrice() + "元");
battercake = new BattercakeWithSausage();
System.out.println("买" + battercake.getMsg() + "一共需要" + battercake.getPrice() + "元");
// 如果现在需要买加俩个鸡蛋,俩个火腿的煎饼,就需要创一个新的类,随着需要的变更,会有越来越多的类。
}
}
V2:使用装饰者模式
package com.raven.pattern.decorator.battercake.v2;
import lombok.Data;
/**
* @PackageName: com.raven.pattern.decorator.battercake.v1
* @ClassName: Battercake
* @Blame: raven
* @Date: 2021-08-07 18:49
* @Description:模拟销售煎饼案例 煎饼实体
*/
@Data
public abstract class Battercake {
/**
* 购买煎饼具体信息
* @return
*/
public abstract String getMsg();
/**
* 煎饼价格
* @return
*/
public abstract int getPrice();
}
/**
* @PackageName: com.raven.pattern.decorator.battercake.v2
* @ClassName: BaseBattercake
* @Blame: raven
* @Date: 2021-08-07 19:03
* @Description:描述一个基本的煎饼的信息
*/
public class BaseBattercake extends Battercake {
@Override
public String getMsg() {
return "一个煎饼";
}
@Override
public int getPrice() {
return 5;
}
}
Decorator
/**
* @PackageName: com.raven.pattern.decorator.battercake.v2
* @ClassName: BattercakeDecorator
* @Blame: raven
* @Date: 2021-08-07 19:05
* @Description: 煎饼的装饰器父类,规范鸡蛋装饰器的行为,
*/
public class BattercakeDecorator extends Battercake {
private Battercake battercake;
public BattercakeDecorator(Battercake battercake) {
this.battercake = battercake;
}
@Override
public String getMsg() {
return this.battercake.getMsg();
}
@Override
public int getPrice() {
return this.battercake.getPrice();
}
}
/**
* @PackageName: com.raven.pattern.decorator.battercake.v2
* @ClassName: EggDecorator
* @Blame: raven
* @Date: 2021-08-07 19:10
* @Description: 加1个鸡蛋的装饰器
*/
public class EggDecorator extends BattercakeDecorator {
public EggDecorator(Battercake battercake) {
super(battercake);
}
@Override
public String getMsg() {
return super.getMsg() + "+ 一个鸡蛋";
}
@Override
public int getPrice() {
return super.getPrice() + 1;
}
}
/**
* @PackageName: com.raven.pattern.decorator.battercake.v2
* @ClassName: SausageDecorator
* @Blame: raven
* @Date: 2021-08-07 19:13
* @Description:
*/
public class SausageDecorator extends BattercakeDecorator {
public SausageDecorator(Battercake battercake) {
super(battercake);
}
@Override
public String getMsg() {
return super.getMsg() + "+ 一个火腿";
}
@Override
public int getPrice() {
return super.getPrice() + 2;
}
}
main
/**
* @PackageName: com.raven.pattern.decorator.battercake.v2
* @ClassName: BattercakeTest
* @Blame: raven
* @Date: 2021-08-07 19:13
* @Description: 通过装饰者模式封装后的卖煎饼案例
*/
public class BattercakeTest {
public static void main(String[] args) {
// 通过装饰者模式包装后,可以买+不同样式的煎饼,并且在需求变更后无需在创建更多的类
Battercake battercake = new BaseBattercake();
System.out.println("买" + battercake.getMsg() + "一共需要" + battercake.getPrice() + "元");
battercake = new EggDecorator(battercake);
System.out.println("买" + battercake.getMsg() + "一共需要" + battercake.getPrice() + "元");
battercake = new EggDecorator(battercake);
System.out.println("买" + battercake.getMsg() + "一共需要" + battercake.getPrice() + "元");
battercake = new SausageDecorator(battercake);
System.out.println("买" + battercake.getMsg() + "一共需要" + battercake.getPrice() + "元");
}
}
2.4装饰者模式和适配器模式的对比
\ | 装饰者模式 | 适配器模式 |
---|---|---|
形式 | 是一种非常特别的适配器模式 | 没有层级关系,装饰者模式有层级关系 |
定义 | 装饰者和被装者都实现了同一个接口,主要目的是为了拓展之后依旧保留OOP关系 | 适配器和被适配者没有必然的联系,通常是采用继承或代理的形式进行包装 |
关系 | 满足is-a的关系 | 满足has-a的关系 |
功能 | 注重覆盖,拓展 | 注重兼容、转换 |
设计 | 前置考虑 | 后置考虑 |
2.5源码中的装饰者模式
jdk中的装饰者模式 : IO流
spring中的装饰者模式: TransactionAwareCacheDecorator、
HttpHeadResponseDecorator
mybatis中的装饰者模式: LruCache、FifoCache
2.6装饰者模式的优点
1.装饰者模式是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象拓展功能,即插即用
2.通过使用装饰类以及这些装饰类的排列组合,可以实现不同效果
3.装饰者完全遵守开闭原则。
2.7装饰者模式的缺点
1.会出现更多的代码,更多的类,增加程序复杂性。
2.动态装饰时,多层装饰时会更复杂。
2.8装饰者模式和静态代理的区别
1.装饰者模式和静态代理的区别主要是职责不同
2.静态代理不一定要满足is-a的关系,静态代理会做功能的增强,同一个职责变得不一样
3.装饰者模式更多考虑的是拓展
3.观察者模式
3.1观察者模式的定义
1.观察者模式(Observer Pattern) 定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主体对象发送变化时,它的所有依赖者(观察者)都会收到通知并更新。
2.属于行为型模式。
3.观察者模式有时也叫发布订阅模式。
3.2 Demo 案例
社区问答案例:
被观察者(生产者):
/**
* @PackageName: com.raven.pattern.observer.advice
* @ClassName: Coder
* @Blame: raven
* @Date: 2021-08-07 19:30
* @Description: 被观察者,通过继承Observable得到实现 是JDK提供的一种观察者的实现方式
*/
@Data
public class Coder extends Observable {
private String name = "Coder生态圈";
private static volatile Coder coder = null;
private Coder() {
}
public static Coder getInstance() {
if (Objects.isNull(coder)) {
synchronized (Coder.class) {
if (Objects.isNull(coder)) {
coder = new Coder();
}
}
}
return coder;
}
public void publishQuestion(Question question) {
System.out.println(question.getUsername() + "在" + name + "提了一个问题");
// 通过该访问提交问题
setChanged();
// 通知所有观察该类的观察者们 将问题传递
notifyObservers(question);
}
}
@Data
public class Question {
private String username;
private String content;
}
观察者(消费者)
public class Teacher implements Observer {
private String name;
public Teacher(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
// 被观察者
Coder coder = (Coder) o;
System.out.println("=================");
Question question = (Question) arg;
System.out.println(name + "老师,你在" + coder.getName() + "中收到了"+question.getUsername()+"提的问题,\n"
+ "问题的内容是:\n" + question.getContent());
}
}
main
/**
* @PackageName: com.raven.pattern.observer.advice
* @ClassName: ObserverTest
* @Blame: raven
* @Date: 2021-08-07 19:52
* @Description: JDK观察者模式 demo测试类
*/
public class ObserverTest {
public static void main(String[] args) {
// 获取一个被观察coder
Coder coder = Coder.getInstance();
Question question = new Question();
question.setContent("如何学好设计模式?");
question.setUsername("小王");
// 创建一个观察者teacher
Teacher teacher = new Teacher("raven");
// 将观察者和被观察者绑定
coder.addObserver(teacher);
// 被观察将问题提交
coder.publishQuestion(question);
}
}
event事件监听案例
main
public class MouseEventTest {
public static void main(String[] args) throws NoSuchMethodException {
MouseEventCallback callback = new MouseEventCallback();
Mouse mouse = new Mouse();
mouse.addListener(MouseEventType.ON_CLICK, callback);
mouse.click();
mouse.doubleClick();
// 当调用 addListener方法时,将名字为onClick的事件 ,注册到events事件管理中
// 当调用click方法时,执行events中已经注册好的事件,未注册的实际不会触发
}
}
event
@Data
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;
}
}
public class EventListener {
/**
* JDK底层的listener通常也是这样设计
*/
private Map<String, Event> eventMap = Maps.newHashMap();
public void addListener(String eventType, Object target) {
try {
this.addListener(eventType,
target,
target.getClass().getMethod("on" + toUpperFirstCase(eventType),Event.class));
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
private String toUpperFirstCase(String eventType) {
char[] chars = eventType.toCharArray();
chars[0] -= 32;
return String.valueOf(chars);
}
/**
* 将事件加入到事件列表中
* @param eventType
* @param target
* @param callback
*/
private void addListener(String eventType, Object target, Method callback) {
eventMap.put(eventType, new Event(target, callback));
}
/**
* 事件名称触发
* @param trigger
*/
public void trigger(String trigger){
if (eventMap.containsKey(trigger)){
this.eventMap.get(trigger).setTrigger(trigger);
trigger(this.eventMap.get(trigger));
}
}
private void trigger(Event event){
event.setSource(this);
event.setTime(System.currentTimeMillis());
try {
event.getCallback().invoke(event.getTarget(),event);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
public class Mouse extends EventListener {
public void click(){
System.out.println("调用单击方法");
trigger(MouseEventType.ON_CLICK);
}
public void doubleClick(){
System.out.println("调用双击方法");
trigger(MouseEventType.ON_DOUBLE_CLICK);
}
}
public interface MouseEventType {
String ON_CLICK = "click";
String ON_DOUBLE_CLICK = "doubleClick";
}
EventCallback
public class MouseEventCallback {
public void onClick(Event event){
System.out.println("=======触发鼠标单击事件======" + "\n" + event);
}
public void onDoubleClick(Event event){
System.out.println("=======触发鼠标双击事件======" + "\n" + event);
}
}
guava
guava event
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
GuavaEvent
public class GuavaEvent {
@Subscribe
public void register(String name){
System.out.println(name + "加入了coder论坛!");
}
}
main
public class GuavaEventTest {
public static void main(String[] args) {
// 消息总线
EventBus eventBus = new EventBus();
// 消息事件
GuavaEvent guavaEvent = new GuavaEvent();
// 注册消息事件
eventBus.register(guavaEvent);
// 发送消息,实时接收,触发事件
eventBus.post("战三");
// jdk的obServer以及spring中的Event都是面向类的, guava是面向方法的。
}
}
3.3观察者模式的适用场景
观察者模式主要用于在关联行为之间建立一套触发机制的场景
3.4观察者模式的优点
1.观察者和被观察者之间建立了一个抽象的耦合。
2.观察者模式支持广播通信。
3.5观察者模式的缺点
1.观察者之间有过多的细节依赖、提高时间消耗及程序的复杂度。
2.使用要得当,要避免循环调用
3.6观察者模式在源码中的应用
contextLoaderListener