软件设计模式-装饰者模式

100 阅读3分钟

结构型模式-装饰者模式

概述

举个例子:快餐店有炒面、炒饭这些快餐,可以额外附加鸡蛋、火腿、培根这些配菜,每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦,传统方式可以使用继承方式实现

使用继承方式存在的问题:

  • 扩展性不好

    如果要在加一种配料,需要给目标类分别定义一个子类,或者新增一个目标类,就需要定义更多的子类

  • 产生过多的子类

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

结构

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

实例代码

抽象构件角色:

//抽象构件角色
@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract class FastFood {

	//价格
	private float price;
	//描述
	private String desc;

	public abstract float cost();
}

具体构件角色:

//具体构件角色
public class FriedNoodles extends FastFood{

	public FriedNoodles(){
		super(12,"炒面");
	}

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

抽象装饰者角色:

//抽象装饰者角色
@Data
public abstract class Garnish extends FastFood{

	//声明快餐类
	FastFood fastFood;

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

	public Garnish(FastFood fastFood,float price, String desc) {
		super(price, desc);
		this.fastFood = 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 super.getDesc() + getFastFood().getDesc();
	}
}
//具体装饰者角色
public class Bacnon extends Garnish{

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

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

	@Override
	public String getDesc(){
		return super.getDesc() + getFastFood().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 Egg(food);
		System.out.println(food.getDesc() + "  " + food.cost());
		//炒饭中加一个培根
		food = new Bacnon(food);
		System.out.println(food.getDesc() + "  " + food.cost());
	}
}
/*
运行结果如下:
炒饭  10.0
鸡蛋炒饭  11.0
鸡蛋鸡蛋炒饭  12.0
培根鸡蛋鸡蛋炒饭  14.0
*/

优缺点

优点

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

使用场景

  • 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时,不能采用继承的情况主要有两类:
    • 第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;
    • 第二类是因为类定义不能继承(如final类)
  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责
  • 当对象的功能要求可以动态地添加,也可以再动态地撤销时