zoukankan      html  css  js  c++  java
  • 装饰者模式学习:模拟咖啡馆的点单系统来剖析装饰者模式的使用 + 装饰者模式在java I/O 中的应用

    通过模拟咖啡馆的点单系统来剖析装饰者模式的使用

    参考:https://blog.csdn.net/gududedabai/article/details/81989196

    一)、传统的点单系统构建,每一个种类的咖啡都定义一个类

    • 弊端:
    1. 如果为每一种混合咖啡都定义一个类,那么,会产生很多的类对象。
    2. 混合咖啡的价格是在单品咖啡的基础上的,如若某一单品咖啡的价格
      发生改变,那么就要修改与之关联的所有的混合咖啡的价格。

    咖啡的共同属性:

    /**
     * 抛转引玉
     *  --:咖啡订单系统
     *      1.咖啡店售卖四款基础咖啡
     *        Espressio, ShortBlack, LongBlack, Decaf
     *      2.可以在四款基础咖啡的基础上加入调料,例如Milk,Soy、Chocolate  ,组成混合咖啡
     *      3.每款咖啡都共同的description属性,和getDecription(),指料明加入的调料,cost()计算咖啡所需的价格。
     */
    
    
    public abstract class Coffee {
        /**
         * 描述咖啡加入的调料+单品种类
         */
        private String description;
    
        public Coffee() {
        }
    
        public void setDescription(String description) {
            this.description = description;
        }
    
        public Coffee(String description) {
            this.description = description;
        }
    
        /**
         * 打印购买的咖啡信息
         */
      void getDescription(){
          System.out.println(description);
      }
    
        /**
         * 计算咖啡所需花费的价格
         * @return
         */
      public abstract int CoffeePrice();
    }
    

    单品咖啡:

    /**
     * 低糖咖啡
     */
    public class Decaf extends Coffee {
        Decaf(String description){
            super(description);
        }
        @Override
        public int CoffeePrice() {
            return 30;
        }
    }
    

    单品咖啡:

    /**
     * 浓咖啡
     */
    public class Espressio extends Coffee {
        Espressio(String description){
            super(description);
        }
        @Override
        public int CoffeePrice() {
            return 10;
        }
    }
    

    单品咖啡:

    /**
     * 黑咖啡
     */
    public class LongBlack extends Coffee{
        LongBlack(String description){
            super(description);
        }
        @Override
        public int CoffeePrice() {
            return 20;
        }
    }
    

    单品咖啡:

    /**
     * 浓缩咖啡
     */
    public class ShortBlack extends Coffee{
        ShortBlack(String description){
            super(description);
        }
        @Override
        public int CoffeePrice() {
            return 15;
        }
    }
    

    混合咖啡:

    /**
     * 组合咖啡: 无糖+牛奶
     */
    public class DecafAndMilk extends Coffee{
        DecafAndMilk(String description){
            super(description);
        }
        @Override
        public int CoffeePrice() {
            return 35;
        }
    }
    

    售卖咖啡:

       1)、在new 对象时就指名加入的调料,getDescription()时打印咖啡种类和加 
    
    	入的调料,直接调用CoffeePrice()返回咖啡的价格。
    
    2)、需要为每一种混合咖啡都创建一个咖啡对象
    
    /**
     * 售卖咖啡
     *  使用传统方式来构建售卖咖啡的类:
     *    --: 所有的混合咖啡都实现了超类Coffee
     *        此时,出现了一个问题
     *        ---》1.因为调料的种类很多,调料与调料之间的组合方式也很多,这时咖啡类的数量就会增多。
     *            2.因为所有的混合咖啡都是在单品咖啡的基础上构建的,当单品咖啡的价格发生了调整,所有
     *               与单品咖啡相关的混合咖啡的价格都要进行调整。
            */
    public class SaleCoffee {
        public static void main(String[] args) {
            Coffee coffee = new Decaf("无糖咖啡");
            //打印咖啡的种类和价格
            coffee.getDescription();
            System.out.println(coffee.CoffeePrice());
    
            //无糖+牛奶的咖啡
            Coffee coffee1 = new DecafAndMilk("无糖咖啡:+牛奶");
            coffee1.getDescription();
            System.out.println(coffee1.CoffeePrice());
        }
    }
    

    结果:

    无糖咖啡
    30
    无糖咖啡:+牛奶
    35
    

    二)、将调料声明在超类中,在单品咖啡的基础上加入调料,只需定义单品咖啡类即可

    • 好处
      1.减少了组合咖啡类的定义,通过判断hasXxx()可以在四个单品咖啡的 基础上加调料,即可以通过四个单品类来得到很多的组合咖啡
    • 弊端
    1. 当需要加入一种调料时,需要修改超类中的代码,这样违反了代码的开闭原则, 一旦修改了代码就会有产生bug的风险。
    2. 当用户需要加两份调料,如:加入两份牛奶时不能通过hasMilk()来计算咖啡的价格。

    所有咖啡的超类:

    将所有的调料以boolean的形式声明在超类中,并通过hasXxx()来判断是否加
    
        入调料以及计算咖啡的价格。
    
    import com.sun.xml.internal.ws.util.StringUtils;
    
    /**
     * 构建第二种形式的咖啡超类
     *   --: 一开始就给定了咖啡的调料
     */
    public abstract class Coffee {
        /**
         * 描述咖啡的种类和调料
         */
        private String description;
    
        /**
         * 将咖啡的调料内置在超类中
         */
        private boolean milk;
    
        private boolean soy;
    
        private boolean chocolate;
    
        public Coffee(String description) {
            this.description = description;
        }
    
        public Coffee() {
        }
    
    
        /**
         * 判断是否加了牛奶
         * @return
         */
        public Boolean hashMilk(){
           return milk;
        }
    
        /**
         * 判断是否加了豆浆
         * @return
         */
        public Boolean hashSoy(){
           return soy;
        }
    
        /**
         * 判断是否加了巧克力
         * @return
         */
        public Boolean hashChocolate(){
            return chocolate;
        }
    
        /**
         * 根据咖啡的种类和调料计算咖啡的价格
         */
        public abstract int CoffeePrice();
    
        public void getDescription() {
            System.out.println(description);
        }
    
        public void setDescription(String description) {
            this.description += description;
        }
    
        public void setMilk(boolean milk) {
            this.milk = milk;
            if(milk == true) {
                setDescription("+牛奶");
            }
        }
    
        public void setSoy(boolean soy){
            this.soy = soy;
            if(soy == true){
                setDescription("+豆浆");
            }
        }
    
        public void setChocolate(boolean chocolate) {
            this.chocolate = chocolate;
            if(chocolate == true) {
                setDescription("+chocolate");
            }
        }
    }
    

    单品咖啡:

    通过hasXxx()来计算最终的价格。
    
    **
     * 低糖咖啡
     */
    public class Decaf extends Coffee {
        Decaf(String description){
            super(description);
        }
        @Override
        public int CoffeePrice() {
            //单品低糖咖啡的价格为30
            int cost = 30;
            if(this.hashMilk()){
                cost = cost + 5;
            }
            if(this.hashChocolate()){
                cost = cost + 10;
            }
            if(this.hashSoy()){
                cost = cost + 2;
            }
            return cost;
        }
    }
    

    单品咖啡:

    /**
     * 浓咖啡
     */
    public class Espressio extends Coffee {
        Espressio(String description){
            super(description);
        }
        @Override
        public int CoffeePrice() {
            //单品浓咖啡的价格为10
            int cost = 10;
            if(this.hashMilk()){
                cost = cost + 5;
            }
            if(this.hashChocolate()){
                cost = cost + 10;
            }
            if(this.hashSoy()){
                cost = cost + 2;
            }
            return cost;
        }
    }
    

    单品咖啡:

    /**
     * 黑咖啡
     */
    public class LongBlack extends Coffee{
        LongBlack(String description){
            super(description);
        }
    
        /**
         * 判断当前种类的咖啡是否有加入调料,若有则加入调料的价格
         * @return
         */
        @Override
        public int CoffeePrice() {
            //单品黑咖啡的价格为20
            int cost = 20;
            if(this.hashMilk()){
                cost = cost + 5;
            }
            if(this.hashChocolate()){
                cost = cost + 10;
            }
            if(this.hashSoy()){
                cost = cost + 2;
            }
            return cost;
        }
    }
    

    单品咖啡:

    /**
     * 浓缩咖啡
     */
    public class ShortBlack extends Coffee{
        ShortBlack(String description){
            super(description);
        }
        @Override
        public int CoffeePrice() {
            //单品浓缩咖啡的价格为15
            int cost = 15;
            if(this.hashMilk()){
                cost = cost + 5;
            }
            if(this.hashChocolate()){
                cost = cost + 10;
            }
            if(this.hashSoy()){
                cost = cost + 2;
            }
            return cost;
        }
    }
    

    售卖咖啡:

    /**
     * 将所有的调料放在超类中
     *   --:减少了组合咖啡类的定义,通过判断hasXxx()可以在四个单品咖啡的基础上加调料,即可以通过四个单品类来得到很多的组合咖啡
     *
     *   弊端:
     *    --:1.当需要加入一种调料时,需要修改超类中的代码,这样违反了代码的开闭原则,一旦修改了代码就会有产生bug的风险。
     *        2.当用户需要加两份调料,如:加入两份牛奶时不能通过hasMilk()来计算咖啡的价格。
     *
     */
    public class SaleCoffee {
        public static void main(String[] args) {
            //先选择咖啡单品
            Coffee coffee = new Decaf("无糖咖啡:");
            //组合咖啡: 无糖 + 牛奶
            coffee.setMilk(true);
            coffee.setChocolate(false);
            coffee.getDescription();
            System.out.println(coffee.CoffeePrice());
        }
    }
    
    
    
    
    结果:
    
    无糖咖啡:+牛奶
    35
    

    三)你要喝什么味的咖啡?

    使用装饰者模式来实现不同咖啡种类的搭配:

    (装饰者模式的模型)

    主体接口类:

    被装饰类:
    
        装饰类:
    
               具体装饰类1;具体装饰类2;具体装饰类3;  
    
               具体装饰类具有叠加效果
    

    主体接口类:

    /**
     * 实现装饰者模式的逻辑:
     *       1.公共接口类:
     *       --:需要一个公共接口类
     *         该接口定义了被装饰类的主要逻辑方法,被装饰者和装饰者分别去实现或继承这个接口
     *       2.被装饰类:
     *         被装饰者实现公共接口类,并对接口方法做具体实现
     *       3.装饰类:
     *        装饰类接收被装饰类对象,调用被装饰类的方法
     *       4.具体装饰类
     *         继承装饰类,做具体的装饰逻辑实现
     */
    
    /**
     * 公共接口类:
     *   --: 定义需要装饰的接口方法以及公共的对象属性
     */
    public abstract class Coffee {
        private String description;
    
        private double price;
    
        /**
         * 获取咖啡的价格
         * @return
         */
        public abstract double getCoffeePrice();
    
        public abstract String getCoffeeDescription();
    
        public String getDescription() {
            return description;
        }
    
        public void setDescription(String description) {
            this.description = description;
        }
    
        public double getPrice() {
            return price;
        }
    
        public void setPrice(double price) {
            this.price = price;
        }
    }
    

    被装饰类:

    /**
     * 被装饰类:
     *  --:该类为咖啡单品类,可以为该咖啡加各种调料,然后计算咖啡的价格以及获取咖啡的种类和所加的调料
     */
    public class Decaf extends Coffee{
    
        /**
         * 因为是单品咖啡,所以对象刚创建时价格和种类就已经确定了
         */
        Decaf(){
            setPrice(30);
            setDescription("低糖:");
        }
        @Override
        public double getCoffeePrice() {
            return this.getPrice();
        }
    
        @Override
        public String getCoffeeDescription() {
            return this.getDescription();
        }
    }
    

    装饰类:

    /**
     * 装饰类:
     *  --:接收被装饰对象,调用被装饰对象的方法
     */
    public class Decorator extends Coffee{
        /**
         * 被装饰对象
         */
       private Coffee coffee = null;
        /**
         * 一初始化就有传入一个被装饰对象
         */
        Decorator(Coffee coffee){
            this.coffee = coffee;
        }
    
        @Override
        public double getCoffeePrice() {
            //使用被装饰者的功能
            return coffee.getCoffeePrice();
        }
    
        @Override
        public String getCoffeeDescription(){
            return coffee.getCoffeeDescription();
        }
    }
    

    具体装饰类:

    /**
     * 具体的装饰者实现类
     *   --:继承装饰类
     */
    public class Milk extends Decorator{
        Milk(Coffee coffee) {
            super(coffee);
            setPrice(10);
            setDescription("+牛奶");
        }
        @Override
        public double getCoffeePrice() {
            //使用被装饰者的功能
            return super.getCoffeePrice()+this.getPrice();
        }
    
        @Override
        public String getCoffeeDescription(){
            return super.getCoffeeDescription()+this.getDescription();
        }
    }
    

    具体装饰类:

    public class Chocolate extends Decorator {
        Chocolate(Coffee coffee) {
            super(coffee);
            setPrice(15);
            setDescription("+chocolate");
        }
        @Override
        public double getCoffeePrice() {
            //使用被装饰者的功能
            return super.getCoffeePrice()+this.getPrice();
        }
    
        @Override
        public String getCoffeeDescription(){
            return super.getCoffeeDescription()+this.getDescription();
        }
    
    }
    

    生产咖啡:

    public class SaleCoffee {
        public static void main(String[] args) {
            //加双份牛奶
            Decorator decorator = new Decorator(new Milk(new Milk(new Decaf())));
            System.out.println(decorator.getCoffeeDescription());
            System.out.println(decorator.getCoffeePrice());
        }
    }
    

    结果:

    低糖:+牛奶+牛奶
    50.0
    

    四)、装饰者模式举例二、你要吃什么味道的鸡腿堡?

    参考: https://blog.csdn.net/jason0539/article/details/22713711

    主题接口类:

    /**
     * 使用汉堡店买汉堡的例子来实现装饰者模式
     *   --:汉堡可以选择加生菜、火腿、沙拉、番茄酱
     */
    public abstract class Hamburger {
        /**
         * 汉堡名字
         */
        private String name;
    
        /**
         * 汉堡的价格
         */
        private double price;
    
        /**
         * 制作汉堡
         */
        public abstract String product();
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public double getPrice() {
            return price;
        }
    
        public void  setPrice(double price) {
            this.price = price;
        }
    }
    

    被装饰类:

    /**
     * 基础汉堡
     */
    public class ChickenBurger extends Hamburger{
        ChickenBurger(){
            setName("鸡腿堡");
            setPrice(25);
        }
        @Override
        public String product() {
           return this.getName()+": " + this.getPrice();
        }
    }
    

    装饰类:

    public class Decorator extends Hamburger {
        Hamburger hamburger;
        Decorator(Hamburger hamburger){
            this.hamburger = hamburger;
        }
    
        public Decorator() {
        }
    
        @Override
        public String product() {
            return hamburger.product();
        }
    }
    

    具体装饰类:

    /**
     * 装饰类,给汉堡加生菜
     */
    public class Lettuce extends Decorator{
        Lettuce(Hamburger hamburger) {
            this.hamburger = hamburger;
            setName(hamburger.getName()+"+生菜");
            setPrice(hamburger.getPrice()+2);
        }
        @Override
        public String product(){
            return getName()+": "+getPrice();
        }
    }
    

    具体装饰类:

    /**
     * 给火腿加鸡蛋
     */
    public class Age extends Decorator{
        Age(Hamburger hamburger) {
            this.hamburger = hamburger;
            setName(hamburger.getName()+"+鸡蛋");
            setPrice(hamburger.getPrice()+1.5);
        }
    
        @Override
        public String product() {
            return getName()+": "+getPrice();
        }
    }
    

    制作汉堡:

    public class SaleHamburger {
        public static void main(String[] args) {
            //鸡腿堡 +生菜 + 鸡蛋
            Decorator decorator = new Decorator(new Lettuce(new Age(new ChickenBurger())));
            //原味鸡腿堡
            Decorator decorator1 = new Decorator(new ChickenBurger());
            //鸡腿堡 +生菜
            Decorator decorator2 = new Decorator(new Lettuce(new ChickenBurger()));
            //鸡腿堡 +鸡蛋
            Decorator decorator3 = new Decorator(new Age(new ChickenBurger()));
    
            System.out.println(decorator.product());
            System.out.println(decorator1.product());
            System.out.println(decorator2.product());
            System.out.println(decorator3.product());
        }
    }
    

    结果:

    鸡腿堡+鸡蛋+生菜: 28.5
    鸡腿堡: 25.0
    鸡腿堡+生菜: 27.0
    鸡腿堡+鸡蛋: 26.5
    

    五)、装饰者模式在java Jdk中的应用

    装饰者模式应用在java Jdk api文档中的IO流机制

    I) :主题接口类:InputStream

       II):被装饰类:
    
             FileInputStream; StringBufferInputStream, ByteArrayInputStream
    
            装饰类:
    
             FilterInputStream
    
                    III): 具体实现类:
    
                             BufferInputStream; DataInputStream; LineNumberInputStream
    

    使用jdk设计好的装饰者模式,将流对象中的小写字母转为大写字母

    具体装饰类:

     继承FilterInpustream
    
    /**
     * java的Io流机制就使用了装饰模式
     *    InputStream: 接口类
     *    FileInputStream: StringBufferInputStream: ByteArrayInPutStream : 被装饰类
     *    FilterInputStream: 装饰类接口
     *       --: 具体装饰类
     *           BufferInputStream: DataInputStream: LineNumberInputStream
     *
     */
    
    import java.io.FilterInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    /**
     * 实现一个将流中的小写字母转为大写字母的装饰者对象
     */
    public class UpperCaseInputStream extends FilterInputStream {
    
        protected UpperCaseInputStream(InputStream in) {
            super(in);
        }
    
        @Override
        //读取流,将流中的小写字母转为大写字母
        public int read() throws IOException//单字符的读
        {
            int c=super.read();//这个super.read()就是调用上面super(in);的主题对象
            return c==-1?c:Character.toUpperCase((char)(c));
        }
    
        @Override
        public int read(byte[] b,int offset,int len) throws IOException//多字符的读
        {
            int result=super.read(b,offset,len);
            for(int i=0;i<result;i++)
            {
                b[i]=(byte)Character.toUpperCase((char)(b[i]));
            }
            return result;
        }
    }
    

    使用类:

    public class Test {
        public static void main(String[] args) {
            int c;
            try {
                InputStream in = new UpperCaseInputStream(new BufferedInputStream(
                        new FileInputStream("F:\test.txt")));
                while((c=in.read())>=0)
                {
                    System.out.print((char)c);
    
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
        }
    
    }
    金麟岂能忍一世平凡 飞上了青天 天下还依然
  • 相关阅读:
    bzoj 2832
    洛谷5月月赛
    P4705 玩游戏
    【bzoj4817】[Sdoi2017]树点涂色&&bzoj3779-重组病毒
    A
    P4715 「英语」Z 语言
    P4714 「数学」约数个数和
    P2860 [USACO06JAN]冗余路径Redundant Paths
    【BZOJ3252】攻略
    BZOJ 1706
  • 原文地址:https://www.cnblogs.com/Auge/p/11589342.html
Copyright © 2011-2022 走看看