设计模式学习总结-装饰模式
一 定义
装饰模式是在不必改变原类文件和不使用继承的情况下,而是使用组合的方式动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
二 装饰模式的特点
(1) 装饰对象和真实对象有相同的接口。这样客户端对象就可以以和真实对象相同的方式和装饰对象交互。
(2) 装饰对象包含一个真实对象的索引(reference)
(3) 装饰对象接受所有的来自客户端的请求。它把这些请求转发给真实的对象。
(4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。
三 UML图
1. 抽象构件:原始的功能接口
2. 具体构件:具体的原始功能类
3. 装饰角色:持有具体构件类的对象,以便执行原有功能
4. 具体装饰:具体扩展的功能在这里
四 代码
使用装饰模式来演示一个对”行走”功能扩展的例子(听音乐+行走 和 唱歌+行走)
1. 抽象构件
publicinterface Component { /**原始接口*/ publicvoid go(); } |
2. 具体构件
publicclass ConcreteComponent implements Component { publicvoid go() { System.out.println("行走"); } } |
3.装饰角色来了
publicclass Decorator implements Component { /**持有私有的原始构件*/ private Component component; /**构造子,委派给原始构件*/ protected Decorator(Component component) { this.component = component; } /**调用原始构件功能,通常就可直接把扩展功能加在此方法中*/ publicvoid go() { this.component.go(); } } |
4.具体装饰(这里演示了两种扩展的情况,走路+听音乐和唱歌s)
(1).
publicclass ConcreteDecoratorListen extends Decorator { /**构造子,相关初始化*/ public ConcreteDecoratorListen(Component component) { super(component); // code is here } /**商业逻辑,对原始构件功能的扩展*/ publicvoid go() { listen("听音乐");//执行扩展功能 super.go(); } privatevoid listen(Object obj){ System.out.println(obj); } } |
(2).
publicclass ConcreteDecoratorSing extends Decorator { /**构造子,相关初始化*/ public ConcreteDecoratorSing(Component component) { super(component); // code is here } /**商业逻辑,对原始构件功能的扩展*/ public void go() { super.go(); System.out.println(sing());;// 执行扩展功能 } private String sing() { return"唱歌"; } } |
5.客户端调用
publicclass Main { publicstaticvoid main(String[] args) { /**原始构件*/ Component component = new ConcreteComponent(); /**边听音乐,边走路*/ ConcreteDecoratorListen cdl = new ConcreteDecoratorListen(component); cdl.go(); System.out.println(); /**边走路,边唱歌*/ ConcreteDecoratorSing cds = new ConcreteDecoratorSing(component); cds.go(); } } |
五 装饰模式与继承的比较
装饰模式和继承都是对功能的扩展,而装饰模式使用的是组合,可以不用继承而达到这一效果.使用过多的继承会增加系统的复杂性和偶合性,下面对二者做一个比较。
装饰模式 VS 继承
装饰模式 | 继承 |
用来扩展特定对象的功能 | 用来扩展一类对象的功能 |
不需要子类 | 需要子类 |
动态地 | 静态地 |
运行时分配职责 | 编译时分派职责 |
防止由于子类而导致的复杂和混乱 | 导致很多子类产生,在一些场合,报漏类的层次 |
更多的灵活性 | 缺乏灵活性 |
对于一个给定的对象,同时可能有不同的装饰对象,客户端可以通过它的需要选择合适的装饰对象发送消息 | 对于所有可能的联合,客户期望 |
很容易增加任何的扩展 | 困难扩展 |
六 扩展学习实例
1. Java IO类库
JDK提供的java.io包中使用了Decorator模式来实现对各种输入输出流的封装。
2. log4j
public class LoggerDecorator implements Logger { Logger logger; public LoggerDecorator(Logger inp_logger) { logger = inp_logger; } public void log(String DataLine) { /* Default implementation to be overriden by subclasses. */ logger.log(DataLine); } }//end of class public class HTMLLogger extends LoggerDecorator { public HTMLLogger(Logger inp_logger) { super(inp_logger); } public void log(String DataLine) { /* Added functionality */ DataLine = makeHTML(DataLine); /* Now forward the encrypted text to the FileLogger for storage */ logger.log(DataLine); } public String makeHTML(String DataLine) { /* Make it into an HTML document. */ DataLine = "" + "" + DataLine + "" + ""; return DataLine; } }//end of class public class EncryptLogger extends LoggerDecorator { public EncryptLogger(Logger inp_logger) { super(inp_logger); } public void log(String DataLine) { /* Added functionality */ DataLine = encrypt(DataLine); /* Now forward the encrypted text to the FileLogger for storage */ logger.log(DataLine); } public String encrypt(String DataLine) { /* Apply simple encryption by Transposition… Shift all characters by one position. */ DataLine = DataLine.substring(DataLine.length() − 1) + DataLine.substring(0, DataLine.length() − 1); return DataLine; } }//end of class |
客户端调用:
public static void main(String[] args) { LoggerFactory factory = new LoggerFactory(); Logger logger = factory.getLogger(); HTMLLogger hLogger = new HTMLLogger(logger); //the decorator object provides the same interface. hLogger.log("A Message to Log"); EncryptLogger eLogger = new EncryptLogger(logger); eLogger.log("A Message to Log"); } |