装饰者模式

144 阅读3分钟

装饰者模式

⭐提出问题

假设我们有一家快餐店,这家快餐店提供炒饭和炒面,炒饭和面条又可以搭配不同的配菜,不同的配菜是不同的价钱,这样计算起来就很繁琐,我们常规想用继承的方式来实现简单的模拟:

UML1.png

这样继承下来,添加每个配菜的饭都有了自己的类,就能很方便的对应,但这样也有缺点:

  • 扩展性差
  • 子类过多

我们就要引入装饰者模式

⭐装饰者模式

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

🌙结构

  • 抽象构件角色——定义一个抽象接口以规范准备接收附加责任的对象(快餐接口)
  • 具体构建角色——实现抽象构建,通过装饰角色为其添加一些职责(实现了快餐接口的炒饭/炒面)
  • 抽象装饰角色——继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能(实现或继承快餐接口/类,并聚合了炒饭/炒面实例)
  • 具体装饰角色——实现抽象装饰的相关方法,并给具体构件对象添加附加的责任

🌰举个例子

还是我们的快餐店案例,现在根据装饰者模式进行如下改动:

UML2.png

抽象构建角色(快餐类):

//FastFood.class
private float price;
private String desc;
​
public abstract float cost();
​
//省略getter、setter

具体构件角色(炒饭、炒面):

public class FriedRice extends FastFood {
    public FriedRice() {
        super(10,"炒饭");//价格和描述
    }
    
    public float cost() {
        return getPrice();
    }
}
public class FriedNoodles extends FastFood {
    public FriedNoodles() {
        super(12,"炒面");//价格和描述
    }
    
    public float cost() {
        return getPrice();
    }
}

抽象装饰角色(聚合快餐类的配料的基类):

public abstract class Garnish extends FastFood {
    //声明快餐类的变量
    private FastFood fastFood;
    
    public FastFood getFastFood() {
        return fastFood;
    }
    
    public void setFastFood(FastFood fastFood) {
        this.fastFood = fastFood;
    }
    
    public Garnishi(FastFood fastFood, float price, String desc) {
        super(price, desc);
        this.fastFood = fastFood;
    }
}

具体装饰者角色(鸡蛋、培根):

public class Egg extends Garnish {
    public Egg(FastFood fastFood) {
        super(fastFood, 1, "鸡蛋");
    }
    
    public float cost() {
         //计算价格
        return getPrice() + getFastFood().cost();
    }
    
    @Override
    public Stringg getDesc() {
        return super.getDesc() + getFastFood().getDesc();
    }
}
public class Bacon extends Garnish {
    public Egg(FastFood fastFood) {
        super(fastFood, 2, "Bacon");
    }
    
    public float cost() {
         //计算价格
        return getPrice() + getFastFood().cost();
    }
    
    @Override
    public Stringg getDesc() {
        return super.getDesc() + getFastFood().getDesc();
    }
}

这样我们就可以这样要一份炒饭:

FastFood food = new FriedRice();`

现在只要炒饭的话,价格就是十元

如果我们想要在炒饭中加鸡蛋:

food = new Egg(food);

现在food对象的价格就是10元

如果我们想再加一个培根:

food = new Bacon(food);

现在food对象的价格就是13元

现在我们就实现了灵活搭配,如果想要再加一个火腿肠配料,只需要再实现一个火腿肠类即可!

🌙优势

  • 装饰着模式可以带来比继承更加灵活的扩展功能,使用更加方便,可以通过组合不同的装饰者对象来获取具有不同行为状态的多样化的结果。装饰者模式比继承更具良好的扩展性,完美的遵循开闭原则,继承是静态的附加责任,装饰者则是动态的附加责任。
  • 装饰者和被装饰类都可以独立发展,不会互相耦合,装饰者模式是继承的一个替代模式,可以动态扩展一个实现类的功能。