Java设计模式 —— 【结构型模式】装饰者模式详解

发布于:2024-12-21 ⋅ 阅读:(7) ⋅ 点赞:(0)


前言

在日常生活中,我们常会遇到一种场景:去快餐店吃饭,里面琳琅满目的主食,还有各式各样的配菜作为消费者,只管挑选就行,但是如果让我们来设计系统的话,我们该如何设计,每种搭配来一个类?
在这里插入图片描述
像上述设计,显然问题明显:

  • 扩展性不好
    • 如果要再加一种配料(火腿肠),我们就会发现需要给“炒饭”和“FriedNoodles”分别定义一个子类。如果要新增一个快餐品类(炒河粉)的话,就需要定义更多的子类。
  • 产生过多的子类

以此引入装饰者模式(定义):

指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。


结构说明

装饰(Decorator)模式中的角色:

  • 抽象构件(Component)角色 :定义一个抽象接口以规范准备接收附加责任的对象。
  • 具体构件(Concrete Component)角色 :实现抽象构件,通过装饰角色为其添加一些职责。
  • 抽象装饰(Decorator)角色 : 继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  • 具体装饰(ConcreteDecorator)角色 :实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

案例演示

使用装饰者模式对上述模型进行改进,UML类图如下:
在这里插入图片描述
快餐接口【抽象构件角色】:

//快餐抽象类
public abstract class FastFood {
    private float price;
    private String desc;

    public FastFood(float price, String desc) {
        this.price = price;
        this.desc = desc;
    }

    public float getPrice() {
        return price;
    }

    public String getDesc() {
        return desc;
    }

    public abstract float cost();  //获取价格
}

主食类【具体构建角色】:

//炒饭(具体构建角色)
public class FriedRice extends FastFood {
    public FriedRice() {
        super(10, "炒饭");
    }

    @Override
    public float cost() {
        return getPrice();
    }
}

//炒面(具体构建角色)
public class FriedNoodles extends FastFood {
    public FriedNoodles() {
        super(12, "炒面");
    }

    @Override
    public float cost() {
        return getPrice();
    }
}

装饰者【抽象装饰者角色】:

//装饰者(抽象装饰者角色)
public abstract class Garnish extends FastFood {
    //声明快餐类变量
    private FastFood fastFood;

    public Garnish(FastFood fastFood, float price, String desc) {
        super(price, desc);
        this.fastFood = fastFood;
    }

    public FastFood getFastFood() {
        return fastFood;
    }
}

配菜【具体装饰者角色】:

//鸡蛋类(具体的装饰者角色)
public class Egg extends Garnish {

    public Egg(FastFood fastFood) {
        super(fastFood, 1, "鸡蛋");
    }

    @Override
    public float cost() {
        //计算价格
        return getPrice() + getFastFood().cost();
    }

    @Override
    public String getDesc() {
        return getFastFood().getDesc() + "加" + super.getDesc();
    }
}

//培根类
public class Bacon extends Garnish {

    public Bacon(FastFood fastFood) {
        super(fastFood, 2, "培根");
    }

    @Override
    public float cost() {
        //计算价格
        return getPrice() + getFastFood().cost();
    }

    @Override
    public String getDesc() {
        return getFastFood().getDesc() + "加" + super.getDesc();
    }
}

测试:

public class Client {
    public static void main(String[] args) {
        //点一份炒饭
        FastFood food = new FriedRice();
        System.out.println(food.getDesc() + ": " + food.cost() + "元");
        //在上面的炒饭中加一个鸡蛋
        food = new Egg(food);
        System.out.println(food.getDesc() + ": " + food.cost() + "元");
        //再加一个培根
        food = new Bacon(food);
        System.out.println(food.getDesc() + ": " + food.cost() + "元");

        //点一份炒面
        FastFood noodles = new FriedNoodles();
        System.out.println(noodles.getDesc() + ": " + noodles.cost() + "元");
        //在上面的炒饭中加一个鸡蛋
        noodles = new Egg(noodles);
        System.out.println(noodles.getDesc() + ": " + noodles.cost() + "元");
        //再加一个培根
        noodles = new Bacon(noodles);
        System.out.println(noodles.getDesc() + ": " + noodles.cost() + "元");
    }
}

在这里插入图片描述


小结

优势:

  • 饰者模式可以带来比继承更加灵活性的扩展功能,使用更加方便,可以通过组合不同的装饰者对象来获取具有不同行为状态的多样化的结果。装饰者模式比继承更具良好的扩展性,完美的遵循开闭原则,继承是静态的附加责任,装饰者则是动态的附加责任。

  • 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

使用场景:

  • 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。

    不能采用继承的情况主要有两类:

    • 第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;
    • 第二类是因为类定义不能继承(如final类)
  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

  • 当对象的功能要求可以动态地添加,也可以再动态地撤销时。


静态代理和装饰者的区别

  • 相同点:
    • 都要实现与目标类相同的业务接口
    • 在两个类中都要声明目标对象
    • 都可以在不修改目标类的前提下增强目标方法
  • 不同点:
    • 目的不同
      装饰者是为了增强目标对象
      静态代理是为了保护和隐藏目标对象
    • 获取目标对象构建的地方不同
      装饰者是由外界传递进来,可以通过构造方法传递
      静态代理是在代理类内部创建,以此来隐藏目标对象

网站公告

今日签到

点亮在社区的每一天
去签到