装饰模式的思想
装饰模式是一种结构型设计模式。
思想: 以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。
装饰模式涉及4个角色
- 抽象构件(Component):要被装饰的类的抽象。
- 具体构件(Concrete Component):要被装饰的具体类型。
- 抽象装饰者(Decorator):内部包装了一个构件的实例,并实现与构件接口相同的接口。
- 具体装饰者(Concrete Decorator):为被装饰者增加加或减少行为。
装饰模式案例
案例: 学校成绩单需要家长签字,成绩单给家长看之前可以给出各科的最高分,家长签字之前可以给出成绩排名。
定义成绩单接口,抽象出成绩单的方法:(抽象构件)
public interface SchoolReport {
//成绩单的主要展示的就是你的成绩情况
void report();
//成绩单要家长签字,这个是最要命的
void sign(String name);
}
具体成绩单实现类,这里是四年级的成绩单:(具体构件)
public class FouthGradeSchoolReport implements SchoolReport {
//我的成绩单
@Override
public void report() {
//成绩单的格式是这个样子的
System.out.println("尊敬的XXX家长:");
System.out.println(" ......");
System.out.println(" 语文 62 数学65 体育 98 自然 63");
System.out.println(" .......");
System.out.println("家长签名:");
}
//家长签名
@Override
public void sign(String name) {
System.out.println("家长签名为:"+name);
}
}
创建一个抽象的装饰者,将成绩单装饰一下:(抽象装饰者)
public abstract class Decorator implements SchoolReport{
//首先我要知道是那个成绩单
private SchoolReport schoolReport;
//构造函数,传递成绩单过来
public Decorator(SchoolReport schoolReport){
this.schoolReport = schoolReport;
}
@Override
public void report() {
this.schoolReport.report();
}
@Override
public void sign(String name) {
this.schoolReport.sign(name);
}
}
创建具体装饰类,并编写装饰方法,在查看成绩单或签名之前先调用装饰方法。
各科最高分装饰类:(具体装饰者)
public class HighScoreDecorator extends Decorator {
public HighScoreDecorator(SchoolReport schoolReport){
super(schoolReport);
}
private void reportHighScore(){
System.out.println("这次考试语文最高是75,数学是78,自然是80");
}
@Override
public void report() {
this.reportHighScore();
super.report();
}
}
排名装饰类:(具体装饰者)
public class SortDecorator extends Decorator {
public SortDecorator(SchoolReport schoolReport){
super(schoolReport);
}
private void reportSort(){
System.out.println("我是排名第38名...");
}
@Override
public void sign(String name) {
this.reportSort();
super.sign(name);
}
}
最后父亲查看成绩单并签字:
public class Father {
public static void main(String[] args) {
SchoolReport schoolReport = new FouthGradeSchoolReport();
schoolReport = new HighScoreDecorator(schoolReport);
schoolReport = new SortDecorator(schoolReport);
schoolReport.report();
schoolReport.sign("James");
}
}
运行结果:
在查看成绩单之前显示了各科最高分,签字之前显示了成绩排名情况。
一般的装饰模式
结构图:
抽象构件、具体构件:
// 抽象构件
public interface Component {
void method1();
void method2();
}
// 具体构件
public class ConcreteComponent implements Component{
@Override
public void method1() {
System.out.println("method 1");
}
@Override
public void method2() {
System.out.println("method 2");
}
}
抽象装饰者、具体装饰者:
// 抽象装饰者
public abstract class DecoratorAbs implements Component {
// 包装一个构件的对象
private Component component;
public DecoratorAbs(Component component) {
this.component = component;
}
@Override
public void method1() {
component.method1();
}
@Override
public void method2() {
component.method2();
}
}
// 具体装饰者
public class ConcreteDecoratorA extends DecoratorAbs {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void method1() {
System.out.println("增强了method1逻辑");
super.method1();
}
}
// 具体装饰者
public class ConcreteDecoratorB extends DecoratorAbs {
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public void method2() {
System.out.println("增强了method2逻辑");
super.method2();
}
}
测试类:
public class Client {
public static void main(String[] args) {
// 未被装饰的对象
Component component = new ConcreteComponent();
component.method1();
System.out.println("---黄金分割线---");
// 装饰后的对象
Component component1 = new ConcreteDecoratorA(new ConcreteComponent());
component1.method1();
System.out.println("---黄金分割线---");
Component component2 = new ConcreteDecoratorB(new ConcreteComponent());
component2.method2();
}
}
运行结果:
装饰模式的简化
如果只有一个ConcreteComponent
而没有抽象的Component
接口,可以让Decorator
直接继承ConcreteComponent
。
如果只有一个ConcreteDecorator
,可以将Decorator
和ConcreteDecorator
合并。
装饰模式的总结
优点:
- 对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧增加。
- 可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,得到功能更为强大的对象。
- 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”。
缺点:
- 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,大量小对象的产生势必会占用更多的系统资源,在一定程序上影响程序的性能。
- 装饰模式提供了一种比继承更加灵活机动的解决方案,但同时也意味着比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。
适用场景:
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
- 当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式。
- 例子:Java.io中的输入流和输出流的设计、javax.swing包中一些图形界面构件功能的增强。