动态地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类更为灵活。

                                                ——《设计模式》GoF

作用:在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

比如,我们现在想设计一个日志类,记录DB日志(或文本日志),要求能够对日志的优先级 和 错误级别进行记录。
也就是对这个日志类添加了记录错误级别 和 优先级的功能。后续还可能增加其他的功能。

 
public interface Log {
public void write(String logContent);
}

public class DatabaseLog implements Log{

    @Override
public void write(String logContent) {
System.out.println("记录DB-Log:" + logContent);
} }
public class TextFileLog implements Log{

    @Override
public void write(String logContent) {
System.out.println("记录TextFile-Log:" + logContent);
} }

用于给日志类添加新功能的LogWrapper类:

public abstract class LogWrapper implements Log{
protected Log log; @Override
public void write(String logContent) {
log.write(logContent);
} }
public class LogErrorWrapper extends LogWrapper{
private String errorLevel; public LogErrorWrapper(Log log) {
super();
this.log = log;
} public String getErrorLevel() {
return errorLevel;
} public void setErrorLevel(String errorLevel) {
this.errorLevel = errorLevel;
} @Override
public void write(String logContent) {
logErrorLevel(errorLevel);
super.write(logContent);
} private void logErrorLevel(String errorLevel) {
System.out.println("错误级别:" + errorLevel);
} }
public class LogPriorityWrapper extends LogWrapper{
private String priority; public LogPriorityWrapper(Log log) {
super();
this.log = log;
} public String getPriority() {
return priority;
} public void setPriority(String priority) {
this.priority = priority;
} @Override
public void write(String logContent) {
logPriority(priority);
super.write(logContent);
} private void logPriority(String priority) {
System.out.println("优先级:" + priority);
} }

客户端调用:

public class Client {

    public static void main(String[] args) {
Log log = new DatabaseLog(); // DB-log
LogErrorWrapper errorWrapper = new LogErrorWrapper(log);// 1级错误级别 DB-log
LogPriorityWrapper priorityWrapper = new LogPriorityWrapper(errorWrapper); // 特别优先 1级错误级别 DB-log errorWrapper.setErrorLevel("1级错误");
priorityWrapper.setPriority("特别优先"); priorityWrapper.write("Hello World!");
}
}

输出结果:

优先级:特别优先
错误级别:1级错误
记录DB-Log:Hello World!

从上面的代码,我们可以看出,DatabaseLog类只拥有最简单的日志打印功能,而LogWrapper类实现了Log且持有Log的引用,而LogWrapper的子类对Log的功能进行了不同的扩展。而且这些扩展的功能对Log是完全透明的。

从调用处,我们可以看到,在LogWrapper(Decorator)子类的构造器中,我们除了传入Log的子类外,同样可以传入LogWrapper自己的子类,这样装饰的功能就能够进行叠加。这也是为什么我们让Decorator抽象类实现Log接口的原因。

因此,如果现在需要加一个功能,我们只需要添加一个功能的装饰子类就可以了,不需要添加其它的子类。它好就好在拥有运行时的灵活性,可以在需要用时随意组合功能,而不需要静态地把各种功能组合写死在代码中。

 

Decorator装饰模式的更多相关文章

  1. C++设计模式-Decorator装饰模式

    Decorator装饰模式作用:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活. UML图如下: Component是定义一个对象接口,可以给这些对象动态地添加职责. ...

  2. c++ 设计模式6 (Decorator 装饰模式)

    4. “单一职责”类模式 在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任. 典型模式代表: Decorato ...

  3. 设计模式09: Decorator 装饰模式(结构型模式)

    Decorator 装饰模式(结构型模式) 子类复子类,子类何其多加入我们需要为游戏中开发一种坦克,除了不同型号的坦克外,我们还希望在不同场合中为其增加以下一种多种功能:比如红外线夜视功能,比如水路两 ...

  4. 设计模式C++学习笔记之十三(Decorator装饰模式)

      装饰模式,动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator模式相比生成子类更为灵活. 13.1.解释 main(),老爸 ISchoolReport,成绩单接口 CFourt ...

  5. Decorator - 装饰模式

    1. 概述 若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一个基本特性.如果已经存在的一个类缺少某些方法,或者须要给方法添加更多的功能(魅力),你也许会仅仅继 ...

  6. 设计模式学习笔记——Decorator装饰模式

    装饰模式的作用或动机就是,尽量避免继承,而使用关联.原因是层层继承下来,内容会越来越多,有失控的危险.就扩展性而言,用关联比用继承好.所谓的关联,A使用了B,就叫A关联了B. Component 抽象 ...

  7. 设计模式学习之路——Decorator装饰模式(结构模式)

    子类复子类,子类何其多 假如我们需要为游戏中开发一种坦克,除了各种不同型号的坦克外,我们还希望在不同场合中为其增加以下一种或多种功能:比如红外线夜视功能,比如水陆两栖功能,比如卫星定位功能等等. 动机 ...

  8. .NET设计模式(10):装饰模式(Decorator Pattern)(转)

    概述 在软件系统中,有时候我们会使用继承来扩展对象的功能,但是由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性:并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多 ...

  9. .NET设计模式(10):装饰模式(Decorator Pattern)

      .NET设计模式(10):装饰模式(Decorator Pattern)   装饰模式(Decorator Pattern) --.NET设计模式系列之十 年月..在....对于..由于使用装饰模 ...

随机推荐

  1. mysql中的semi-join

    1. 背景介绍 什么是semi-join? 所谓的semi-join是指semi-join子查询. 当一张表在另一张表找到匹配的记录之后,半连接(semi-jion)返回第一张表中的记录.与条件连接相 ...

  2. mvn使用问题

    http://mirrors.ibiblio.org/maven2/org/apache/maven/archetypes/ http://blog.csdn.net/u011340807/artic ...

  3. SignalR的安装

    介绍 SignalR 是 ASP.NET 团队正在开发的一个 Microsoft .NET Framework 库和 jQuery 插件,可能包括在以后版本的 ASP.NET 平台中. 它提供了一些前 ...

  4. 【总结】IE和Firefox的Javascript兼容性总结(转)

    文章转自:http://www.cnblogs.com/wiky/archive/2010/01/09/IE-and-Firefox-Javascript-compatibility.html 长久以 ...

  5. mysql将字符转换成数字

    在操作mysql时,经常需要将字符转换成数字,这一步虽然简单,但不常用的话也很容易忘记,现将在网上找到的方法记录如下: 1.将字符的数字转成数字,比如'0'转成0可以直接用加法来实现例如:将pony表 ...

  6. Dynamics CRM4.0 和 Dynamics CRM2011 Plugin 实现一样的功能的方法的比较

    1.给类型赋值不同 CRM4 plugin给lookup赋值为空 : Lookup lookupnull = new Lookup(); lookupnull.IsNull = true; looku ...

  7. 简单了解ddos攻击

    1.一种为流量攻击,主要是针对网络带宽的攻击,即大量攻击包导致网络带宽被阻塞,合法网络包被虚假的攻击包淹没而无法到达主机: 2.另一种为资源耗尽攻击,主要是针对服务器主机的攻击,即通过大量攻击包导致主 ...

  8. wap图片滚动特效_无css3 元素js脚本编写

    手机图片滑动切换,网上有很多这样的例子,但都借助于其他组件,让代码混乱的不行:还有就是用到css3里的 transform:translate(x,y);移动元素,不过发现在不支持css3的设备上马上 ...

  9. Python基础教程【读书笔记】 - 2016/7/24

    希望通过博客园持续的更新,分享和记录Python基础知识到高级应用的点点滴滴! 第九波:第9章  魔法方法.属性和迭代器  在Python中,有的名称会在前面和后面都加上两个下划线,这种写法很特别.已 ...

  10. 剑指offer系列19--栈的压入、弹出序列

    题目描述 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序 ...