设计模式--装饰器(Decorator)模式

1. 意图

装饰器模式的意图是在运行时组合操作的新变化
动态地给一个对象添加一些额外的职责。相比生成子类更加灵活。

装饰模式能够实现动态的为对象添加功能,是从一个对象外部来给对象添加功能。通常给对象添加功能,要么直接修改对象添加相应的功能,要么派生对应的子类来扩展,抑或是使用对象组合的方式。显然,直接修改对应的类这种方式并不可取。在面向对象的设计中,而我们也应该尽量使用对象组合,而不是对象继承来扩展和复用功能。

装饰器模式就是基于对象组合的方式,可以很灵活的给对象添加所需要的功能。装饰器模式的本质就是动态组合。动态是手段,组合才是目的。总之,装饰模式是通过把复杂的功能简单化,分散化,然后再运行期间,根据需要来动态组合的这样一个模式。

2. 示例1:汉堡的组合

下面这个例子有助于理解 装饰的流程和作用
现在需要一个汉堡,主体是鸡腿堡,可以选择添加生菜、酱、辣椒等等许多其他的配料,这种情况下就可以使用装饰者模式。

汉堡基类(被装饰者)

package decorator;

public abstract class Humburger {

    private String name;

    public String getName(){
        return this.name;

    public abstract double getPrice(); 
}

鸡腿堡类(被装饰者的初始状态)

package decorator;

public class ChickenBurger extends Humburger{
    private String name;
    public ChickenBurger() {
        this.name="鸡腿堡";
    }
    @Override
    public double getPrice(){
        return 10;
    }

    @Override
    public String getName(){
        return this.name;
    }
}

配料的基类(装饰者,用来对汉堡进行多层装饰,每层装饰增加一些配料)

package decorator;

public abstract class Condiment extends Humburger{

    public abstract String getName();
}

生菜(装饰者的第一层)

package decorator;

public class Lettuce extends Condiment{
    private Humburger hum;
    Lettuce(Humburger hum){
        this.hum=hum;
    }

    @Override
    public String getName(){
        return hum.getName()+ " 加生菜";
    }

    @Override
    public double getPrice(){
        return hum.getPrice()+1.5;
    }
}

辣椒(装饰者的第二层)

package decorator;

public class Chilli extends Condiment{
    private Humburger hum;

    public Chilli(Humburger hum) {
        this.hum=hum;
    }

    @Override
    public String getName(){
        return hum.getName()+" 加辣椒";
    }

    @Override
    public double getPrice(){
        return hum.getPrice(); //辣椒免费
    }
}

测试

package decorator;

public class Test {

    public static void main(String[] args) {

        Humburger hum = new Chilli(new Lettuce(new ChickenBurger()));
        System.out.println(hum.getName());
        System.out.println(hum.getPrice());     
    }
}

//结果
鸡腿堡 加生菜 加辣椒
11.5

3. 结构及角色

这里写图片描述

Component:组件对象的接口,可以给这些对象动态的添加职责;

ConcreteComponent:具体的组件对象,实现了组件接口。该对象通常就是被装饰器装饰的原始对象,可以给这个对象添加职责;

Decorator:所有装饰器的父类,需要定义一个与组件接口一致的接口(主要是为了实现装饰器功能的复用,即具体的装饰器A可以装饰另外一个具体的装饰器B,因为装饰器类也是一个Component),并持有一个Component对象,该对象其实就是被装饰的对象。如果不继承组件接口类,则只能为某个组件添加单一的功能,即装饰器对象不能在装饰其他的装饰器对象。

ConcreteDecorator:具体的装饰器类,实现具体要向被装饰对象添加的功能。用来装饰具体的组件对象或者另外一个具体的装饰器对象。

4. 示例2:人的行为的组合

组件接口

package decorator;

public interface Human {

    public void wearClothes();
    public void walk();

}

具体实现接口的对象

package decorator;

public class Person implements Human{

    @Override
    public void wearClothes() {
        System.out.println("穿什么呢");     
    }

    @Override
    public void walk() {
        System.out.println("去哪里呢");     
    }

}

装饰器接口(抽象类)

package decorator;

public abstract class Decorator implements Human{

    protected Human human;

    public Decorator(Human human){
        this.human = human;
    }

    public void wearClothes(){
        human.wearClothes();
    }

    public void walk(){
        human.walk();
    }
}

装饰器的具体实现对象A、B、C…

package decorator;

public class Decorator_first extends Decorator{

    public Decorator_first(Human human) {
        super(human);       
    }

    public void goRoom(){
        System.out.println("进房子...");
    }
    public void findMap(){
        System.out.println("找地图...");
    }

    @Override
    public void wearClothes(){
        super.wearClothes();
        goRoom();
    }

    @Override
    public void walk(){
        super.walk();
        findMap();
    }
}
package decorator;

public class Decorator_two extends Decorator{

    public Decorator_two(Human human) {
        super(human);
    }

    public void goClothesPress(){
        System.out.println("去衣柜找找...");
    }

    public void findPlaceOnMap(){
        System.out.println("在Map上找找...");
    }

    @Override
    public void wearClothes(){
        super.wearClothes();
        goClothesPress();
    }

    @Override
    public void walk(){
        super.walk();
        findPlaceOnMap();
    }
}
package decorator;

public class Decorator_three extends Decorator{

    public Decorator_three(Human human) {
        super(human);
    }

    public void findClothes(){
        System.out.println("找到一件衣服");
    }

    public void findTarget(){
        System.out.println("找到目的地");
    }

    @Override
    public void wearClothes(){
        super.wearClothes();
        findClothes();
    }

    @Override
    public void walk(){
        super.walk();
        findTarget();
    }
}

测试

package decorator;

public class TestHuman {

    public static void main(String[] args) {
        Human human = new Person();
        //链式调用
        Decorator decorator = new Decorator_three(
                new Decorator_two(new Decorator_first(human)));
        decorator.wearClothes();
        decorator.walk();
    }
}
//结果
穿什么呢
进房子...
去衣柜找找...
找到一件衣服
去哪里呢
找地图...
在Map上找找...
找到目的地

5. 何时使用

在以下两种情况下可以考虑使用装饰器模式:

(1)需要在不影响其他对象的情况下,以动态、透明的方式给对象添加职责。

(2)如果不适合使用子类来进行扩展的时候,可以考虑使用装饰器模式。

6. java I/O

java中的IO是明显的装饰器模式的运用。FilterInputStreamFilterOutputStreamFilterReadFilterWriter分别为具体装饰器的父类,相当于Decorator类,它们分别实现了InputStreamOutputStreamReaderWriter类(这些类相当于Component,是其他组件类的父类,也是Decorator类的父类)。

继承自InputStreamOutputStreamReaderWriter这四个类的其他类是具体的组件类,每个都有相应的功能,相当于ConcreteComponent类。而继承自FilterInputStreamFilterOutputStreamFilterReadFilterWriter这四个类的其他类就是具体的装饰器对象类,即ConcreteDecorator类。通过这些装饰器类,可以给我们提供更加具体的有用的功能。如FileInputStreamInputStream的一个子类,从文件中读取数据流,BufferedInputStream是继承自FilterInputStream的具体的装饰器类,该类提供一个内存的缓冲区类保存输入流中的数据。我们使用如下的代码来使用BufferedInputStream装饰FileInputStream,就可以提供一个内存缓冲区来保存从文件中读取的输入流。
图解如下:

①组件接口,相当于Component:

    InputStream
    OutputStream
    Reader
    Writer

②组件的实现类,相当于ConcreteComponent

    FileInputStream

③装饰器接口,相当于Decorator

    FilterInputStream
    FilterOutputStream
    FilterRead
    FilterWriter

④装饰器类

    BufferedInputStream继承自FilterInputStream

使用说明:我们可以用BufferedInputStream修饰FileInputStream

如:

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); 
//其中file为某个具体文件的File或者FileDescription对象

再如:

FileWriter file = new FileWriter("sample.txt");
BufferedWriter writer = new BufferedWriter(file);
writer.write("a small amount of sample text");
writer.newLine();
writer.close();

特点:上述例子的特点是从一个流组合另一个流。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值