栏目分类:
子分类:
返回
终身学习网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
终身学习网 > IT > 软件开发 > 后端开发 > Java

装饰者模式

Java 更新时间:发布时间: 百科书网 趣学号
概述

装饰模式是用来替代继承的一种设计模式。它通过一种无须定义子类的方式来给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系。降低了系统的耦合,可以动态的增加或者删除对象的职责。

装饰模式:动态地给一个对象增加一些额外的职责。就扩张功能而言,装饰模式提供了一种比使用子类更加灵活的替代方案。

装饰器模式作用是针对目标方法进行增强,提供新的功能或者额外的功能。

不同于适配器模式和桥接模式,装饰器模式涉及的是单方,和代理模式相同,而且目标必须是抽象的!!。

而实际上,装饰器模式和代理模式的实现方式基本一致,只在目标的存在上有些差别。

装饰模式的UML图

不想画,借用一下

  • Component(抽象构建类):它是具体构件类和抽象装饰类的父类。声明规范具体构建类的业务方法。
  • ConcreteComponent(具体构件类):它是抽象构建类的子类,实现了抽象构建类业务方法,具体装饰类会给它增加额外的功能。
  • Decorator(抽象装饰类):它也是抽象构建类的子类,继承或实现抽象构件。它维护一个指向抽象构建对象的引用,通过该引用可以调用装饰之前构建对象的方法,并通过其子类扩展该方法,已达到装饰的目的。
  • ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,实现抽象装饰类的相关方法,负责给具体构件类添加新的功能。每一个具体的装饰类都定义了一个具体的行为。
代码演示

抽象构建类Component

package 装饰器;

public interface Component {
    public void display();
}

具体构件类ConcreteComponent,实现接口Component

package 装饰器;

public class ConcreteComponent implements Component {
    @Override
    public void display() {
        System.out.println("ConcreteComponent");
    }
}

抽象装饰类Decorator,实现接口Component

package 装饰器;

public class Decorator implements Component{
    public Component component;
    
    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public void display() {
        component.display();
        System.out.println(component + "+++" + "Decorator");
    }
}

具体装饰类ConcreteDecoratorA,继承Decorator

package 装饰器;

public class ConcreteDecoratorA extends Decorator {
    public ConcreteDecoratorA(Component component) {
        super(component);
    }

    public void display() {
        super.display();
        System.out.println("ConcreteDecoratorA");
    }
}

具体装饰类ConcreteDecoratorB,继承Decorator

package 装饰器;

public class ConcreteDecoratorB extends Decorator {

    public ConcreteDecoratorB(Component component) {
        super(component);
    }

    public void display() {
        super.display();
        System.out.println("ConcreteDecoratorB");
    }
}

调用类Client

package 装饰器;

public class Client {
    public static void main(String[] args) {
        
        Component component = new ConcreteComponent();
        component.display();
        System.out.println("-------");

        
        Component component1 = new ConcreteDecoratorA(component);
        component1.display();
        System.out.println("-------");

        Component component2 = new ConcreteDecoratorB(component1);
        component2.display();
        System.out.println("-------");
    }
}

运行结果:

ConcreteComponent
-------
ConcreteComponent
装饰器.ConcreteComponent@60e53b93+++Decorator
ConcreteDecoratorA
-------
ConcreteComponent
装饰器.ConcreteComponent@60e53b93+++Decorator
ConcreteDecoratorA
装饰器.ConcreteDecoratorA@5e2de80c+++Decorator
ConcreteDecoratorB
-------

分析结果:
Component component = new ConcreteComponent(); 实例化一个ConcreteComponent对象,执行component.display();,实际调用类ConcreteComponent的方法display(),打印”ConcreteComponent“。

构造过程:Component component1 = new ConcreteDecoratorA(component); 先调用ConcreteDecoratorA构造方法,执行 super(component); 调用父类Decorator 的构造方法,执行 this.component = component;,将传入参数 component 赋值给属性component。
调用过程:执行 component1.display(); 时,其实是执行ConcreteDecoratorA的方法display(),而其方法第一步super.display();执行父类Decorator的方法display()。而父类Decorator的方法display()第一步 component.display(); 执行上述传参的component 的方法display()。故依次打印 ConcreteComponent --> 装饰器.ConcreteComponent@60e53b93+++Decorator --> ConcreteDecoratorA。

构造过程:Component component2 = new ConcreteDecoratorB(component1); 先调用ConcreteDecoratorB构造方法,其构造方法 super(component); 调用父类Decorator 的构造方法 this.component = component;,将传入参 component1 赋值给属性component。
调用过程:执行 component2.display(); 时,其实是执行ConcreteDecoratorB的方法display(),而其方法第一步 super.display(); 执行父类Decorator的方法display()。而父类Decorator的方法display()第一步 component.display(); 执行上述传参的 component1 的方法display()。这一步流程就是上面 “执行component1.display(); ”的流程。 即打印ConcreteComponent --> 装饰器.ConcreteComponent@60e53b93+++Decorator --> ConcreteDecoratorA。然后再打印 装饰器.ConcreteDecoratorA@5e2de80c+++Decorator ,再打印ConcreteDecoratorB。

注:装饰的调用有点类似递归,形成一个调用链。

需求:如果想复用 具体装饰类ConcreteDecoratorA的既有功能,怎么处理? (该需求可以参见《SpringMVC-快速入门(6.2)- 自定义过滤器》里复用HttpServletRequestWrapper原有功能,然后自定义修改逻辑)

解决方案:编写装饰类继承ConcreteDecoratorA

package 装饰器;

public class ConcreteDecoratorD extends ConcreteDecoratorA {
    public ConcreteDecoratord(Component component) {
        super(component);
    }

    public void display() {
        super.display();
        System.out.println("ConcreteDecoratorD");
    }
}

新增ConcreteDecoratorD继承ConcreteDecoratorA。
调用类Client:

package 装饰器;

public class Client {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        component.display();
        System.out.println("-------");

        Component component1 = new ConcreteDecoratorA(component);
        component1.display();
        System.out.println("-------");

        

        
        Component component3 = new ConcreteDecoratord(component);
        component3.display();
        System.out.println("-------");

        Component component4 = new ConcreteDecoratord(component1);
        component4.display();
        System.out.println("-------");
    }
}

运行结果:

ConcreteComponent
-------
ConcreteComponent
ConcreteDecoratorA
-------
ConcreteComponent
ConcreteDecoratorA
ConcreteDecoratorD
-------
ConcreteComponent
ConcreteDecoratorA
ConcreteDecoratorA
ConcreteDecoratorD
-------

结果可以看出,ConcreteDecoratorD.display()执行之前均会先执行其父类ConcreteDecoratorA.display()。

优点
  1. 可以通过一种动态的方式扩张一个类的功能。
  2. 可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合可以创造不同的行为的组合。
  3. 对于扩张一个对象的功能,装饰模式比继承模式更加灵活,不会导致类的数量急剧增加。
缺点
  1. 因为增加装饰类,是必产生一些小对象,而这些小对象属性、方法差不多,会使系统复杂性增加。
  2. 装饰模式是一种比继承更加灵活的解决方案。但同时,也意味着比继承更加容易出错,更难排查问题。对于多层装饰的对象,需要逐级排查,较为繁琐。
使用场景:
  1. 在不影响其他对象的情况下以动态的,透明的方式给单个对象添加职责。
  2. 不能用继承进行扩张的时候。
与代理的区别

装饰类的代码编写和代理很类似,具体区别如下:

  1. 代理是全权代理,目标根本不对外,全部由代理类来完成。
  2. 装饰是增强,是辅助,目标仍然可以自行对外提供服务,装饰器只起增强作用。
  3. 代理的目标也可以是类,装饰目标必须是接口。
实际案例

快餐店有炒面、炒饭这些快餐,可以额外附加鸡蛋、火腿、培根这些配菜,加配菜需要额外加钱,并且每个配菜的价钱不一样,计算快餐价格如何实现?

继承的方式:

这是继承的方式,但是横向扩展性不好:如果要再加一种配料(火腿肠),我们就会发现需要给FriedRice和FriedNoodles分别定义一个子类。如果要新增一个快餐品类(炒河粉)的话,就需要定义更多的子类,会出现类爆炸的问题。

装饰者模式: 注:图片网上找的,大致意思符合,和下面代码不对应。囧。。。 代码实现

抽象构件FastFood:

package 装饰器;

public interface FastFood {
    public float getCountPrice();
    public String getDesc();
}

具体构件RiceFried(炒饭):

package 装饰器;

public class RiceFried implements FastFood {
    public float price = 10.0f;
    public String desc = "炒饭";

    @Override
    public float getCountPrice() {
        return price;
    }

    @Override
    public String getDesc() {
        return desc;
    }
}

抽象装饰:

package 装饰器;

public class FoodDecorator implements FastFood {
    public FastFood fastFood;

    public FoodDecorator(FastFood fastFood) {
        this.fastFood = fastFood;
    }

    @Override
    public float getCountPrice() {
        float countPrice = fastFood.getCountPrice();
        return countPrice;
    }

    @Override
    public String getDesc() {
        String desc = fastFood.getDesc();
        return desc;
    }
}

具体装饰EggDecrator(鸡蛋)

package 装饰器;

public class EggDecrator extends FoodDecorator {
    public float price = 2.0f;
    public String desc = "鸡蛋";

    public EggDecrator(FastFood fastFood) {
        super(fastFood);
    }

    public float getCountPrice() {
        
        float countPrice = super.getCountPrice();
        
        countPrice += price;
        return countPrice;
    }

    public String getDesc() {
        String desc = super.getDesc();
        desc = this.desc + desc;
        return desc ;
    }
}

具体装饰BaconDecrator(培根)

package 装饰器;

public class BaconDecrator extends FoodDecorator{
    public float price = 3.0f;
    public String desc = "培根";

    public BaconDecrator(FastFood fastFood) {
        super(fastFood);
    }

    public float getCountPrice() {
        
        float countPrice = super.getCountPrice();
        
        countPrice += price;
        return countPrice;
    }

    @Override
    public String getDesc() {
        String desc = super.getDesc();
        desc = this.desc +desc ;
        return desc ;
    }
}

调用:

package 装饰器;

public class Shopping {
    public static void main(String[] args) {
        FastFood RiceFried = new RiceFried();
        System.out.println(RiceFried.getDesc() + ":" + RiceFried.getCountPrice());

        FastFood eggDecrator = new EggDecrator(RiceFried);
        System.out.println(eggDecrator.getDesc() + ":" + eggDecrator.getCountPrice());

        BaconDecrator baconDecrator = new BaconDecrator(RiceFried);
        System.out.println(baconDecrator.getDesc() + ":" + baconDecrator.getCountPrice());

        BaconDecrator baconDecrator2 = new BaconDecrator(eggDecrator);
        System.out.println(baconDecrator2.getDesc() + ":" + baconDecrator2.getCountPrice());
    }
}

运行结果:

炒饭:10.0
鸡蛋炒饭:12.0
培根炒饭:13.0
培根鸡蛋炒饭:15.0

如果要再加一种配料(火腿肠),我们只需要定义一个修饰类。
如果要新增一个快餐品类(炒河粉)的话,我们只需要定义一个具体构件类。

转载请注明:文章转载自 www.051e.com
本文地址:http://www.051e.com/it/852485.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 ©2023-2025 051e.com

ICP备案号:京ICP备12030808号