装饰器模式与java.io包

       Decorator设计模式是典型的结构型模式(在GOF的那本模式的Bible中将模式分为:1.建立型模式;2.结构型模式;3.行为模式三种)。它的主要用意是:动态地为对象添加一些额外的功能。(记住上面两种颜色的词汇,理解装饰器模式的精髓所在!)下面是GOF的《Element of reusable Object-Oriented Software》中对Decorator用意的概述:java

Decorator Pattern――Attaches additional responsibilities to an object dynamically . Decorators provide a flexible alternative to subclassing for extending functionality .设计模式

什么时候须要使用装饰器模式

       GOF的那本Bible中关于装饰器模式列举的是一个文本组件与边框的例子(在这里我就不举了,主要是由于我会在书中举一个类似的,但却很是有说服力的例子,它对Swing中的某些原本应该使用Decorator却没有使用的对象的改进。同时会提出内包装、外包装的概念。看到这个例子后你们仔细体会吧!经过例子告诉你们一点:任何设计不是一成不变的、模式的应用是极其灵活的……)。下面我举一个“三明治”的例子!编辑器

       很 多人都吃过三明治(我除外!“没吃过猪肉,俺可听过猪叫”),都会知道三明治必不可少的是两块面包片,而后能够在夹层里加上蔬菜、沙拉、咸肉等等,外面可 以涂上奶油之类的。假如如今你要为一个三明治小店构造一个程序,其中要设计各类三明治的对象。可能你已经建立了一个简单的Sandwich对象,如今要产生带蔬菜的就是继承原有的Sandwich添加一个蔬菜的成员变量,看起来很“正点”的作法,之后我还要带咸肉的、带奶油的、带蔬菜的又分为带青菜的、带芹菜的、生菜的……仍是一个一个继承是吧!假如咱们还须要即带蔬菜又带其它肉类,设置咱们还要求这些添加成分的任意组合,那你就慢慢继承吧!ide

       读过几年书的会下面这个算术,咱们有n种成分,在作三明治的时候任意搭配,那么有多少种方案呢?!算算吧!你会有惊人的发现。N种成分,什么都不要是Cn0种方案吧!要1种是Cn1吧!…..n种是Cnn吧!加起来不就是吗?Cn0+Cn1+……+Cnn-1+Cnn还不会啊!牛顿莱布尼兹公式记得吧!(惋惜Word的公式编辑器安装不了)总共2n次方案。有可能前面10天写了K个类,老板让你再加一种成分你就得再干10天,下一次再加一种你可得干20天哦!同时你能够发现你的类库急剧地膨胀!(老板可能会说你:XXXK天你加了n个成分,怎么如今这么不上进呢?后K天只加了1个成分啊?!!可能你会拿个比给老板算算,老板那么忙会睬你吗?!有可能你的老板会说:无论怎么样我就要你加,K天你还给我加n个成分!!呵呵,怎么办啊!跳槽啊!跳槽了也没人要你!!人家一看就知道你没学设计模式)。下面咱们就使用装饰器模式来设计这个库吧!下图是咱们的设计图:工具

       下面是以上各个类的意义:测试

1.Ingredient(成分):全部类的父类,包括它们共有的方法,通常为抽象类且方法都有默认的实现,也能够为接口。它有BreadDecorator两个子类。这种实际不存在的,系统须要的抽象类仅仅表示一个概念,图中用红色表示。flex

2. Bread(面包):就是咱们三明治中必须的两片面包。它是系统中最基本的元素,也是被装饰的元素,和IO中的媒质流(原始流)一个意义。在装饰器模式中属于一类角色,因此其颜色为紫色。this

3. Decorator(装饰器):全部其它成分的父类,这些成分能够是猪肉、羊肉、青菜、芹菜。这也是一个实际不存在的类,仅仅表示一个概念,即具备装饰功能的全部对象的父类。图中用蓝色表示。spa

4.Pork(猪肉):具体的一个成分,不过它做为装饰成分和面包搭配。设计

5. Mutton(羊肉):同上。

6. Celery(芹菜):同上。

7. Greengrocery(青菜):同上。

总结一下装饰器模式中的四种角色:1.被装饰对象(Bread);2.装饰对象(四种);3.装饰器(Decorator);4.公共接口或抽象类(Ingredient)。其中12是系统或者实际存在的,34是实现装饰功能须要的抽象类。

       写段代码体会其威力吧!(程序很简单,可是实现的方法中能够假如如何你须要的方法,意境慢慢体会吧!)

//Ingredient.java
public abstract class Ingredient {

    public abstract String getDescription();

    public abstract double getCost();

    public void printDescription(){
        System.out.println(" Name      "+ this.getDescription());
        System.out.println(" Price RMB "+ this.getCost());
    }
}

       全部成分的父类,抽象类有一个描述本身的方法和一个获得价格的方法,以及一个打印自身描述和价格的方法(该方法与上面两个方法构成模板方法哦!)

//Bread.java
public class Bread extends Ingredient {

    private String description ;

    public Bread(String desc) {
        this.description=desc ;
    }

    public String getDescription() {
        return description ;
    }

    public double getCost() {
        return 2.48 ;
    }
}

       面包类,由于它是一个具体的成分,所以实现父类的全部的抽象方法。描述能够经过构造器传入,也能够经过set方法传入。一样价格也是同样的,我就很简单地返回了。

//Decorator.java
public abstract class Decorator extends Ingredient {

    Ingredient ingredient ;

    public Decorator(Ingredient igd) {
        this.ingredient = igd;
    }

    public abstract String getDescription();

    public abstract double getCost();
    
}

       装饰器对象,全部具体装饰器对象父类。它最经典的特征就是:1.必须有一个它本身的父类本身的成员变量;2.必须继承公共父类。这是由于装饰器也是一种成分,只不过是那些具体具备装饰功能的成分的公共抽象罢了。在咱们的例子中就是有一个Ingredient做为其成员变量。Decorator继承了Ingredient类。

//Pork.java
public class Pork extends Decorator {

    public Pork(Ingredient igd) {
        super(igd);
    }

    public String getDescription() {
        String base = ingredient.getDescription();
        return base +"\n"+"Decrocated with Pork !";
    }

    public double getCost() {
        double basePrice = ingredient.getCost();
        double porkPrice = 1.8;
        return basePrice + porkPrice ;
    }
}

       具体的猪肉成分,同时也是一个具体的装饰器,所以它继承了Decorator类。猪肉装饰器装饰能够全部的其余对象,所以经过构造器传入一个Ingredient的实例,程序中调用了父类的构造方法,主要父类实现了这样的逻辑关系。一样由于方法是具体的成分,因此getDescription获得了实现,不过因为它是具备装饰功能的成分,所以它的描述包含了被装饰成分的描述和自身的描述。价格也是同样的。价格放回的格式被装饰成分与猪肉成分的种价格哦!

       从上面两个方法中咱们能够看出,猪肉装饰器的功能获得了加强,它不只仅有本身的描述和价格,还包含被装饰成分的描述和价格。主要是由于被装饰成分是它的成员变量,所以能够任意调用它们的方法,同时能够增长本身的额外的共同,这样就加强了原来成分的功能。     

//Mutton.java
public class Mutton extends Decorator {
    public Mutton(Ingredient igd) {
        super(igd);
    }

    public String getDescription() {
        String base = ingredient.getDescription();
        return base +"\n"+"Decrocated with Mutton !";
    }

    public double getCost() {
        double basePrice = ingredient.getCost();
        double muttonPrice = 2.3;
        return basePrice + muttonPrice ;
    }
}

       羊肉的包装器。

//Celery.java
public class Celery extends Decorator {

    public Celery(Ingredient igd) {
        super(igd);
    }

    public String getDescription() {
        String base = ingredient.getDescription();
        return base +"\n"+"Decrocated with Celery !";
    }

    public double getCost() {
        double basePrice = ingredient.getCost();
        double celeryPrice =0.6;
        return basePrice + celeryPrice ;
    }
}

       芹菜的包装器。

//GreenGrocery.java
public class GreenGrocery extends Decorator {
    
    public GreenGrocery (Ingredient igd) {
        super(igd);
    }

    public String getDescription() {
        String base = ingredient.getDescription();
        return base +"\n"+"Decrocated with GreenGrocery !";
    }

    public double getCost() {
        double basePrice = ingredient.getCost();
        double greenGroceryPrice = 0.4;
        return basePrice + greenGroceryPrice;
    }
}

       青菜的包装器。     

       下面咱们就领略装饰器模式的神奇了!咱们有一个测试类,其中创建夹羊肉的三明治、全蔬菜的三明治、全荤的三明治。(感受感受吧!很香的哦!)

public class DecoratorTest {

    public static void main(String[] args) {
        Ingredient compound = new Mutton(new Celery(new Bread("Master24's Bread")));
        compound.printDescription();

        compound = new Celery(new GreenGrocery(new Bread("Bread with milk")));
        compound.printDescription();

        compound = new Mutton(new Pork(new Bread("Bread with cheese")));
        compound.printDescription();
    }
}

       以上就是一个简单的装饰器类!假如你对想中国式的吃法,能够将加入馒头、春卷皮、蛋皮……夹菜能够为肉丝……忽然想到了京酱肉丝。     

装饰器模式的结构

       在谈及软件中的结构,通常会用UML图表示(UMLANTJUnit等都是软件设计中基本的工具,会了没有啊!)。下面是一个咱们常常看到的关于Decorator模式的结构图。

1.Component就是装饰器模式中公共方法的类,在装饰器模式结构图的顶层。

2.ConcreateComponent是转换器模式中具体的被装饰的类,IO包中的媒体流就是此种对象。

3.Decorator装饰器模式中的核心对象,全部具体装饰器对象的父类,完成装饰器的部分职能。在上面的例子中Decorator类和这里的对应。该类能够只作一些简单的包裹被装饰的对象,也能够还包含对Component中方法的实现……他有一个鲜明的特色:继承至Component,同时包含一个Component做为其成员变量。装饰器模式动机中的动态地增长功能是在这里实现的。

4.ConcreteDecoratorAConcreteDecoratorB是两个具体的装饰器对象,他们完成具体的装饰功能。装饰功能的实现是经过调用被装饰对象对应的方法,加上装饰对象自身的方法。这是装饰器模式动机中的添加额外功能的关键。

从上面图中你可能还会发现:ConcreteDecoratorAConcreteDecoratorB的方法不同,这就是通常设计模式中谈及装饰器模式的“透明装饰器”和“不透明装饰器”。“透明装饰器”就是整个Decorator的结构中全部的类都保持一样的“接口”(这里是共同方法的意思),这是一种极其理想的情况,就像餐饮的例子同样。现实中绝大多数装饰器都是“不透明装饰器”,他们的“接口”在某些子类中获得加强,主要看这个类与顶层的抽象类或者接口是否有一样的公共方法。IO中的ByteArrayInputStream就比Inputstrem抽象类多一些方法,所以IO中的装饰器是一个“不通明装饰器”。下面是IO中输入字节流部分的装饰器的结构图。

1. InputStream是装饰器的顶层类,一个抽象类!包括一些共有的方法,如:1.读方法――read3个);2.关闭流的方法――close3.mark相关的方法――markresetmarkSupport4.跳跃方法――skip5.查询是否还有元素方法――available。图中红色的表示。

2.FileInputStreamPipedInputStream…五个紫色的,是具体的被装饰对象。从他们的“接口”中能够看出他们通常都有额外的方法。

3. FilterInputStream是装饰器中的核心,Decorator对象,图中蓝色的部分。

4. DataInputStreamBufferedInputStream…四个是具体的装饰器,他们保持了和InputStream一样的接口。

5.  ObjectInputStreamIO字节输入流中特殊的装饰器,他不是FilterInputStream的子类(不知道Sun处于何种意图不做为FileterInputStream的子类,其中流中也有很多的例子)。他和其余FilterInputStream的子类功能类似均可以装饰其余对象。 

IO包中不只输入字节流是采用装饰器模式、输出字节流、输入字符流和输出字符流都是采用装饰器模式。关于IO中装饰器模式的实现能够经过下面的源代码分析从而了解细节。

3 装饰器模式与适配器模式的比较

     共同点:都拥有一个目标对象。装饰器经过包装一个装饰对象来扩展其功能,而又不改变其接口,这其实是基于对象的适配器模式的一种变种。
     不一样点:适配器模式须要实现另一个接口,而装饰器模式必须实现该对象的接口。适配器模式主要是为了接口的转换,而装饰者模式关注的是经过组合来动态的为被装饰者注入新的功能或行为(即所谓的责任)。 

相关文章
相关标签/搜索