【Java设计模式】结构型模式-装饰模式

源代码:https://gitee.com/AgentXiao/DecroatorPattern
要点:
1、装饰模式解决的问题
2、装饰模式的各个模块实现
3、装饰模式的优缺点
4、装饰模式和桥接模式的区别

一、场景引入

场景引入

如上图所示,有一个狗(Dog)类,下面有三个子类,分别是会飞的狗(FlyDog)、会游泳的狗(SwimDog)、会跑的狗(RunDog)。假设此时三个子类都是单一功能的,也就说FlyDog只会飞,游泳和跑步都不会。

如果此时需要一个既会飞也会跑的狗,我们可以通过继承的方式实现,一次类推,可以得到:

继承具有新功能的狗

这显然不是最好的方法!当功能非常多时,需要继承父类的子类数量将会大大增加!而装饰模式就是解决这个问题的一种设计模式。

二、装饰模式

装饰模式是一种用于代替继承的技术,无需通过继承增加子类就能够扩展对象的新功能。使用对象的组合关系(区分于“组合设计模式”)代替继承关系,避免类型体系的快速膨胀。

简单地说,装饰模式用于动态增加一个对象的新功能,或成为功能增强。

装饰模式实现细节:

装饰模式实现细节.png

(1)抽象构建角色Component。这是真实对象和装饰对象都需要实现的接口,便于客户端可以使用相同的方式交互装饰对象和真实对象,指上文的Dog。
(2)真实对象ConcreteComponent。具体需要装饰的对象,定义为MyDog。
(3)装饰对象Decorator。持有一个抽象构件的引用(核心)。装饰对象接受所有客户端的请求,并把这些请求通过引用转发给真实对象。这样,就能在真实对象调用前后增加新的功能,定义为SuperDog。
(4)具体装饰对象ConcreteDecorator:指上文的Fly、Swim、Run。

三、装饰模式的实现

1、抽象角色

/**
 * @InterfaceName Dog
 * @Description 抽象构建角色
 * @Author xwd
 * @Date 2018/10/25 9:52
 */
public interface Dog {
    /**
     * @MethodName showPower
     * @Descrition 展示能力
     * @Param []
     * @return void
     */
    void showPower();
}

2、具体角色

/**
 * @ClassName MyDog
 * @Description 具体对象
 * @Author xwd
 * @Date 2018/10/25 9:53
 */
public class MyDog implements Dog{
    @Override
    public void showPower() {
        System.out.println("我还没有什么能力!");
    }
}

3、装饰角色(核心)

/**
 * @ClassName SuperDog
 * @Description 装饰对象
 * @Author xwd
 * @Date 2018/10/25 9:54
 */
public class SuperDog implements Dog{
    protected Dog dog;//持有抽象构建角色的引用

    public SuperDog(Dog dog) {
        this.dog = dog;
    }

    @Override
    public void showPower() {
        dog.showPower();
    }
}

4、具体的装饰角色

/**
 * @ClassName Fly
 * @Description 飞行能力
 * @Author xwd
 * @Date 2018/10/25 9:57
 */
public class Fly extends SuperDog{
    public Fly(Dog dog) {
        super(dog);
    }

    @Override
    public void showPower() {
        super.showPower();
        System.out.println("我具备飞行功能!");
    }
}
/**
 * @ClassName Run
 * @Description 奔跑能力
 * @Author xwd
 * @Date 2018/10/25 9:57
 */
public class Run extends SuperDog{
    public Run(Dog dog) {
        super(dog);
    }

    @Override
    public void showPower() {
        super.showPower();
        System.out.println("我具备奔跑功能!");
    }
}
/**
 * @ClassName Swim
 * @Description 游泳能力
 * @Author xwd
 * @Date 2018/10/25 9:57
 */
public class Swim extends SuperDog{
    public Swim(Dog dog) {
        super(dog);
    }

    @Override
    public void showPower() {
        super.showPower();
        System.out.println("我具备游泳功能!");
    }
}

5、测试

/**
 * @ClassName Client
 * @Description 测试装饰模式
 * @Author xwd
 * @Date 2018/10/25 9:59
 */
public class Client {
    public static void main(String[] args) {

        //原生态的狗,还没有什么能力
        Dog dog = new MyDog();
        dog.showPower();
        System.out.println("*********************");

        //添加了装饰模式的狗,但是还没有添加具体功能
        SuperDog superDog = new SuperDog(dog);
        superDog.showPower();
        System.out.println("*********************");

        //添加了飞行能力的狗
        SuperDog flyDog = new Fly(dog);
        flyDog.showPower();
        System.out.println("*********************");

        //添加了游泳能力的狗
        SuperDog swimDog = new Swim(dog);
        swimDog.showPower();
        System.out.println("*********************");

        //既添加了飞行能力又添加了奔跑能力的狗
        SuperDog runDog = new Run(dog);
        SuperDog fsDog = new Fly(runDog);
        fsDog.showPower();
        System.out.println("*********************");
    }
}

6、测试结果

测试结果

由此可见,需要为一个对象添加新功能时,只需要建立这个功能,将具体对象传入。如果传入的对象是已经具备某些功能的,就相当远在那个基础上添加新的功能。

四、开发中使用的场景

  • IO中输入流和输出流的设计。
  • Swing包中图形界面构件功能。
  • Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,HttpServletRequestWrapper类,增强了request对象的功能。
  • Struts2中,request,response,session对象的处理。

在这里只解释其中一种:
在IO流中,抽象构建对象是InputStream、OutputStream、Reader、Writer。

真实对象是FileInputStream、FileOutputStream。

真实对象

装饰对象是FilterInputStream、FilterOutputStream。

装饰对象

具体的装饰对象:BufferedOutputStream、BufferedInputStream等。

继承装饰对象
构造器中传入抽象构建对象

五、总结

1、功能

装饰模式(Decorator)也叫包装器模式(Wrapper)。

装饰模式降低系统的耦合度,可以动态的增加或删除对象的职责,并使得需要装饰的具体构建类和具体装饰类可以独立变化,以便增加新的具体构建类和具体装饰类。

2、优点

  • 扩展对象功能,比继承灵活,不会导致类个数急剧增加。
  • 可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更加强大的对象。
  • 具体构建类和具体装饰类可以独立变化,用户可以根据需要自己增加新的具体构件子类和具体装饰子类。

3、缺点

  • 产生很多小对象。大量小对象占据内存,一定程度上影响性能。
  • 装饰模式易于出错,调试排查比较麻烦。

4、装饰模式和桥接模式的区别

两个模式都是为了解决过多子类对象问题。但他们的诱因不一样。

  • 桥接模式是对象自身现有机制沿着多个维度变化。
  • 装饰模式是为了增加新的功能。

你可能感兴趣的:(【Java设计模式】结构型模式-装饰模式)