装饰器设计模式
装饰器模式
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
问题引出
业务场景:有一家奶茶店卖奶茶,一开始该奶茶店只有4种奶茶,无糖的,少糖的,去冰的,加冰的,给每一种奶茶都标明一个价格。
abstract class MilkTea{
private String description;
public MilkTea(String description) {
this.description = description;
}
public abstract double cost();
public String getDescription() {
return description;
}
}
class SugarFree extends MilkTea{
public SugarFree() {
super("无糖奶茶");
}
@Override
public double cost() {
return 1;
}
}
class Noice extends MilkTea{
public Noice() {
super("去冰奶茶");
}
@Override
public double cost() {
return 2;
}
}
class LessSugar extends MilkTea{
public LessSugar() {
super("少糖奶茶");
}
@Override
public double cost() {
return 3;
}
}
class Ice extends MilkTea{
public Ice() {
super("加冰的奶茶");
}
@Override
public double cost() {
return 3;
}
}
// ===========================================
public class Problem {
public static void main(String[] args) {
MilkTea milkTea1=new SugarFree();
MilkTea milkTea2=new Noice();
MilkTea milkTea3=new LessSugar();
MilkTea milkTea4=new Ice();
System.out.println(milkTea1.getDescription()+":"+milkTea1.cost());
System.out.println(milkTea2.getDescription()+":"+milkTea2.cost());
System.out.println(milkTea3.getDescription()+":"+milkTea3.cost());
System.out.println(milkTea4.getDescription()+":"+milkTea4.cost());
}
}
上述代码很"很完美的完成了我们需求",当但我们的需求发生变化时,上述代码就会出现很严重的问题。比如:此茶奶茶店老板想出了一些新的业务,奶茶里面可以加调料,比如:珍珠,烧仙草,椰果,我们如果要在原来的类上进行修改,那么我们就需要再建至少4*4个类,如果后期需求一多,容易出现类爆炸,所以上述代码的实现方式是不可取的。
解决部分问题
为了避免上述的问题,程序员重新编写了代码,设置要添加的挑调料的布尔值,当需要添加的时候,将那一项设置为true即可
class MilkTea {
private String description;
private boolean pearl;
private boolean hotGrassJelly;
private boolean coconut;
public MilkTea(String description) {
this.description = description;
}
public double cost() {
double total = 0;
if (pearl) {
total += 0.1;
}
if (hotGrassJelly) {
total += 0.2;
}
if (coconut) {
total += 0.3;
}
return total;
}
public void setPearl(boolean pearl) {
this.pearl = pearl;
}
public void setHotGrassJelly(boolean hotGrassJelly) {
this.hotGrassJelly = hotGrassJelly;
}
public void setCoconut(boolean coconut) {
this.coconut = coconut;
}
public String getDescription() {
if (pearl) {
description = description + "+珍珠";
}
if (hotGrassJelly) {
description = description + "+烧仙草";
}
if (coconut) {
description = description + "+椰果";
}
return description;
}
}
class SugarFree extends MilkTea {
public SugarFree() {
super("无糖奶茶");
}
@Override
public double cost() {
return 1 + super.cost();
}
}
class Noice extends MilkTea {
public Noice() {
super("去冰奶茶");
}
@Override
public double cost() {
return 2 + super.cost();
}
}
class LessSugar extends MilkTea {
public LessSugar() {
super("少糖奶茶");
}
@Override
public double cost() {
return 3 + super.cost();
}
}
class Ice extends MilkTea {
public Ice() {
super("加冰的奶茶");
}
@Override
public double cost() {
return 3 + super.cost();
}
}
public class EasySolution {
public static void main(String[] args) {
MilkTea lessSugar = new LessSugar();
lessSugar.setCoconut(true);
lessSugar.setHotGrassJelly(true);
lessSugar.setPearl(true);
System.out.println(lessSugar.getDescription() + " " + lessSugar.cost());
}
}
少糖奶茶+珍珠+烧仙草+椰果 3.6
上述代码种,解决了类爆炸的问题,没有出现各种各样的类。奶茶店老板新加入一种的奶茶,也不会带来什么影响,符合开闭原则。
但当此时,奶茶店老板又加入了一种调料,这样就又会违反开闭原则,因为要修改父类。
装饰器模式
定义一个调料的抽象类,让不同的调料去实现。然后再将已有的奶茶传入。(套娃)
abstract class MilkTea {
private String description;
public MilkTea(String description) {
this.description = description;
}
public abstract double cost();
public String getDescription() {
return description;
}
}
class SugarFree extends MilkTea {
public SugarFree() {
super("无糖奶茶");
}
@Override
public double cost() {
return 1;
}
}
class Noice extends MilkTea {
public Noice() {
super("去冰奶茶");
}
@Override
public double cost() {
return 2;
}
}
class LessSugar extends MilkTea {
public LessSugar() {
super("少糖奶茶");
}
@Override
public double cost() {
return 3;
}
}
class Ice extends MilkTea {
public Ice() {
super("加冰的奶茶");
}
@Override
public double cost() {
return 3;
}
}
// 定义一个调料的抽象类
abstract class Condiment extends MilkTea {
protected MilkTea milkTea;
public Condiment(MilkTea milkTea) {
super("调料:");
this.milkTea = milkTea;
}
}
// 定义具体调料
class Pearl extends Condiment {
public Pearl(MilkTea milkTea) {
super(milkTea);
}
@Override
public double cost() {
return milkTea.cost() + 0.1;
}
@Override
public String getDescription() {
return milkTea.getDescription() + " 珍珠";
}
}
class HotGrassJelly extends Condiment {
public HotGrassJelly(MilkTea milkTea) {
super(milkTea);
}
@Override
public double cost() {
return milkTea.cost() + 0.2;
}
@Override
public String getDescription() {
return milkTea.getDescription() + " 烧仙草";
}
}
class Coconut extends Condiment {
public Coconut(MilkTea milkTea) {
super(milkTea);
}
@Override
public double cost() {
return milkTea.cost() + 0.3;
}
@Override
public String getDescription() {
return milkTea.getDescription() + " 椰果";
}
}
public class DecoratorPattern {
public static void main(String[] args) {
MilkTea milkTea = new SugarFree();
MilkTea milkTea1 = new Coconut(milkTea);
MilkTea milkTea2 = new Pearl(milkTea1);
System.out.println(milkTea2.getDescription() + " :" + milkTea2.cost());
}
}
装饰器模式图解
装饰器模式UML图
IO中的装饰器设计模式
使用了装饰器模式之后,类还是有点多,但我们已经尽力了。
jdk中的IO流用的就是装饰器模式。
如图,在IO流中,InputStream
就相当于奶茶,继承自他的类,就相当与各种类型的奶茶,比如ObjectInputSteam
,在众多继承了他的类中FilterInputStream
,就相当于调料抽象类,继承了FilterInputStream
的类,就相当于具体的调料了。
在调料抽象类中,一定会继承主类,而且会关联主类,且构造方法中也会将主类传入。
根据装饰器设计模式扩展Reader
// 读取文件中的字符串,并在字符串前加上行号
public class LineReader extends BufferedReader {
protected Reader reader;
private int lineNumber;
public LineReader(Reader reader) {
super(reader);
}
public int getLineNumber() throws IOException {
lineNumber++;
return lineNumber;
}
@Override
// 不实现
public int read(char[] cbuf, int off, int len) throws IOException {
return 0;
}
@Override
public void close() throws IOException {
reader.close();
}
public static void main(String[] args) throws IOException {
Reader reader = new FileReader("E:\\桌面\\a.txt");
BufferedReader bf = new BufferedReader(reader);
LineReader lineReader = new LineReader(bf);
String line;
while ((line = bf.readLine()) != null) {
System.out.println(lineReader.getLineNumber() + ": " + line);
}
}
}
a.txt:
装饰器设计模式
七大设计原则
工厂模式
结果:
1: 装饰器设计模式
2: 七大设计原则
3:
4: 工厂模式