《23种设计模式》三:装饰者模式

本文介绍了一位程序员如何运用装饰者设计模式,解决煎饼摊配料动态组合及统计的需求。通过创建一系列装饰者类,实现了灵活添加配料并计算总价,展示了装饰者模式在实际场景中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目背景

       最近项目经理小王因为手里没有合适的项目,但是他个人还是一个非常上进的人。于是就自己租了一个小推车,晚上没事的时候出来摊煎饼赚点外快。但是他发现出入账,统计物料,流水计算是个挺麻烦的东西。毕竟资深程序员,只好写一个系统来干这些事情了。

需求分析

       小王同学煎饼分为两大类,一种细粮煎饼,一种粗粮煎饼。配料有鸡蛋,烤肠,馃子,脆皮。用户在点了主体煎饼之后,随意的添加后面的配料,不限数量,不限组合。小王同学在经过市场调研之后发现大家普遍购买的有三种,所以就搞了三个套餐。

套餐A细粮 + 2个鸡蛋 + 1个馃子
套餐B粗粮 + 2个鸡蛋 + 1个脆皮
套餐C细粮 + 1个鸡蛋 + 1个烤肠

当然,每种配料的价格不同,所以价格也不一样,系统最终可以统计每单需要什么配料,多少钱即可

构思

       因为这次没有甲方来着急上线,所以小王同学就想了一下怎么去实现这个功能。小王同学打算设计一个超类Jianbing,然后粗粮和细粮两个类继承这个超类。在超类中定义各种配料为一个标志位(int)0为没有,2为2个。然后有一个总计cost()方法来计算,具体实现就是判断每一个配料的标志位是否均有值,明细就把所有有值的配料列出来,就可以搞定这个事。

      但是如果这个时候引进一种新的配料比如卫龙。那就需要把超类重新调整一遍,这不符合设计模式的依赖倒置原则(见一位大牛的见解,快速入口), 而且再改动的时候肯定就又风险导致系统出现新的buff。

      几经思考之后,就有了下面的解决方案

代码实现

/**
 * 煎饼摊超类
 */
public abstract class Booth {

	public String	description	= "";
	private int		price		= 0;;

	public void setDescription(String description) {
		this.description = description;
	}

	/**
	 * 订单明细
	 */
	public String getDescription() {
		return description + "-" + this.getPrice() + "¥";
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}

	/**
	 * 花费
	 */
	public abstract int cost();
}

/**
 * 煎饼超类
 */
public class JianBing extends Booth{

	@Override
	public int cost() {
		return super.getPrice();
	}
}
public class CuLiang extends JianBing{

	public CuLiang() {
		super.setDescription("粗粮煎饼");
		super.setPrice(5);
	}
}
public class XiLiang extends JianBing{

	public XiLiang() {
		super.setDescription("细粮煎饼");
		super.setPrice(5);
	}
}

/**
 * 配料父类
 */
public class Materials extends Booth{

	private Booth obj;
	
	public Materials(Booth booth) {
		this.obj = booth;
	}
	
	@Override
	public int cost() {
		return super.getPrice() + obj.cost();
	}
	
	@Override
	public String getDescription() {
		return super.description  + "-" + super.getPrice() + "¥\r\n " + obj.getDescription();
	}
}

public class Guozi extends Materials{

	public Guozi(Booth booth) {
		super(booth);
		super.setDescription("馃子");
		super.setPrice(2);
	}
}
public class Egg extends Materials{

	public Egg(Booth booth) {
		super(booth);
		super.setDescription("鸡蛋");
		super.setPrice(1);
	}
}
public class Roast extends Materials{

	public Roast(Booth booth) {
		super(booth);
		super.setDescription("烤肠");
		super.setPrice(1);
	}
}
/**
 * 套餐A
 */
public class TariffA extends Booth{

	Booth order;
	
	public TariffA() {
		order = new XiLiang();
		order = new Guozi(order);
		order = new Egg(order);
		order = new Egg(order);
	}

	@Override
	public int cost() {
		return order.cost();
	}
	
	@Override
	public String getDescription() {
		return order.getDescription();
	}
}
public static void main(String[] args) {
		Booth order;
		order = new CuLiang();
		System.out.println("===========订单1===========");
		System.out.println("合计:" + order.cost());
		System.out.println("订单详情:" + order.getDescription());
		
		
		order = new XiLiang();
		order = new CuiPI(order);
		order = new Egg(order);
		order = new Roast(order);
		System.out.println("===========订单2===========");
		System.out.println("合计:" + order.cost());
		System.out.println("订单详情:" + order.getDescription());
		
		order = new TariffA();
		System.out.println("===========订单3(套餐A)===========");
		System.out.println("合计:" + order.cost());
		System.out.println("订单详情:" + order.getDescription());
	}
// 运行结果
===========订单1===========
合计:5
订单详情:粗粮煎饼-5¥
===========订单2===========
合计:9
订单详情:烤肠-1¥
 鸡蛋-1¥
 脆皮-2¥
 细粮煎饼-5¥
===========订单3(套餐A)===========
合计:9
订单详情:鸡蛋-1¥
 鸡蛋-1¥
 馃子-2¥
 细粮煎饼-5¥

       这样就解决了之前构思中提到的问题,还能解决后期物料添加,根本不会影响主体代码,只需要添加自己的名称和单价即可解决。这就引出关于装饰者设计模式的定义:在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

      装饰者模式结构

抽象构件(Component)角色给出一个抽象接口,以规范准备接收附加责任的对象
具体构件(Concrete Component)角色定义一个将要接收附加责任的类
装饰(Decorator)角色持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口
具体装饰(Concrete Decorator)角色负责给构件对象添加上附加的责任

在Java源码中典型的装饰者模式就是java I/O, 其实装饰者模式也有其缺点,就是产生了太多的装饰者小类,有类爆炸的趋势。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值