23种设计模式(七)装饰器模式

本文介绍

装饰器模式(Decorator Pattern)

装饰器模式

装饰模式指的是在不必改变原类文件和使用继承的情况下,
动态地扩展一个对象的功能。它是通过创建一个包装对象,
也就是装饰来包裹真实的对象

简介

装饰模式允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计
模式属于结构型模式,它是作为现有的类的一个包装

主要角色

1、抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。

2、具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。

3、装饰(Decorator)角色:持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。

4、具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。

目的

用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能,比生成子类更为灵活

基本的实现思路

将具体功能职责划分,同时继承装饰者模式

实际使用

java IO 流是典型的装饰模式

优缺点

优点

1.、Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性。

2、 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合

缺点

1、这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性

2、装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。

3、装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择

注意事项

适用场景

  1. 需要扩展一个类的功能,或给一个类添加附加职责

  2. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销

  3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。

  4. 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类

与其他模式的区别

1、关于新职责:适配器也可以在转换时增加新的职责,但主要目的不在此。装饰者模式主要是给被装饰者增加新职责的。

2、关于原接口:适配器模式是用新接口来调用原接口,原接口对新系统是不可见或者说不可用的。装饰者模式原封不动的使用原接口,系统对装饰的对象也通过原接口来完成使用。(增加新接口的装饰者模式可以认为是其变种–“半透明”装饰者)

3、关于其包裹的对象:适配器是知道被适配者的详细情况的(就是那个类或那个接口)。装饰者只知道其接口是什么,至于其具体类型(是基类还是其他派生类)只有在运行期间才知道

实现代码

接口

/**
 * @author test
 * @date
 */
public interface Home {
    /**
     * 家庭名称
     * @return
     */
    void homeName();
    /**
     * 家庭地址
     * @return
     */
    void homeAddress();
}

实现

/**
 * @author test
 * @date
 */
public class AmericanHome implements Home {

    @Override
    public void homeName(){
        System.out.println("HomeName:AmericanH");
    }

    @Override
    public void homeAddress(){
        System.out.println("HomeAddress:AmericanH");
    }
}


/**
 * @author test
 * @date
 */
public class ChineseHome implements Home {

    @Override
    public void homeName(){
        System.out.println("HomeName:Chinese");
    }

    @Override
    public void homeAddress(){
        System.out.println("HomeAddress:Chinese");
    }
}

接口的抽象装饰类

/**
 * @author test
 * @date
 */
public abstract class HomeDecorator implements Home {
    protected Home homeDecorator;

    public HomeDecorator(Home homeDecorator){
        this.homeDecorator = homeDecorator;
    }

    @Override
    public void homeName(){
        homeDecorator.homeName();
    }

    @Override
    public void homeAddress(){
        homeDecorator.homeAddress();
    }
}

创建扩展了 抽象装饰类的实体装饰类

/**
 * @author test
 * @date
 */public class NewHomeDecorator extends HomeDecorator {

    public NewHomeDecorator(Home home){
        super(home);
    }

    @Override
    public void homeName(){
        homeDecorator.homeName();
        recordHomeComeFrom(homeDecorator);
    }

    @Override
    public void homeAddress(){
        homeDecorator.homeAddress();
        recordHomeComeFrom(homeDecorator);
    }

    private void recordHomeComeFrom(Home home){
        System.out.println("record come from: 已记录家庭来自的地址");
    }
}

使用 实体装饰类 来装饰对象

/**
 * @author test
 * @date
 */
public class Main {

    public static void main(String[] args) {

        Home home = new ChineseHome();

        Home chineseHome = new NewHomeDecorator(new ChineseHome());

        Home americanHome = new NewHomeDecorator(new AmericanHome());
        System.out.println("Home with normal ");
        home.homeName();
        home.homeAddress();

        System.out.println("\nHome of chinese");
        chineseHome.homeName();
        chineseHome.homeAddress();

        System.out.println("\nHome of american");
        americanHome.homeName();
        americanHome.homeAddress();
    }
}

输出结果

Home with normal 
HomeName:Chinese
HomeAddress:Chinese

Home of chinese
HomeName:Chinese
record come from: 已记录家庭来自的地址
HomeAddress:Chinese
record come from: 已记录家庭来自的地址

Home of american
HomeName:American
record come from: 已记录家庭来自的地址
HomeAddress:American
record come from: 已记录家庭来自的地址

Process finished with exit code 0
 

总结

参考文章

https://baike.baidu.com/item/%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F/10218946?fr=aladdin