大话设计模式读书笔记——装饰模式

编程入门 行业动态 更新时间:2024-10-15 00:18:43

大话设计<a href=https://www.elefans.com/category/jswz/34/1771241.html style=模式读书笔记——装饰模式"/>

大话设计模式读书笔记——装饰模式

文章目录

  • 1. 需求
  • 2. 代码版本1.0
    • 2.1 Person类
    • 2.2 客户端代码
  • 3. 代码版本2.0
    • UML类图
    • 3.1 Person类
    • 3.2 服饰抽象类
    • 3.3 各种服饰子类
    • 3.4 客户端代码
    • 3.5 弊端
  • 4. 装饰模式案例
    • 4.1 UML类图
    • 4.2 Component类
    • 4.3 ConcreteComponent类
    • 4.4 Decorator类
    • 4.5 ConcreteDecoratorA类
    • 4.6 ConcreteDecoratorB类
    • 4.7 客户端代码
  • 5. 装饰模式实现
    • 5.1 UML类图
    • 5.2 ICharacter接口(Component)
    • 5.3 Person类(ConcreteComponent)
    • 5.4 Finery类(Decorator)
    • 5.5 具体服饰类(ConcreteDecorator)
    • 5.6 客户端代码
    • 5.7 代码拓展
      • 5.7.1 草帽子类
      • 5.7.2客户端代码
  • 6. 商品收银软件回顾
    • 6.1 策略模式实现
      • 6.1.1 UML类图
      • 6.1.2 先折扣再返利的算法子类
      • 6.1.3 修改后的CashContext类
      • 6.1.4 弊端
    • 6.2 简单工厂+策略+装饰模式实现
      • 6.2.1 UML类图
      • 6.2.2 ISale类
      • 6.2.3 CashSuper
      • 6.2.4 CashNormal
      • 6.2.5 CashReturn和CashRebate
      • 6.2.6 CashContext
  • 7. 总结


1. 需求

现在需要设计一个可以给人搭配不同服饰的系统,比如类似QQ、电商平台或者游戏都有的Avatar系统。通俗的来说,就是设计一个可以换各种各样衣服裤子的个人形象系统。

2. 代码版本1.0

2.1 Person类

public class Person {private String name;public Person(String name){this.name = name;}public void wearTShirts(){System.out.println("大T恤");}public void wearBigTrouser(){System.out.println("垮裤");}public void wearSneakers(){System.out.println("球鞋");}public void wearSuit(){System.out.println("西装");}public void wearTie(){System.out.println("领带");}public void wearLeatherShoes(){System.out.println("皮鞋");}public void show(){System.out.println("装扮的"+name);}
}

2.2 客户端代码

		Person xc = new Person("小菜");System.out.println("第一种装扮:");xc.wearTShirts();xc.wearBigTrouser();xc.wearSneakers();xc.show();System.out.println("第二种装扮:");xc.wearSuit();xc.wearTie();xc.wearLeatherShoes();xc.show();

运行结果如下:

第一种装扮:
大T恤 垮裤 球鞋装扮的小菜
第二种装扮:
西装 领带 皮鞋装扮的小菜

3. 代码版本2.0

显然易见,代码1.0版本违背了设计模式中开放----封闭原则,因此,我们需要将服饰写成子类。

UML类图

3.1 Person类

public class Person {private String name;public Person(String name){this.name = name;}public void show(){System.out.println("装扮的"+name);}
}

3.2 服饰抽象类

public abstract class Finery{public abstract void show();
}

3.3 各种服饰子类

public class TShirts extends Finery{public void show(){System.out.println("大T恤");}
}
public class BigTrouser extends Finery{public void show(){System.out.println("垮裤");}
}

注意上面都是继承Finery类,其余类类似,省略。

3.4 客户端代码

        Person xc = new Person("小菜");System.out.println("第一种装扮:");Finery dtx = new TShirts();Finery kk = new BigTrouser();Finery pqx = new Sneakers();dtx.show();kk.show();pqx.show();xc.show();System.out.println("第二种装扮:");Finery xz = new Suit();Finery ld = new Tie();Finery px = new LeatherShoes();xz.show();ld.show();px.show();xc.show();

3.5 弊端

如下代码

	 dtx.show();kk.show();pqx.show();xc.show();

就相当于你光着身子,当着大家的面,先穿T恤,再穿裤子,再穿鞋,仿佛在跳穿衣舞。因此,我们需要把所需的功能按正确的顺序串联起来进行控制,这就需要用到装饰模式

4. 装饰模式案例

装饰模式(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

4.1 UML类图


Component是定义一个对象接口,可以给这些对象动态地添加职责。ConcreteComponent是定义了一个具体的对象,也可以给这个对象添加一些职 责 。 Decorator装饰抽象类,继承了 Component,从外类来扩展Component类的功能,但对于Component来说,是无须知道Decorator的存在的。至于ConcreteDecorator就是具体的装饰对象,起到给Component添加职责的功能

4.2 Component类

//Component类
abstract class Component{public abstract void Operation();
}

4.3 ConcreteComponent类

//ConcreteComponent类
class ConcreteComponent extends Component{public void Operation(){System.out.println("具体对象的实际操作");}
}

4.4 Decorator类

//Decorator类
abstract class Decorator extends Component{protected Component component;//装饰一个Component对象public void SetComponent(Component component){thisponent = component;}//重写Operation(),实际调用component的Operation方法public void Operation(){if (component!=null){component.Operation();}}
}

4.5 ConcreteDecoratorA类

//ConcreteDecoratorA类
class ConcreteDecoratorA extends Decorator{private String addState;//本类独有字段,以区别于ConcreteDecorator类public void Operation(){super.Operation(); //首先运行原有Component的Operation()this.addState = "具体装饰对象A的都有操作";System.out.println(this.addState);}
}

4.6 ConcreteDecoratorB类

//ConcreteDecoratorB类
class ConcreteDecoratorB extends Decorator{public void Operation(){super.Operation();this.AddedBehavior();}//本类都有方法,以区别于ConcreteDecoratorA类private void AddedBehavior(){System.out.println("具体装饰对象B的独有操作");}
}

4.7 客户端代码

	ConcreteComponent c = new ConcreteComponent();ConcreteDecoratorA d1 = new ConcreteDecoratorA();ConcreteDecoratorB d2 = new ConcreteDecoratorB();d1.SetComponent(c); //首先用d1包装cd2.SetComponent(d1); //再用d2来包装d1d2.Operation(); //最终执行d2的Operation

可见,装饰模式是利用SetComponent来对对象进行包装的。这样每个装饰对象的实现就和如何使用这个对象分离开了,每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链当中。

5. 装饰模式实现

5.1 UML类图

5.2 ICharacter接口(Component)

//人物形象接口
public interface ICharacter{public void show();
}

5.3 Person类(ConcreteComponent)

//具体人类
public class Person implements ICharacter{private String name;public Person(String name){this.name = name;}public void show(){System.out.println("装扮的"+name);}
}

5.4 Finery类(Decorator)

//服饰类
public class Finery implements ICharacter{protected ICharacter component;public void decorate(ICharacter component){thisponent = component;}public void show(){if (thisponent != null){thisponent.show();}}
}

5.5 具体服饰类(ConcreteDecorator)

//服饰类
public class TShirts extends Finery{public void show(){System.out.println("大T恤");super.show();}
}

其余类类似,省略。

5.6 客户端代码

        Person xc = new Person("小菜");System.out.println("第一种装扮");Sneakers pqx = new Sneakers();pqx.decorate(xc);BigTrouser kk = new BigTrouser();kk.decorate(pqx);TShirts dtx = new TShirts();dtx.decorate(kk);dtx.show();System.out.println("第二种装扮");LeatherShoes px = new LeatherShoes();px.decorate(xc);Tie ld = new Tie();ld.decorate(px);Suit xz = new Suit();xz.decorate(ld);xz.show();

结果显示如下:

第一种装扮:
大T恤垮裤球鞋装扮的小菜
第二种装扮:
西装领带皮鞋装扮的小菜

5.7 代码拓展

如果换一种装饰方式,比如说,增加草帽装扮,再重新组合一下服饰,则需要增加一个草帽子类,再修改一下装饰方案就好了。

5.7.1 草帽子类

public class Strawhat extends Finery{public void show(){System.out.println("草帽");super.show();}
}

5.7.2客户端代码

        System.out.println("第三种装扮:");Sneakers pqx2 = new Sneakers();pqx2.decorate(xc);LeatherShoes px2 = new LeatherShoes();px2.decorate(pqx2);Tie ld2 = new Tie();ld2.decorate(kk2);Strawhat cm2 = new Strawhat();cm2.decorate(ld2);cm2.show();

运行结果如下:

第三种装扮:
草帽 领带 垮裤 皮鞋 球鞋装扮的小菜

6. 商品收银软件回顾

策略模式的读书笔记中(策略模式读书笔记)介绍了商品收银软件的设计,现在再次增加需求:所有商品,在总价打8折的基础上,再满300返100,当然这只是一种销售方案,还可以是打7折,再满200送50,或者满300返50,再打7折,也就是说,可以多种促销方案组合起来使用。
我们的要求是最终实现的代码,影响面越小越好,也就是原来的代码能利用就要利用,改动尽量小一些,因此,可以使用本文提到的装饰模式。但是在使用装饰模式之前,我们先用策略模式进行实现,下面给出UML类图。

6.1 策略模式实现

6.1.1 UML类图

6.1.2 先折扣再返利的算法子类

    public class CashReturnRebate extends CashSuper{private double moneyRebate = 1d;private double moneyCondition = 0d;private double moneyReturn = 0d;//先折扣,再返利,初始化时需要输入折扣参数,再输入返利条件和返利值//比如”先八折,再满300返100“,就是moneyRebate=0.8,moneyCondition=300,moneyReturn=100public CashReturnRebate(double moneyRebate,double moneyCondition,double moneyReturn){this.moneyRebate = moneyRebate;this.moneyCondition = moneyCondition;this.moneyReturn = moneyReturn;}//先折扣再返利public double acceptCash(double price,int num){double result = price * num * this.moneyRebate;if (moneyCondition>0 && result>=moneyCondition){result = result - Math.floor(result/moneyCondition)*moneyReturn}return result;}}

6.1.3 修改后的CashContext类

public class CashContext{private CashSuper cs; //声明一个CashSuper对象//通过构造方法,传入具体的收费策略public CashContext (int cashType){switch (cashType){case 1:this.cs = new CashNormal();break;case 2:this.cs = new CashRebate(0.8d);break;case 3:this.cs = new CashRebate(0.7d);break;case 4:this.cs = new CashReturn(300d,100d);break;case 5:this.cs = new Strawhat.CashReturnRebate(0.9d,300d,100d);break;}}public double getResult(double price,int num){//根据收费策略不同,获取计算结果return this.cs.acceptCash(price,num);}
}

6.1.4 弊端

观察上述代码可发现:新类CashReturnRebate有原来的两个类:CashReturn和CashRebate有大量重复的代码,为了解决上述问题,可以用装饰模式

6.2 简单工厂+策略+装饰模式实现

6.2.1 UML类图

6.2.2 ISale类

public interface ISale {public double acceptCash(double price,int num);
}

6.2.3 CashSuper

CashSuper原来是抽象类,改成普通类,但实现ISale接口。

    public double acceptCash(double price,int num){double result = 0d;if (thisponent != null){//若装饰对象存在。则执行装饰的算法运算result = thisponent.acceptCash(price,num);}return result;}

6.2.4 CashNormal

CashNormal,相当于ConcreteComponent,是最基本的功能实现,也就是单价×数量的原价算法。

public class CashNormal implements ISale {//正常收费,原价返回public double acceptCash(double price,int num){return price * num;}
}

6.2.5 CashReturn和CashRebate

public class CashRebate extends CashSuper {private double moneyRebate = 1d;//打折收费,初始化时必须输入折扣率。八折就输入0.8public CashRebate(double moneyRebate){this.moneyRebate = moneyRebate;}//收费计算时需要在原价的基础上乘以折扣率public double acceptCash(double price,int num){double result = price * num * this.moneyRebate;return super.acceptCash(result,1);}
}
public class CashReturn extends CashSuper {private double moneyCondition = 0d; //返利条件private double moneyReturn = 0d; //返利值//返利收费。初始化时需要输入返利条件和返利值//比如满300返100,就是moneyCondition=300,moneyReturn=100public CashReturn(double moneyCondition,double moneyReturn){this.moneyCondition = moneyCondition;this.moneyReturn = moneyReturn;}//计算收费时,当达到返利条件,就原价减去返利值public double acceptCash(double price,int num){double result = price * num;if (moneyCondition>0 && result >= moneyCondition){result = result - Math.floor(result/moneyCondition)*moneyReturn;}return super.acceptCash(result,1);}
}

6.2.6 CashContext

重点在CashContext类,因为涉及组合算法,所以用装饰模式的方式进行包装,这里需要注意包装的顺序,先打折后满多少返多少,与先满多少返多少,再打折会得到完全不同的结果。

public class CashContext {private ISale cs; //声明一个ISale接口对象//通过构造方法,传入具体的收费策略public CashContext(int cashType){switch (cashType){case 1:this.cs = new CashNormal();break;.....case 5://先打八折,再慢300返100CashNormal cn = new CashNormal();CashReturn cr1 = new CashReturn(300d,100d);CashRebate cr2 = new CashRebate(0.8d);cr1.decorate(cn);cr2.decorate(cr1);this.cs = cr2;break;case 6://先满200返50,再打七折CashNormal cn2 = new CashNormal();CashRebate cr3 = new CashRebate(0.7d);CashReturn cr4 = new CashReturn(200d,50d);cr3.decorate(cn2);cr4.decorate(cr3);this.cs = cr4;break;}public double getResult(double price,int num){return this.cs.acceptCash(price,num);}}
}


测试结果:
单价1000元,数量为1的商品,原需支付1000元,如果选择先8折再满300送100算法的话,就是1000×0.8=800元,满足两个300元,返200元,最终结果是客户只需支付600元。
单价500元,数量为4的商品,原需支付2000元,如果选择先满200送50再7折算法的话,就是2000中有10个200,送10×50=500元,所以2000-500=1500元,再打7折,1500×0.7,最终结果是客户只需支付1050元。


7. 总结

装饰模式是为已有功能动态地添加更多功能的一种方式。
当系统需要新功能的时候,是向旧的类中添加新的代码。这些新加的代码通常装饰了原有类的核心职责或主要行为,这就会增加原有代码的复杂度。
因此我们需要使用装饰模式它把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象了。

更多推荐

大话设计模式读书笔记——装饰模式

本文发布于:2024-03-06 05:43:48,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1714511.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:模式   大话   读书笔记

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!