Decorator装饰者模式
引入业务场景
当前有一家饮料店,饮料包括基础饮料,购买饮料时候,可以选择需要提那家的配料,及饮料杯大小类型,然后结算金额。其中,饮料和配料可以会新增很多种,或原原料或配料因销售不佳可能下架,或饮料或者配料单价可以修改,(饮料大小类型不变,及没有其他类型)
初级模型设计(不考虑饮料杯大小类型)
饮料为继承类,配料为变量
对应代码
Beverage
package headfirst.hd.decorator.old;
public abstract class Beverage {
String description = "饮料";
boolean pearl;
boolean watermelon;
boolean banana;
boolean sugar;
public String getDescription() {
return description;
}
public abstract double cost();
public boolean isPearl() {
return pearl;
}
public void setPearl(boolean pearl) {
this.pearl = pearl;
}
public boolean isWatermelon() {
return watermelon;
}
public void setWatermelon(boolean watermelon) {
this.watermelon = watermelon;
}
public boolean isBanana() {
return banana;
}
public void setBanana(boolean banana) {
this.banana = banana;
}
public boolean isSugar() {
return sugar;
}
public void setSugar(boolean sugar) {
this.sugar = sugar;
}
//辅助计算方法
protected double condimentCost() {
double cost = 0;
if (isPearl()) {
cost += 1.0;
}
if (isBanana()) {
cost += 2.0;
}
if (isSugar()) {
cost += 1.0;
}
if (isWatermelon()) {
cost += 1.0;
}
return cost;
}
}
H2CO3
package headfirst.hd.decorator.old;
public class H2CO3 extends Beverage {
public H2CO3() {
description = "碳酸饮料";
}
@Override
public double cost() {
//饮料基础价格
double cost = 2.0;
//配料价格
cost += super.condimentCost();
return cost;
}
}
Coffee
package headfirst.hd.decorator.old;
public class Coffee extends Beverage {
public Coffee() {
description = "咖啡";
}
@Override
public double cost() {
//饮料基础价格
double cost = 4.0;
//配料价格
cost += super.condimentCost();
return cost;
}
}
Fruit
package headfirst.hd.decorator.old;
public class Fruit extends Beverage {
public Fruit() {
description = "果汁饮料";
}
@Override
public double cost() {
//饮料基础价格
double cost = 2.0;
//配料价格
cost += super.condimentCost();
return cost;
}
}
Tea
package headfirst.hd.decorator.old;
public class Tea extends Beverage {
public Tea() {
description = "茶饮料";
}
@Override
public double cost() {
//饮料基础价格
double cost = 3.0;
//配料价格
cost += super.condimentCost();
return cost;
}
}
测试,买一杯果汁,加珍珠,加香蕉
TestDrive
package headfirst.hd.decorator.old;
public class TestDrive {
public static void main(String[] args) {
Beverage beverage = new Fruit();
beverage.setPearl(true);
beverage.setBanana(true);
//果汁(2)+ 珍珠(1)+ 香蕉(2)
System.out.println(beverage.cost());
}
}
当前设计引发的问题
关于配料数量问题,买两份西瓜,当前设计不能满足
买奶茶碰到一个人点了一杯奶茶加了十份珍珠,店员还特地打电话问是不是搞错了,结果人家还挺凶的回没搞错,又问他要不要分杯子装物料,人家也说不用!大家都一脸懵逼笑到岔气………
某些饮料的配料不合理,茶饮料不能加西瓜,香蕉等配料,但拥有了这些配料变量
- 调料价格变化,会修改涉及饮料相关代码
- 新增加或删除配料,会修改涉及饮料相关代码
修改设计解决以上问题
引入装饰着模式,重新设计
将其分为两类,浅蓝色为我们的基本组件(我们的饮料),粉色为我们的装饰类(我们的配料)
对应代码
基础组件
Beverage
package headfirst.hd.decorator.news.component;
public abstract class Beverage {
String description = "饮料";
public String getDescription() {
return description;
}
public abstract double cost();
}
Coffee
package headfirst.hd.decorator.news.component;
public class Coffee extends Beverage {
public Coffee() {
description = "咖啡";
}
@Override
public double cost() {
//饮料基础价格
double cost = 4.0;
return cost;
}
}
Fruit
package headfirst.hd.decorator.news.component;
public class Fruit extends Beverage {
public Fruit() {
description = "果汁饮料";
}
@Override
public double cost() {
//饮料基础价格
double cost = 2.0;
return cost;
}
}
H2CO3
package headfirst.hd.decorator.news.component;
public class H2CO3 extends Beverage {
public H2CO3() {
description = "碳酸饮料";
}
@Override
public double cost() {
//饮料基础价格
double cost = 2.0;
return cost;
}
}
Tea
package headfirst.hd.decorator.news.component;
public class Tea extends Beverage {
public Tea() {
description = "茶饮料";
}
@Override
public double cost() {
//饮料基础价格
double cost = 3.0;
return cost;
}
}
装饰者类
CondimentDecorator (配料包装类基类)
package headfirst.hd.decorator.news.decorator;
import headfirst.hd.decorator.news.component.Beverage;
public abstract class CondimentDecorator extends Beverage {
Beverage beverage;
public CondimentDecorator(Beverage beverage) {
this.beverage = beverage;
}
}
Banana
package headfirst.hd.decorator.news.decorator;
import headfirst.hd.decorator.news.component.Beverage;
public class Banana extends CondimentDecorator {
public Banana(Beverage beverage) {
super(beverage);
}
@Override
public double cost() {
return beverage.cost() + 2.0;
}
}
Pearl
package headfirst.hd.decorator.news.decorator;
import headfirst.hd.decorator.news.component.Beverage;
public class Pearl extends CondimentDecorator {
public Pearl(Beverage beverage) {
super(beverage);
}
@Override
public double cost() {
return beverage.cost() + 1.0;
}
}
Sugar
package headfirst.hd.decorator.news.decorator;
import headfirst.hd.decorator.news.component.Beverage;
public class Sugar extends CondimentDecorator {
public Sugar(Beverage beverage) {
super(beverage);
}
@Override
public double cost() {
return beverage.cost() + 1.0;
}
}
Watermelon
package headfirst.hd.decorator.news.decorator;
import headfirst.hd.decorator.news.component.Beverage;
public class Watermelon extends CondimentDecorator {
public Watermelon(Beverage beverage) {
super(beverage);
}
@Override
public double cost() {
return beverage.cost() + 1.0;
}
}
测试,买一杯果汁,加2个珍珠,加一个香蕉
TestDrive
package headfirst.hd.decorator.news.component;
import headfirst.hd.decorator.news.decorator.Banana;
import headfirst.hd.decorator.news.decorator.Pearl;
public class TestDrive {
public static void main(String[] args) {
//点一杯果汁
Beverage beverage = new Fruit();
//加两个珍珠
beverage = new Pearl(beverage);
beverage = new Pearl(beverage);
//加一个香蕉
beverage = new Banana(beverage);
//果汁(2)+ 珍珠(1)+ 珍珠(1)+ 香蕉(2)
System.out.println(beverage.cost());
}
}
cost调用关系图
运行结果
回顾之前设计引发的问题
- 关于配料数量问题,买两份西瓜,当前设计不能满足
- 某些饮料的配料不合理,茶饮料不能加西瓜,香蕉等配料,但拥有了这些配料变量
- 调料价格变化,会修改涉及饮料相关代码
- 新增加或删除配料,会修改涉及饮料相关代码
似乎都已经解决啦
装饰者运用,io
在上面的关系图中可以看出:
1. InputStream是所有的输入字节流的父类,它是一个抽象类。
2. ByteArrayInputStream、StringBufferInputStream、FileInputStream是三种基本的介质流,它们分别将Byte数组、StringBuffer、和本地文件中读取数据。
3. FilterInputStream是装饰者的基类,我们可以通过继承继承增加新的装饰者
主要是重写以下两个方法
编码一个将输入流小写转换成大写的输入流UperCaseInputStream
UperCaseInputStream
package headfirst.hd.decorator.app;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
public class UpperCaseInputStream extends FilterInputStream {
public UpperCaseInputStream(InputStream inputStream) {
super(inputStream);
}
@Override
public int read() throws IOException {
return toUpperCase(super.read());
}
@Override
public int read(byte[] bytes, int offset, int len) throws IOException {
int byteslen = super.read(bytes, offset, len);
int end = offset + byteslen;
for (int i = offset; i < end; i++) {
int c = toUpperCase((char) bytes[i]);
bytes[i] = (byte) c;
}
return byteslen;
}
//转换
private int toUpperCase(int c) {
// -1是文件读取的结束标志
if (c != -1) {
c = Character.toUpperCase(c);
}
return c;
}
}
测试类TestInputStream
package headfirst.hd.decorator.app;
import java.io.InputStream;
import java.io.StringBufferInputStream;
public class TestInputStream {
public static void main(String[] args) throws Exception {
InputStream inputStream = new UpperCaseInputStream(new StringBufferInputStream("Do You Like Java!"));
//测试一
/*int c;
while ((c = inputStream.read()) != -1) {
System.out.print((char)c);
}*/
//测试二
byte[] bytes = new byte[1024];
int len;
while ((len = inputStream.read(bytes, 0, 1024)) != -1) {
System.out.println(new String(bytes, 0, len));
}
}
}
测试结果
OO原则
类应该对修改关闭,扩展开发