设计模式-总览https://mp.csdn.net/mp_blog/creation/editor/122202507目录
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。《HeadFirst设计模式》上的装饰器模式的例子,我觉得还是比较形象的。有很多的饮料种类可以售卖,每种饮料又可以随客户任意加入不同总量和数量的点缀物(比如在茶里加柠檬,糖等),最后肯定是要统计用户点了哪些东西总共多少钱。
一、装饰器模式实现
1)、首先定义饮料接口(价钱和描述):
public abstract class Beverage {
String description = "我还不知道什么饮料类型,呵呵!";
public String getDescription() {
return description;
}
/**
* 饮料当前的总价钱
*
* @return 装饰后钱的总和
*/
public abstract double cost();
}
2)、各种饮料的实现(老外举的饮料例子不是很知道,管他呢)
/**
* 重炒
* @author lihongmin
* @date 2018/9/2 3:18
*/
public class DarkRoast extends Beverage {
private static final double COST = 3.6;
public DarkRoast() {
description = "我的 DarkRoast 哦!";
}
@Override
public double cost() {
return COST;
}
}
/**
* Espresso饮料
* @author lihongmin
* @date 2018/9/2 2:39
*/
public class Espresso extends Beverage {
private static final double COST = 1.99;
public Espresso() {
description = "我是Espresso Espresso饮料哦!";
}
@Override
public double cost() {
return COST;
}
}
/**
* HouseBlend饮料
* @author lihongmin
* @date 2018/9/2 2:42
*/
public class HouseBlend extends Beverage {
private static final double COST = 0.89;
public HouseBlend() {
description = "我的 HouseBlend 哦!";
}
@Override
public double cost() {
return COST;
}
}
3)、饮料可以添加的点缀物的抽象(必须实现饮料接口)
/**
* 调料顶层抽象类,装饰者的类型必须与顶层一致,必须继承(可能是实现){@link Beverage}
*
* @author lihongmin
* @date 2018/9/2 2:34
*/
public abstract class CondimentDecorator extends Beverage{
/**
* 覆盖父类的描述方法,并且抽象后让子类实现
*/
@Override
public abstract String getDescription();
}
4)、点缀物(调料)的实现
/**
* 摩卡
* @author lihongmin
* @date 2018/9/2 2:48
*/
public class Mocha extends CondimentDecorator {
private static final double COST = 13.5;
/**
* 被装饰者
*/
Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
// 委托的形式得到描述
final String description = beverage.getDescription();
String result = description + ", 得到委托描述后, 我又在里面加了些摩卡(装饰)!";
return result;
}
@Override
public double cost() {
System.out.println("我现在的价钱是委托之前的,加上Mocha的钱了,切记!");
return beverage.cost() + COST;
}
}
/**
* 豆浆
* @author lihongmin
* @date 2018/9/2 3:11
*/
public class Soy extends CondimentDecorator {
private static final double COST = 3.4;
Beverage beverage;
public Soy(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
// 委托的形式得到描述
final String description = beverage.getDescription();
String result = description + ", 得到委托描述后, 我又在里面加了些豆浆(装饰)!";
return result;
}
@Override
public double cost() {
System.out.println("我加上了Soy的钱!");
return beverage.cost() + COST;
}
}
public class Whip extends CondimentDecorator {
private static final double COST = 13.7;
Beverage beverage;
public Whip(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
// 委托的形式得到描述
final String description = beverage.getDescription();
String result = description + ", 得到委托描述后, 我又在里面加了些奶泡(装饰)!";
return result;
}
@Override
public double cost() {
System.out.println("在装饰前时候,我加上了Whip(奶泡)的钱!");
return beverage.cost() + COST;
}
}
5)、测试
/**
* 测试装饰者模式
*
* 我单独点了一杯Espresso,什么都不加先尝尝!
* 我是Espresso Espresso饮料哦! , 花了我1.99 钱
* ---------------------------------------------------------------------------------------------------
* 我在点一杯DarkRoast, 但是需要double Mocha,还要加一份Whip,这样比较美味吧!
* 在装饰前时候,我加上了Whip(奶泡)的钱!
* 我现在的价钱是委托之前的,加上Mocha的钱了,切记!
* 我现在的价钱是委托之前的,加上Mocha的钱了,切记!
* 我的 DarkRoast 哦!, 得到委托描述后, 我又在里面加了些摩卡(装饰)!, 得到委托描述后, 我又在里面加了些摩卡(装饰)!, 得到委托描述后, 我又在里面加了些奶泡(装饰)! , 花了我44.3 钱
* ---------------------------------------------------------------------------------------------------
* 我再点一个HouseBlend咖啡,里面多加几样调味品吧,一样来一点,哈哈哈!
* 我加上了Soy的钱!
* 在装饰前时候,我加上了Whip(奶泡)的钱!
* 我现在的价钱是委托之前的,加上Mocha的钱了,切记!
* 我的 HouseBlend 哦!, 得到委托描述后, 我又在里面加了些摩卡(装饰)!, 得到委托描述后, 我又在里面加了些奶泡(装饰)!, 得到委托描述后, 我又在里面加了些豆浆(装饰)! , 花了我31.49 钱
*
* @author lihongmin
* @date 2018/9/1 19:42
*/
public class TestDecorator {
public static void main(String[] args) {
System.out.println("我单独点了一杯Espresso,什么都不加先尝尝!");
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() + " , 花了我" + beverage.cost() + " 钱");
System.out.println("---------------------------------------------------------------------------------------------------");
System.out.println("我在点一杯DarkRoast, 但是需要double Mocha,还要加一份Whip,这样比较美味吧!");
Beverage beverage1 = new DarkRoast();
beverage1 = new Mocha(beverage1);
beverage1 = new Mocha(beverage1);
beverage1 = new Whip(beverage1);
System.out.println(beverage1.getDescription() + " , 花了我" + beverage1.cost() + " 钱");
System.out.println("---------------------------------------------------------------------------------------------------");
System.out.println("我再点一个HouseBlend咖啡,里面多加几样调味品吧,一样来一点,哈哈哈!");
Beverage beverage2 = new HouseBlend();
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
beverage2 = new Soy(beverage2);
System.out.println(beverage2.getDescription() + " , 花了我" + beverage2.cost() + " 钱");
}
}
总结分析:装饰对象必须都实现同一个接口,每个装饰者就是接口下的一个实现,各个实现是平级的。创建一个装饰对象就增加一种特有的功能,并且在构造中传入之前的对象(因为实现了同一接口,想起了各种inputSteam)。最后就能拿到想要的最终结果。
二、装饰器模式使用场景分析
1、Jdk的InputStream实现分析
InputStram是一个抽象类,二级的实现类非常的多,看了一下好几十个。
比较常用的如下:
/**
* InputStream 装饰器模式
* @author kevin
* @date 2019/11/12 16:49
* @since 1.0.0
*/
public class ImputDecoratorTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
InputStream inputStream = new FileInputStream(new File(""));
// FileInputStream特有的功能
FileInputStream fileInputStream = (FileInputStream) inputStream;
fileInputStream.getChannel();
fileInputStream.getFD();
// ZipInputStream特有的功能
inputStream = new ZipInputStream(inputStream);
ZipInputStream zipInputStream = (ZipInputStream) inputStream;
zipInputStream.getNextEntry();
// ObjectInputStream用于反序列化的能力
inputStream = new ObjectInputStream(zipInputStream);
ObjectInputStream objectInputStream = (ObjectInputStream) inputStream;
objectInputStream.readObject();
}
}
就是同一个对象,传入不同的子类(都直接或者间接的实现了InputStream)构造器中就拥有的不同的功能,和顶层接口的公共方法。
2、ServletRequest
/**
* {@link javax.servlet.ServletRequest} 的装饰器模式
* @author kevin
* @date 2019/11/12 17:18
* @since 1.0.0
*/
public class ServletRequestTest {
public static void main(String[] args) {
ServletRequest servletRequest = new ServletRequestWrapper(null);
// 用于上传文件的能力
StandardMultipartHttpServletRequest multipartRequest = new
StandardMultipartHttpServletRequest(null);
HttpHeaders httpHeaders = multipartRequest.getMultipartHeaders("myFile");
MultipartFile multipartFile = multipartRequest.getFile("myFile");
System.out.println("" + httpHeaders + multipartFile);
ContentCachingRequestWrapper cacheWrapper = new ContentCachingRequestWrapper(null);
cacheWrapper.getRequestedSessionId();
}
}