什么是装饰模式?
装饰模式是一种对象结构型模式。
装饰模式可以动态地为一个对象增加一些额外的行为。
装饰模式可以在不改变一个对象本身功能的基础上,提供给对象额外的实现。不同于继承在编译时为类增加额外的实现,装饰模式在运行时为对象提供了额外的实现。装饰模式无需子类即可为对象动态添加额外的实现,用关联关系代替了继承关系。
比如生活中,我们买到的毛坯房,基本功能就是可以住,有水有电有网,但是我们通常不会这样就搬进去住,我们还要对其进行个性化装修,让其增加做饭、睡觉和休息等功能,但是有水优点有网这些基本功能还是存在的,这其实就是装饰模式。
核心结构
- Component,抽象构件,是具体构件和抽象装饰的共同父类,声明了在具体构件中需要实现的业务执行,。
- ConcreteComponent,具体构件,是抽象构件的子类,实现抽象构件中声明的业务执行。
- Decorator,抽象装饰,是抽象构件的子类,用于给具体构件增加职责,并声明具体装饰中需要实现的业务执行,它维护了一个抽象构件的引用,在必要时通过这个引用实现装饰的目的。
- ConcreteComponent,具体装饰,是抽象装饰的子类,具体实现了抽象装饰中声明的装饰职责。
类图展示
代码分析
推送功能对于用户来说,可能就是推推消息,很讨厌。其实,推送还有一个功能就是及时解决线上问题,也就是push一段hotfix代码过去。
在这里,我们依然举推送中的实例来实现装饰模式。
首先我们定义推送主题——抽象构件:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/20 0020.
* 装饰模式——抽象构件
*/
public abstract class Push {
public abstract void send();
}
有了抽象构件,接下来我们就需要实现具体构件——指定推送:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/20 0020.
* 装饰模式——具体构件
*/
public class AppointPush extends Push {
@Override
public void send() {
System.out.println("appoint push task");
}
}
全量推送:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/20 0020.
* 装饰模式——具体构件
*/
public class FullAmountPush extends Push {
@Override
public void send() {
System.out.println("full amount push task");
}
}
在核心结构中我们讲过,抽象装饰也需要实现抽象构件,所以接下来我们需要定义抽象装饰——消息:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/20 0020.
* 装饰模式——抽象装饰
*/
public abstract class Message extends Push {
private Push push;
public Message(Push push) {
this.push = push;
}
@Override
public void send() {
push.send();
}
}
接下来就需要针对抽象装饰,实现一些具体的装饰职责。
透传消息类型:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/20 0020.
* 装饰模式——具体装饰
*/
public class MessageMessage extends Message {
public MessageMessage(Push push) {
super(push);
}
@Override
public void send() {
this.setMessageMessage();
super.send();
}
private void setMessageMessage() {
System.out.println("append title to message's body");
System.out.println("append content to message's body");
}
}
通知消息类型:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/20 0020.
* 装饰模式——具体装饰
*/
public class NotificationMessage extends Message {
public NotificationMessage(Push push) {
super(push);
}
@Override
public void send() {
this.setNotificationMessage();
super.send();
}
private void setNotificationMessage() {
System.out.println("append title to notification's body");
System.out.println("append content to notification's body");
}
}
这样,一个简单的装饰模式就已经实现了,接下来上测试代码:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/20 0020.
* 装饰模式测试
*/
public class DecoratorTest {
public static void main(String[] args) {
decoratorTest();
}
private static void decoratorTest() {
try {
Message notificationMessage = new NotificationMessage(new FullAmountPush());
notificationMessage.send();
Message messageMessage = new MessageMessage(new FullAmountPush());
messageMessage.send();
notificationMessage = new NotificationMessage(new AppointPush());
notificationMessage.send();
messageMessage = new MessageMessage(new AppointPush());
messageMessage.send();
} catch (Exception e) {
e.printStackTrace();
}
}
}
模式总结
优点
- 对比于继承扩展,装饰模式避免了类的指数级剧增。
- 可以对对象进行多层装饰,实现更丰富的行为。
- 符合开闭原则,在更新对象的表现行为时,可以新增、变换装饰方式,而无需改变现有代码。
缺点
- 装饰模式虽然有更好的灵活性,但同时增加的系统的设计难度和维护难度。
- 使用装饰模式对对象进行装饰时,势必会产生多个用于装饰对象,如果一个系统中具体装饰类过多,可能会影响系统GC,在一定程度上影响了系统的性能。
适用场景
- 在不影响其他对象的情况下,透明、动态地给单个对象添加职责。
- 在不能够或者不适合使用继承的方式扩展类时,可以采用装饰模式。比如说用final修饰的类,或者继承类时需要爆炸添加多个子类。