超级链接: Java常用设计模式的实例学习系列-绪论
参考:《HeadFirst设计模式》
装饰者模式是一种结构型
模式。
装饰者模式:动态地给一个对象添加一些额外的职责。
本文以生日蛋糕装饰
这一场景来学习装饰者模式
:
无论什么实现方式,对于蛋糕来说都需要一个抽象类,下面的ICake
定义了蛋糕的基本操作,如:展示制作过程、显示花费等。
/**
* 生日蛋糕接口
*
* @author hanchao
*/
public interface ICake {
/**
* 展示制作过程
*/
void showMakingProcess();
/**
* 总花费
*/
float getCost();
/**
* 显示花费
*/
void showCost();
}
大概思路就是:蛋糕店的每个蛋糕品种,都对应着一个ICake
的子类。
子类1:水果蛋糕:FruitCake
/**
* 实现方式1:每种蛋糕一个子类:水果蛋糕
*
* @author hanchao
*/
@Slf4j
public class FruitCake implements ICake {
/**
* 展示制作过程
*/
@Override
public void showMakingProcess() {
log.info("选取一个蛋糕胚...");
log.info("包裹一层奶油...");
log.info("摆放6片菠萝...");
log.info("摆放6颗草莓...");
}
/**
* 计算总花费
*/
@Override
public float getCost() {
return 320.99f;
}
/**
* 显示花费
*/
@Override
public void showCost() {
log.info("总价:{}", 320.99f);
}
}
子类2:奶油蛋糕:CreamCake
/**
* 实现方式1:每种蛋糕一个子类:奶油蛋糕
*
* @author hanchao
*/
@Slf4j
public class CreamCake implements ICake {
/**
* 展示制作过程
*/
@Override
public void showMakingProcess() {
log.info("选取一个蛋糕胚...");
log.info("包裹一层奶油...");
}
/**
* 显示总花费
*/
@Override
public float getCost() {
return 120.99f;
}
/**
* 显示花费
*/
@Override
public void showCost() {
log.info("总价:{}", 120.99f);
}
}
….
缺点:
这样来看,如果蛋糕店有100种蛋糕,则需要实现100个子类。
这种实现方式的缺点很明显:
实现复杂
:几百种蛋糕需要几百个类。复用性低
:每种蛋糕类的代码基本不会复用,存在大量重复代码。扩展性差
:如果需求发送变化,例如蛋糕胚价格发送变化,则可能修改几百个类。违背设计原则对修改关闭,对扩展放开
。大概思路就是:所有蛋糕共用一个大类,所有的蛋糕装饰方法如添加奶油、水果等等都放在这个类中。
超级大类:SuperCake
/**
* 实现方式2:所有蛋糕共用一个大类
*
* @author hanchao
*/
@Slf4j
public class SuperCake implements ICake {
/**
* 制作过程
*/
private List<String> processList = new ArrayList<>();
/**
* 价格
*/
private float cost;
/**
* 展示制作过程
*/
@Override
public void showMakingProcess() {
processList.forEach(log::info);
}
/**
* 显示总花费
*/
@Override
public float getCost() {
return cost;
}
/**
* 显示花费
*/
@Override
public void showCost() {
log.info("总价:{}", cost);
}
/**
* 选取蛋糕胚
*/
public void selectCake() {
processList.add("选取一个蛋糕胚...");
cost += 30f;
}
/**
* 添加奶油
*/
public void addCream() {
processList.add("包裹一层奶油...");
cost += 10f;
}
/**
* 添加水果
*/
public void addFruit(String name, float price) {
processList.add("摆放" + name + "...");
cost += price;
}
}
缺点:
扩展性差
:如果需求发送变化,例如新增一种装饰品巧克力,则必然需要修改SuperCake,违背设计原则对修改关闭,对扩展放开
。首先,需要定义一个装饰者的抽象类。
层层包裹
的,所以装饰者本身应该与蛋糕是同一类型,及装饰者decorator
应该实现ICake
。decorator
的装饰目标是蛋糕本身,所以装饰者decorator
应该拥有ICake
的成员变量。根据上述分析,进行编码。
装饰者抽象类:AbstractCakeDecorator
/**
* 蛋糕装饰品
*
* @author hanchao
*/
@Slf4j
public abstract class AbstractCakeDecorator implements ICake {
/**
* 包含一个蛋糕对象
*/
@Getter
private ICake cake;
public AbstractCakeDecorator(ICake cake) {
this.cake = cake;
}
/**
* 展示制作过程
*/
@Override
public abstract void showMakingProcess();
/**
* 总花费
*/
@Override
public abstract float getCost();
/**
* 显示花费
*/
@Override
public void showCost() {
log.info("总价:{}", getCost());
}
}
装饰者实现类:水果装饰:FruitDecorator
/**
* 水果
*
* @author hanchao
*/
@Slf4j
public class FruitDecorator extends AbstractCakeDecorator {
private String fruit;
private float cost;
public FruitDecorator(ICake cake, String fruit, float cost) {
super(cake);
this.fruit = fruit;
this.cost = cost;
}
/**
* 展示制作过程
*/
@Override
public void showMakingProcess() {
super.getCake().showMakingProcess();
log.info("摆放" + fruit + "...");
}
/**
* 显示总花费
*/
@Override
public float getCost() {
return super.getCake().getCost() + cost;
}
}
装饰者实现类:奶油装饰:FruitDecorator
/**
* 奶油
*
* @author hancha
*/
@Slf4j
public class CreamDecorator extends AbstractCakeDecorator {
public CreamDecorator(ICake cake) {
super(cake);
}
/**
* 展示制作过程
*/
@Override
public void showMakingProcess() {
super.getCake().showMakingProcess();
log.info("包裹一层奶油...");
}
/**
* 显示总花费
*/
@Override
public float getCost() {
return super.getCake().getCost() + 12f;
}
}
被装饰者:蛋糕胚:CakeEmbryo
无论做多少装饰,最初都有一个原始的被装饰者。
放到蛋糕装饰场景中,无论添加多少蛋糕装饰,蛋糕胚是最原始的东西。
/**
* 蛋糕胚
*
* @author hnchao
*/
@Slf4j
public class CakeEmbryo implements ICake {
/**
* 展示制作过程
*/
@Override
public void showMakingProcess() {
log.info("选取一个蛋糕胚...");
}
/**
* 显示总花费
*/
@Override
public float getCost() {
return 30f;
}
/**
* 显示花费
*/
@Override
public void showCost() {
log.info("总价:{}", getCost());
}
}
应用代码:DecoratorDemo
注意其装饰的方式:层层包裹
public static void main(String[] args) {
//实现方式3:装饰者模式
log.info("实现方式3:装饰者模式:制作一个水果蛋糕");
cake = new FruitDecorator(new FruitDecorator(new CreamDecorator(new CakeEmbryo()), "6片菠萝", 20f), "6颗草莓", 30f);
cake.showMakingProcess();
cake.showCost();
System.out.println();
log.info("实现方式3:装饰者模式:制作一个奶油蛋糕:");
cake = new CreamDecorator(new CakeEmbryo());
cake.showMakingProcess();
cake.showCost();
}
优点:
复用性高
:只要使用某种装饰品,则必然复用其代码。扩展性高
:如果需求发送变化,例如新增一种装饰品巧克力,则只需要新增一种装饰品ChocolateDecorator
即可。场景:
HttpServletRequest
的body
是流格式,只可读取一次。RequestBodyWrapper.java
继承HttpServletRequestWrapper,重写getInputStream和getReader实现了对HttpServletRequest请求的包装,将body数据提取出来放入byte数组中,从而实现request中body数据的多次使用。URL: Spring MVC代码实例系列-11:Spring MVC实现简单的权限控制拦截器和请求信息统计拦截器
最后以UML类图来总结本文的生日蛋糕装饰场景以及装饰者模式。