浅谈设计模式-装饰者模式

本文介绍了装饰者模式的概念和应用场景,通过点咖啡的例子展示了装饰者模式如何解决类爆炸问题。代码示例中,详细解释了装饰者模式的实现,包括Drink、Decorator、Espresso和Chocolate类的定义及使用。最后,通过 JDK 中 FilterInputStream 的 UML 类图和模拟代码进一步阐述了装饰者模式的实际运用。
摘要由CSDN通过智能技术生成


前言

初学设计模式,看的韩顺平老师的视频,记录一下。


一、装饰者模式的引入

问题分析

以点咖啡为例,我们一般会点一杯咖啡加上一些调料,如果按照正常思维我们会大致做出如下类图:
源自韩顺平图解Java设计模式

常规思路存在的问题

但是这样会出现问题就是组合种类很多,比方说:n种咖啡、m种调料,就会有n*m种组合来计算花费,而且每增加一种类就会增加非常多的新组合,导致类爆炸。
换一种解决方案就是在类里直接聚合调料字段,UML类图如下:
在这里插入图片描述
这种方案的思路是:在Drink类中直接增加调料字段,这样在每个单品咖啡子类(ShortBlack、LongBlack、Espresso)计算花费时,会根据是否有调料来计算花费(hasMilk…),这种方案是减少了类的个数(因为调料都变成了字段),但是存在问题就是在增加或删除调料时代码的维护量很大(因为每个单品咖啡的cost方法都会由与调料相关的代码,增加或删除调料字段都会导致很多类要修改)。
因此,引出了装饰者模式。

二、什么是装饰者模式?

1.装饰者模式定义

动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)

2.如何理解?

先来看一张图:
在这里插入图片描述
说明:先大致说一下这张图,这张图其实可以理解为4个部分:

  • 第一个部分就是Drink抽象类,这个类是被其它类直接或间接的继承,其实就是我们常规思维创建的基类。
  • 第二个部分是装饰者类Decorator,这个类体现了装饰者模式的核心思想,就是增加了一个父类字段(组合关系),这个字段十分重要!它是用来装饰对象(下面理解部分会讲解)。
  • 第三个部分是普通类,也就是组合对象。有时候会再抽象出一个基类,但仍然可以视为一个部分。
  • 第四部分类似第三部分。

理解:简单来说就是装饰者类Decorator聚合了父类Drink,这样每次增加调料就可以直接将原来的对象作为新的对象的Drink字段。举个例子:我原本点了一杯意大利咖啡,那就是创建一个Espresso对象,记为order。然后我又点了巧克力调料,那就是创建一个Chocolate对象。不过,这里要将原来的order对象作为新对象的字段,new Chocolate(order),这样就是将原来的对象包裹(装饰)起来。至于怎么将两者的价格计算起来,其实很好理解,就是将obj的价格(原来的单品咖啡价格)和当前对象的价格(Chocolate的价格)加和。

三、代码部分

1.Drink 代码:

public abstract class Drink {
	public String des; // 描述
	private float price = 0.0f;
	
	public String getDes() {
		return des;
	}
	public void setDes(String des) {
		this.des = des;
	}
	public float getPrice() {
		return price;
	}
	public void setPrice(float price) {
		this.price = price;
	}
	
	public abstract float cost();
}

2.Decorator 代码:

public class Decorator extends Drink {
	private Drink obj;
	
	public Decorator(Drink obj) { //组合关系
		this.obj = obj;
	}
	
	@Override
	public float cost() {
		// getPrice 获取的是当前对象的价格
		return super.getPrice() + obj.cost();
	}
	
	@Override
	public String getDes() {
		// obj.getDes() 获取的是当前对象的描述
		return super.des + " " + getPrice() + " && " + obj.getDes();
	}
}

3.Espresso 代码:

public class Espresso extends Coffee {
	public Espresso() {
		setDes(" 意大利咖啡 ");
		setPrice(6.0f);
	}
}

4.Chocolate 代码:

public class Chocolate extends Decorator {	//间接继承了Drink类
	public Chocolate(Drink obj) {
		super(obj);
		setDes(" 调料-巧克力 ");
		setPrice(3.0f); //价格
	}
}

5.测试类 CoffeeBar 代码:

public class CoffeeBar {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// 装饰者模式下的订单:2份巧克力+一份牛奶的LongBlack

		// 1. 点一份 LongBlack
		Drink order = new LongBlack();
		System.out.println("费用1=" + order.cost());
		System.out.println("描述=" + order.getDes());

		// 2. order 加入一份牛奶
		order = new Milk(order);

		System.out.println("order 加入一份牛奶 费用 =" + order.cost());
		System.out.println("order 加入一份牛奶 描述 = " + order.getDes());

		// 3. order 加入一份巧克力

		order = new Chocolate(order);

		System.out.println("order 加入一份牛奶 加入一份巧克力  费用 =" + order.cost());
		System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDes());

		// 3. order 加入一份巧克力

		order = new Chocolate(order);

		System.out.println("order 加入一份牛奶 加入2份巧克力   费用 =" + order.cost());
		System.out.println("order 加入一份牛奶 加入2份巧克力 描述 = " + order.getDes());
	
		System.out.println("===========================");
		
		Drink order2 = new DeCaf();
		
		System.out.println("order2 无因咖啡  费用 =" + order2.cost());
		System.out.println("order2 无因咖啡 描述 = " + order2.getDes());
		
		order2 = new Milk(order2);
		
		System.out.println("order2 无因咖啡 加入一份牛奶  费用 =" + order2.cost());
		System.out.println("order2 无因咖啡 加入一份牛奶 描述 = " + order2.getDes());

	
	}

}

6.装饰者模式图解:下订单两份巧克力+一份牛奶的LongBlack

在这里插入图片描述

四、装饰者模式在jdk中的应用

1.FilterInputStream的UML类图:

在这里插入图片描述

2.模拟代码-装饰者

public class Decorator {

	public static void main(String[] args) throws Exception{
		// TODO Auto-generated method stub
		
		//说明
		//1. InputStream 是抽象类, 类似我们前面讲的 Drink
		//2. FileInputStream 是  InputStream 子类,类似我们前面的 DeCaf, LongBlack
		//3. FilterInputStream  是  InputStream 子类:类似我们前面 的 Decorator 修饰者
		//4. DataInputStream 是 FilterInputStream 子类,具体的修饰者,类似前面的 Milk, Soy 等
		//5. FilterInputStream 类 有  protected volatile InputStream in; 即含被装饰者
		//6. 分析得出在jdk 的io体系中,就是使用装饰者模式
		
		DataInputStream dis = new DataInputStream(new FileInputStream("d:\\abc.txt"));
		System.out.println(dis.read());
		dis.close();
	}
}

五、总结

个人理解:

装饰者模式从代码层面来看,就是在装饰者类聚合了其基类(也就是被装饰者),从思想层面上是装饰(也可以理解为套娃,旧对象聚合在新对象),通过这种“包装”思想,就可以达到减少类的数量和提高维护性。
PS:目前初学,理解还不到位!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值