连载:面向对象葵花宝典:思想、技巧与实践(40) - DECORATOR模式

掌握了设计模式之道后,我们将以全新的方法来理解设计模式,这个方法更简单、更直观,不信?看几个样例就知道了

=====================================================================

DECORATOR模式(以设计模式之道来理解)

【业务】

假设你进入了一个信息安全管理非常严格的公司,这家公司不允许员工自行打印文档,所有的文档打印都需要交给文档打印系统统一管理。文档打印系统会记录每次打印的时间、内容、打印人员。。。。。。等等,以便后续出现问题的时候进行追查。

 

由于公司有很多的部门,每个部门的安全要求并不完全一样,同时每个部门关于文档打印也有自己的一些规定。

 

我们的任务就是要开发一套能够支持整个公司文档打印需求的系统。

 

【发现变化】

文档打印系统面对的变化主要体现在:文档打印要求是变化的,不同的部门有不同的要求,同一个部门也可能修改自己的打印需求。

 

例如:

A部门是一个战略规划的部门,里面的资料都非常重要,打印的时候需要在页眉位置打印“绝密”,在页脚的位置打印“密级申明”,同时要加上“绝密文档”的水印;

B部门是内部培训部门,打印培训材料的时候需要在页眉位置打印“内部公开”,但不需要密级申明,同时加上“培训资料”的水印

C部门是对外宣传部门,打印宣传材料的时候只需要加上“公司logo”的水印;

 

【传统方法】

传统方法使用类继承来封装打印请求,为每个部门创建一个打印的子类。详细示例代码如下:

PrintTask.java -- 文档打印系统开发小组负责维护

package com.oo.designpattern.decorator;

/**
 * 打印任务类
 *
 */
abstract public class PrintTask {

    abstract public void print(String text);

}

SecretPrint.java -- 文档打印系统开发小组负责维护:

package com.oo.designpattern.decorator;

/**
 * 绝密文档的打印
 *
 */
public class SecretPrint extends PrintTask{

    @Override
    public void print(String text) {
        Printer.printHeader("绝密");
        Printer.printText(text);
        Printer.printFooter("本文包含绝密信息,请勿泄露!");
        Printer.printTextWaterMark("绝密文档");
    }

}

InternalPrint.java -- 文档打印系统开发小组负责维护:

package com.oo.designpattern.decorator;

/**
 * 内部公开的文档打印
 *
 */
public class InternalPrint extends PrintTask {

    @Override
    public void print(String text) {
        Printer.printHeader("内部公开");
        Printer.printText(text);
        Printer.printTextWaterMark("培训资料");
    }

}

PublicPrint.java -- 文档打印系统开发小组负责维护:

package com.oo.designpattern.decorator;

import java.awt.Image;

/**
 * 对外宣传的文档打印
 *
 */
public class PublicPrint extends PrintTask {

    private Image _logo;
    @Override
    public void print(String text) {
        Printer.printText(text);
        Printer.printImgWaterMark(_logo);
    }

}

文档打印系统实现如下:

PrintServer -- 文档打印系统开发小组负责维护

package com.oo.designpattern.decorator;

/**
 * 文档打印系统
 *
 */
public class PrintServer {

    /**
     * 执行打印任务
     * @param task
     * @param text
     */
    public static void executePrintTask(PrintTask task, String text){
        log();
        task.print(text);
        audit();
    }

    /**
     * 记录日志
     */
    private static void log(){
        //省略具体实现代码
    }

    /**
     * 记录审计相关信息
     */
    private static void audit(){
        //省略具体实现代码
    }
}

定义好不同的打印任务后,每个部门根据自己的需要,选择不同的任务发给文档打印系统。

例如,A部门的打印处理如下:

SecretDepartment.java  -- A部门负责维护

package com.oo.designpattern.decorator;

/**
 * A部门的打印处理
 *
 */
public class SecretDepartment {

    public void print(String text){
        PrintTask task = new SecretPrint();

        //PrintServer即“文档打印系统”
        PrintServer.executePrintTask(task, text);
    }
}

传统方法使用类继承来封装变化的打印需求,当面对变化时,存在如下问题:

1)新增部门的时候,需要文档打印系统提供一个新的打印任务类,将导致出现大量的***Print类;

例如:新建了一个D部门,D部门只需要打印纯文本即可,那么已有的SecretPrint、InternalPrint、PublicPrint类都无法满足需求,必须新增一个PurePrint的类;

2)某个部门的打印需求变更的时候,需要改变已有的***Print类;

例如:C部门希望在对外宣传材料的页眉上打印公司名称,则需要修改PublicPrint类。

【设计模式方法】

设计模式封装变化的方法就是Decorator模式。Decorator模式定义如下:

“动态的给一个对象添加一些额外的职责”

 

《设计模式》一书中关于Decorator模式的描述并不很直观,我理解Decorator模式为“通过聚合的方式将动态变化的职责组合起来”。

 

我们详细看看Decorator模式是如何封装变化的。

首先,将变化的职责封装为独立的类。传统方式实现中,不同的职责是对应不同的函数调用,而设计模式中,不同的职责是不同的类;

其次,通过聚合将变化的职责组合起来。传统方式中,不同职责的组合是通过在一个函数中写多行代码来体现的,而设计模式中,通过对象的聚合将不同职责组合起来。

【Decorator模式结构】

Component:定义一个对象接口(对应结构图中的operation函数),可以给这些对象动态添加职责

ConcreteComponent:定义一个对象,这个对象是实际的Component,将被Decorator修饰

Decorator:定义修饰对象的接口,Decorator实现的关键在于聚合了一个Component对象

ConcreteDecorator:具体的修饰对象

【代码实现】

使用Decorator设计模式实现的文档打印系统代码如下:

*********************类设计*****************************

 

PrintComponent.java -- 文档打印系统开发小组负责维护

package com.oo.designpattern.decorator2;

/**
 * 打印组件的父类
 *
 */
abstract public class PrintComponent {

    abstract public void print();
}

PrintDecorator.java -- 文档打印系统开发小组负责维护

package com.oo.designpattern.decorator2;

/**
 * 修饰的打印任务,对应Decorator模式中的Decorator
 * Decorator可以聚合ConcreteComponent或者其他Decorator
 * 这样可以使得打印任务能够嵌套执行下去,直到最后完成所有打印任务
 *
 */
public abstract class PrintDecorator extends PrintComponent {

    abstract public void print();
}

TextComponent.java -- 文档打印系统开发小组负责维护

package com.oo.designpattern.decorator2;

import com.oo.designpattern.decorator.Printer;

/**
 * 文本打印,对应Decorator模式中的ConcreteComponent
 * 打印任务到ConcreteComponent就算真正完成了
 *
 */
public class TextComponent extends PrintComponent {

    private String _text;
    TextComponent(String text){
        this._text = text;
    }

    @Override
    public void print() {
        Printer.printText(this._text);
    }
}

HeaderDecorator.java -- 文档打印系统开发小组负责维护

package com.oo.designpattern.decorator2;

import com.oo.designpattern.decorator.Printer;

/**
 * 页眉打印
 *
 */
public class HeaderDecorator extends PrintDecorator {

    private PrintComponent _comp; //被修饰的打印组件
    private String _text;         //需要打印的页眉内容

    /**
     * 初始化的时候,必须包含其它打印组件comp,这是实现Decorator模式的前提
     * 同时也需要指定当前组件所需的参数,不能在print函数的参数中指定,
     * 因为每个Decorator所需的参数是不一样的
     * @param comp
     * @param text
     */
    HeaderDecorator(PrintComponent comp, String text) {
        this._comp = comp;
        this._text = text;
    }

    /**
     * 打印
     */
    @Override
    public void print() {

        //打印的时候将当前Decorator和被修饰的Component分开,这是Decorator模式的关键
        Printer.printHeader(_text);  //打印页眉

        //_comp本身如果是Decorator,就会嵌套打印下去
        //_comp本身如果不是Decorator,而是ConcreteComponent,则打印任务到此结束
        _comp.print();
    }
}

FooterDecorator.java

package com.oo.designpattern.decorator2;

import com.oo.designpattern.decorator.Printer;

/**
 * 页脚打印,和页眉打印类似,此处省略相同的注释代码
 *
 */
public class FooterDecorator extends PrintDecorator {

    private PrintComponent _comp;
    private String _text;         

    FooterDecorator(PrintComponent comp, String text) {
        this._comp = comp;
        this._text = text;
    }

    /**
     * 打印
     */
    @Override
    public void print() {

        Printer.printFooter(_text);  //打印页脚

        _comp.print();
    }
}

TextWatermarkDecorator.java

package com.oo.designpattern.decorator2;

import com.oo.designpattern.decorator.Printer;

/**
 * 文本水印打印,和页眉打印类似,此处省略相同的注释代码
 *
 */
public class TextWatermarkDecorator extends PrintDecorator{

    private PrintComponent _comp;
    private String _text;         

    TextWatermarkDecorator(PrintComponent comp, String text) {
        this._comp = comp;
        this._text = text;
    }

    /**
     * 打印
     */
    @Override
    public void print() {

        Printer.printTextWaterMark(_text);  //打印文本水印

        _comp.print();
    }

}

ImgWatermarkDecorator.java

package com.oo.designpattern.decorator2;

import java.awt.Image;

import com.oo.designpattern.decorator.Printer;

/**
 * 图片水印打印,和页眉打印类似,此处省略相同的注释代码
 *
 */
public class ImgWatermarkDecorator extends PrintDecorator {

    private PrintComponent _comp;
    private static Image _logo; //图片水印只能是公司logo        

    ImgWatermarkDecorator(PrintComponent comp) {
        this._comp = comp;
    }

    /**
     * 打印
     */
    @Override
    public void print() {

        Printer.printImgWaterMark(ImgWatermarkDecorator._logo);  //打印图片水印

        _comp.print();
    }

}

PrintServer.java 

package com.oo.designpattern.decorator2;

public class PrintServer {

    /**
     * 执行打印任务
     * @param comp
     */
    public static void executePrintTask(PrintComponent comp){
        log();
        comp.print();
        audit();
    }

    /**
     * 记录日志
     */
    private static void log(){
        //省略具体实现代码
    }

    /**
     * 记录审计相关信息
     */
    private static void audit(){
        //省略具体实现代码
    }
}

*********************类使用*****************************

 

A部门的打印处理如下(如下代码请仔细阅读,特别是注释部分):

SecretDepartment.java  -- A部门负责维护

package com.oo.designpattern.decorator2;

/**
 * A部门的打印处理,注意与传统方法中的SecretDepartment类对比
 *
 */
public class SecretDepartment {

    /**
     * 打印任务1,对应传统方式的SecretePrint类
     * @param text
     */
    public void print(String text){

        /**
         * 使用Decorator设计模式后,打印任务不再是一个单独的类SecretPrint类,
         * 而是通过将多个打印项目聚合成一个打印任务
         */
        PrintComponent textComp = new TextComponent(text);

        //注意header聚合了textComp
        PrintDecorator header = new HeaderDecorator(textComp, "绝密");
        //注意footer聚合了header,而不是textComp,这样就能够嵌套执行下去
        PrintDecorator footer = new FooterDecorator(header, "本文包含绝密信息,请勿泄露!");
        //注意watermark聚合了footer,而不是textComp,这样就能够嵌套执行下去
        PrintDecorator watermark = new TextWatermarkDecorator(footer, "绝密文档");

        //PrintServer即“文档打印系统”,与传统的PrintServer相比,这里不需要知道打印的text内容
        //text内容已经封装到TextComponent中去了(对应代码行14行)
        PrintServer.executePrintTask(watermark);  //注意这里传递给打印系统的是最后一个Decorator对象watermark
    }

    /**
     * A部门的第二个打印任务,将文本水印改为图片水印,并且不再打印页脚
     * @param text
     */
    public void print2(String text){

        /**
         * 新增打印任务,无需文档管理系统增加新的类,只要A部门自己修改代码即可
         */
        PrintComponent textComp = new TextComponent(text);

        //注意header聚合了textComp
        PrintDecorator header = new HeaderDecorator(textComp, "绝密");
        //注意watermark聚合了header,而不是textComp,这样就能够嵌套执行下去
        PrintDecorator watermark = new ImgWatermarkDecorator(header);

        PrintServer.executePrintTask(watermark);
    }
}

可以看到,使用了设计模式的方法后,打印业务的变化,可以通过类似数学上的排列组合已有的打印功能来完成,而不再需要新的打印类了。

================================================ 
转载请注明出处:http://blog.csdn.net/yunhua_lee/article/details/38865605
================================================ 

时间: 2024-09-17 03:08:11

连载:面向对象葵花宝典:思想、技巧与实践(40) - DECORATOR模式的相关文章

连载:面向对象葵花宝典:思想、技巧与实践(28) - 设计原则:内聚&耦合

前面通过实例讲解了一个一环扣一环的面向对象的开发流程:用例模型 -> 领域模型 -> 设计模型(类模型 + 动态模型),解答了面向对象如何做的问题.接下来我们就要讲"如何做好面向对象设计"的技巧了 =================================================================== [内聚] 参考维基百科的解释,内聚的含义如下: cohesion refers to the degree to which the eleme

连载:面向对象葵花宝典:思想、技巧与实践(1) - 程序设计思想的发展

史前时代:面向机器 最早的程序设计都是采用机器语言来编写的,直接使用二进制码来表示机器能够识别和执行的指令和数据.简单来说,就是直接编写0和1的序列来代表程序语言.例如:使用0000 代表 加载(LOAD),0001 代表 存储(STORE)等.  机器语言由机器直接执行,速度快,但一个很明显的缺点就是:写起来实在是太困难了,一旦你发现自己写错了,改起来更蛋疼!这样直接导致程序编写效率十分低下,编写程序花费的时间往往是实际运行时间的几十倍或几百倍.  有一个关于机器语言和比尔盖茨的笑话,是说比尔

013_《Delphi面向对象编程思想》

<Delphi面向对象编程思想> Delphi 教程 系列书籍 (013) <Delphi面向对象编程思想> 网友(邦)整理 EMail: shuaihj@163.com 下载地址: Pdf 作者: 刘艺 [作译者介绍] 丛书名: Borland核心技术丛书 出版社:机械工业出版社 ISBN:7111127722 上架时间:2003-10-10 出版日期:2003 年9月 开本:16开 页码:476 版次:1-1 内容简介 这是一本纯粹讨论dlephi面向对象编程的力作. 本书以精

Silverlight游戏设计:(五)面向对象的思想塑造游戏对象

传说,面向对象的开发模式最初是因为程序员偷懒而不小心诞生的.发展至今,人们从最初的热忠于 讨论某某语言是否足够面向对象到现在开始更广泛的关注面向对象的思想而不是具体内容.面向对象的思 想其实并不深奥,它存在的目的只有一个:让程序开发更贴近我们的现实世界. 还记得猫.猫叫:狗.狗吃东西吗?无数的程序员都喜欢将此类似的情形设计当作面向对象最好的例 子.是的,非常生动且形象:但实际运用中你是否能真正做到举一反三? 回述到游戏设计中,大家是否时常会感觉游戏世界与我们的真实世界如此贴近?游戏中的精灵好比我

java语言学习002_面向对象编程思想

      人类在认识世界时,为了方便自己和智慧提升,很自然的对事物进行了分类.对世界进行了抽象,若把所有各个事物看做对象,纵观所有对象,这些对象具有各自的或共有的特征,并且又有共有的或各自的的能力,这样就可以对具有相同一些特征和一些能力的事物进行了归类.       比如,车,有汽车,火车他们都有哪些属性?                  汽车,特征:长度,颜色,速度,轮胎,载重,平面行走--能力:移动,载东西,--                  火车,特征:长度,颜色,速度,轮胎,载重

求助、面向对象的思想查找字符串中的数字

问题描述 要用面向对象的思想来查找字符串中的数字.实现判断某个字符是否位数字的方法如下:publicstaticboolgetNumeric(stringstr){boolb=false;string[]ArrayInt=newstring[]{"1","2","3","4","5","6","7","8","9","

将面向对象的思想带入TC

写TC貌似是很简单的工作,但当动手写的时候往往会出现,不知道写什么,又感觉有一堆的东西需要写,即使一个简单的日常也会觉得里面的逻辑非常复杂,然后就是晕得不知所向. 个人认为,写TC没有固定的模式,也没有唯一的答案,每个人的方式不同,习惯不同,TC中的如何分类归纳也就自然不相同.但目标是一致的,基本目标是覆盖需求.无盲区:加强目标是加深测试点,完善用户友好性等. 下面分享下我写TC的几种思路. 第一种思路--先对象,后流程 面向对象是在平常入门学习中 首先接触到的概念,它不仅仅存在于代码的编写中,

重温面向对象的思想——构造器和重载

1.this关键字 this表示这个对象的参考名称:例如this.age1=age2;表示将age2的值,赋值给这个对象的私有属性age1. 2. .重温面向对象的思想--构造器和重载 构造器:创建一个对象时,有时候需要对在实例化一个对象时,对这个对象进行初始化,这个时候我们就需要构造方法来进行这种初始化. 重载:当这种初始化需要按照不同的语境,不同的参数的构造器来进行初始化. 总结--方法的重载是多种构造器,用以完成不同的初始化. -注意:构造器==构造方法,两者一样 3.构造方法和自定义方法

重温面向对象的思想OOP——封装

面向对象的思想OOP--封装 对象的封装: 封装的目的:隐藏对象的内部细节,将对象进行黑箱操作.用户是不知道对象的内部细节的,同样的道理,用户也不知道你定义的方法. 说明:一个对象(类)的属性,这个属性必须一定是私有属性,用户一定不能直接获取得到属性,必须通过你定义的工程来操作数据(设置.得到), 标准格式:通过定义私有属性的Set方法和Get方法来分别设置.得到私有属性. package demo; public class Student { private double schoolCod

艾伟:WCF从理论到实践(9):实例模式和对象生命周期

本系列文章导航 WCF从理论到实践(1):揭开神秘面纱 WCF从理论到实践(2):决战紫禁之巅 WCF从理论到实践(3):八号当铺之黑色契约 WCF从理论到实践(4):路在何方 WCF从理论到实践(5):Binding细解 WCF从理论到实践(6):WCF架构 WCF从理论到实践(7):消息交换模式 WCF从理论到实践(8):事件广播 WCF从理论到实践(9):实例模式和对象生命周期 WCF从理论到实践(10):异常处理 WCF从理论到实践(11)-异步 WCF从理论到实践(12):事务 WCF