不考虑消息日志的全部实现过程具体代码如下
interface ILogger{void log(String msg);}
class ConsoleLogger implements ILogger{public void log(String msg) {Sout;}}
class FileLogger implements ILogger {public void log(String msg) {DataOutputStream dos null;try{dos new DataOutputStream(new FielOutputStream("d:log.txt", true));dos.writeBytes(msg "\r\n");dos.close();} catch (Exception e) {e.printStackTrace();}}}
加入现在提出了新需求接收到的信息科转化成大写字母或转化成 XML 文档然后屏幕显示或日志保存。常规思路是利用派生类实现。 如果按照继承思路若需求分析继续变化则类的数目增加非常快。那么装饰器模式就是较好的思路之一。
装饰器模式
装饰器模式利用包含代替继承动态的给一个对象添加一个额外的功能。以消息日志功能为例装饰器模式类图如图 抽象修饰器基类 Decorator
abstract class Decorator implements ILogger {protected ILogger logger;public Decorator(Logger logger) {this.logger logger;}}
主要体会“implements ILogger” 中的 “ILogger” 与定义的成员变量 “ILogger logger” 中的 “ILogger”语义有什么不同。需求分析变化了但无论怎么变它终究还是一个日志类因此 Decorator 类要从接口 ILogger 派生。而成员变量 logger 标明 Decorator 对象要对已有的 logger 对象进行装饰也就是说要对已有的 FileLogger 或 ConsoleLogger 对象进行装饰。但是由于装饰的内容不同因此该类只能是抽象类。具体的装饰内容由子类完成。
具体实现类
class UpLogger extends Decorator {public UpLogger(ILogger logger) {super(logger);}public void log(String msg) {// 对字符串进行大写修饰msg msg.toUpperCase();// 再执行已有的日志功能logger.log(msg);}}
代码解释 log() 方法先对字符串大写装饰再执行已有的日志功能。若已有日志能有有 n 个则装饰后的字符串可能有 n 个去处也就是说该类可以表示 n 个动态含义。 若按照 继承模式变成则需要编制 n 个 具体的类从中可知 装饰器模式是采用动态编程的缩小了程序的规模。这就是上面提到的 两个 ILogger 的第二个含义的体现
另一个实现类也是同样的形式。
一个简单的测试类
public class Test{public static void main(String[] args) throws Exception {// 已有的日志功能ILogger existobj new FileLogger();// 新的日志装饰类对 existobj 装饰ILogger newobj new XMLLogger(existObj);......}}
装饰器模式主要有 4 中角色
- 抽象构件角色Component 他是一个接口封装了将要实现的方法如 ILogger
- 具体构件角色ConcreteComponent 他是多个类该类实现了 Component 接口如 FileLogger、ConsoleLogger
- 装饰角色Decorator 他是一个抽象类该类实现了 Component 接口同时也必须持有 Component 的对象的医用如 Decorator
- 具体的装饰角色Decorator 类的子类 这些类继承了 类 Decorator 实现了 Component 接口描述了具体的装饰过程。如 UpLogger、XMLLogger
深入理解装饰器模式
一本菜谱已在全国发型特点是具有通用性但没有考虑地域差异。加入以做白菜和大头菜为例时间情况是以菜谱为蓝本在考虑地域差异比如A 地先吃辣的B 地喜欢吃甜的用计算机如何描述
interface ICook {// 做菜void cook()}
class Vegetable implements ICook {public void cook() {}}class Cabbage implements ICook {public void cook() {}}
abstract class Decorator implements ICook{ICook obj;public Decorator(ICook obj) {this.obj obj;}}
class PepperDecorator extends Decorator {public PepperDecorator(ICook obj) {super(obj);}private void addPepper() {// 添加辣椒}public void cook() {addPepper();obj.cook();}}
class SUgarDecorator extends Decorator{......}
和上面操作时一样的。
Vegetable 和 Cabbage 是具体角色也就是说菜谱中每道菜的做法相当于具体角色他们都是长期经验的总结。没有这些具体角色也就他不上与地域相关的特色装饰菜了。好的菜谱有一个重要的特点一般来说就是只要人们能想到的菜就能在菜谱中找到。转化为计算机专业术语即是具体角色相当于底层的具体实现有哪些实现功能非常重要。如果做得好的话很大程度上上层功能相当于对底层功能进行进一步的封装和完善类似于装饰的功能。
jdk 装饰器模式
抽象构件相当于 Reader具体构件相当于 InputStream、CharArrayReder 以及 FileReader。该类图中并没有体现抽象装饰器 BufferedReader、LineNumberReader 都是具体装饰器因为他们都有Reader 类型的成员变量 in。 也可以查看 菜鸟教程的装饰器模式