大话设计模式之装饰模式

装饰模式

装饰模式(Decorator)动态的给一个对象添加一些额外的职责。就增加功能来说,装饰模式比生成子类更为灵活。将类中的装饰功能从类中搬移去除,这样简化了原有的类。

引子

做一个人可以搭配不同服装的系统。
版本一:

package decorator;

public class Person1 {
    private String name;
    public Person1(String name)
    {
        this.name=name;
    }
    public void WearBigTrouser()
    {
        System.out.println(this.name+"穿垮裤");
    }
    public void WearSneakers()
    {
        System.out.println(this.name+"破球鞋");
    }
    public void WearSuit()
    {
        System.out.println(this.name+"穿西装");
    }
    public void WearLeatherShoes()
    {
        System.out.println(this.name+"穿皮鞋");
    }
    public static void main(String[] args) {
        Person1 p1=new Person1("菜菜");
        p1.WearBigTrouser();
        p1.WearLeatherShoes();
    }
}

如果按照这种写法,当我们需要增加一件装扮的时候,就需要去改动Person1这个类,这就违背了 开放-封闭原则,衣服这种需求是会经常变化的,对于这种可能存在变化的功能,我们一般会将它抽象出来,利用继承、多态,使其满足多变的需求。

抽象 皮肤类

package decorator;

public interface Skin {
    public abstract void  MyShow();
}

头盔皮肤

package decorator;

public class HeadSkin implements Skin{

    @Override
    public void MyShow() {
        // TODO Auto-generated method stub
        System.out.println("鸡冠头");
    }

}

全身皮肤

package decorator;

public class ClothesSkin implements Skin {

    @Override
    public void MyShow() {
        // TODO Auto-generated method stub
        System.out.println("金光闪闪的全身");
    }

}

客户端代码:

package decorator;

public class Person2 {
    public String name;
    public Person2(String name)
    {
        this.name=name;
    }
    public void MyClothes()
    {
        System.out.println("这就是我的新衣");
    }
    public static void main(String[] args) {
        Skin s=new HeadSkin();
        s.MyShow();
        Skin s2=new ClothesSkin();
        s2.MyShow();
        Person2 p2=new Person2("菜");
        p2.MyClothes();
        
    }
}

结果:

鸡冠头
金光闪闪的全身
这就是我的新衣

这个时候,如果我们需要增加裤子或者换其他款式的衣服,我们并不需要去修改,暴露原来已经写好的其他衣服的设计的代码,只需 增加一个 Skin的子类 即可。
仔细观察上面代码,会发现一个问题,人和衣服是分开的,感觉就像是 ‘鸡冠头’,‘金光闪闪的全身’,‘这就是我的新衣’ ,这三件 一个一个拼接显示出来, 这样子显然不妥,我们期望的是 能够在 内部组装完毕,然后再显示出来,穿衣,衣服的先后顺序不同,搭配出来的样子也是不同的,所以先后顺序也是有讲究的,我们需要把所需的功能按正确的顺序串联起来进行控制,这就需要我们来学习一个模式--装饰模式

装饰模式

装饰模式 动态的给一个对象添加一些额外的职责,从增加功能来说,装饰模式比生成子类更加灵活。

例子: 不同的身份可能会搭配不同的服装,人的身份,服装都是会改变的。

person类,作为基类,有姓名的属性 和消费的 功能

package decorator;

public abstract class Person {
    protected String name;
    
    public void SetName(String name)
    {
        this.name=name;
    }
    public String GetName()
    {
        return this.name;
    }
    public  String getDescription()
    {
        return this.name+"买了:";
    }
    public   abstract double cost();
    
}

人的身份是不同且多变的,有医生,程序员,还有法师,还有战斗法师等等
青少年 类:

package decorator;

public class Teenager extends Person{

    public Teenager(String name)
    {
        this.name=name;
    }
    @Override
    public double cost() {
        // TODO Auto-generated method stub
        return 0;
    }

}

年轻少女 类

package decorator;

public class YoungLady extends Person{

    public YoungLady(String name)
    {
        this.name=name;
    }
    @Override
    public double cost() {
        // TODO Auto-generated method stub
        return 0;
    }

}

服装 作为装饰类 Decorator,而且不同的衣服多种多样

package decorator;

public abstract  class ClothingDecorator extends Person  {
     public abstract String getDescription();
}

  package decorator;
public abstract class HatDecorator extends Person{
    public abstract String getDescription();
}
package decorator;

public class Shirt extends ClothingDecorator{

    Person person;
    public Shirt(Person person)
    {
        this.person=person;
    }
    @Override
    public String getDescription() {
        // TODO Auto-generated method stub
        return person.getDescription()+"a shirt ";
    }

    @Override
    public double cost() {
        // TODO Auto-generated method stub
        return 100+person.cost();
    }

}

package decorator;

public class Hat extends HatDecorator {

    Person person;
    public Hat(Person person)
    {
        this.person=person;
    }
    @Override
    public String getDescription() {
        // TODO Auto-generated method stub
        return person.getDescription()+"a Hat ";
    }

    @Override
    public double cost() {
        // TODO Auto-generated method stub
        return 50+person.cost();
    }

}

客户端代码:

  package decorator;

public class Person3 {
    
    public static void main(String[] args) {
        Person p1=new Teenager("年轻菜");
        p1=new Shirt(p1);
        p1=new Hat(p1);
        System.out.println(p1.getDescription()+p1.cost());
        
        Person p2=new YoungLady("女仔");
        p2=new Shirt(p2);
        p2=new Hat(p2);
        System.out.println(p2.getDescription()+p2.cost());
    }
    
}

结果:

  年轻菜买了:a shirt a Hat 150.0
女仔买了:a shirt a Hat 150.0

UML类图

大话设计模式之装饰模式_第1张图片
image.png

1、Component是基类。在上面的例子中,person类则是基类。通常是一个抽象类或者一个接口,定义了属性或者方法,方法的实现可以由子类实现或者自己实现。通常不会直接使用该类,而是通过继承该类来实现特定的功能,它约束了整个继承树的行为。比如说,如果Component代表人,即使通过装饰也不会使人变成别的动物。
2、ConcreteComponent是Component的子类,实现了相应的方法,它充当了“被装饰者”的角色。 在上面例子中,YoungLady和Teenager 为‘被装饰者’
3、 Decorator也是Component的子类,它是装饰者共同实现的抽象类(也可以是接口)。比如说,Decorator代表衣服这一类装饰者,那么它的子类应该是T恤、裙子这样的具体的装饰者。 在上面例子中,ClothingDecorator和HatDecorator 则为Decorator
4、ConcreteDecorator是Decorator的子类,是具体的装饰者,由于它同时也是Component的子类,因此它能方便地拓展Component的状态(比如添加新的方法)。每个装饰者都应该有一个实例变量用以保存某个Component的引用,这也是利用了组合的特性。在持有Component的引用后,由于其自身也是Component的子类,那么,相当于ConcreteDecorator包裹了Component,不但有Component的特性,同时自身也可以有别的特性,也就是所谓的装饰。上面例子中,Shirt和Hat 则为ConcreteDecorator

应用

学习了装饰者模式用法、特点以及优缺点后,我们再来看看装饰者模式在实际开发过程的应用。装饰者模式在Java中经常出现的地方就是JavaIO。提到JavaIO,脑海中就冒出了大量的类:InputStream、FileInputStream、BufferedInputStream……等,真是头都大了,其实,这里面大部分都是装饰类,只要弄清楚这一点就容易理解了。我们来看看JavaIO是怎样使用装饰者模式的。
从字符流来分析,我们知道,有两个基类,分别是InputStreamOutputStream,它们也就是我们上面所述的Component基类。接着,它有如下子类:FileInputStreamStringBufferInputStream等,它们就代表了上面所述的ConcreteComponent,即装饰对象。此外,InputStream还有FilterInputStream这个子类,它就是一个抽象装饰者,即Decorator,那么它的子类:BufferedInputStreamDataInputStream等就是具体的装饰者了。那么,从装饰者模式的角度来看JavaIO,是不是更加容易理解了呢?

你可能感兴趣的:(大话设计模式之装饰模式)