美文网首页
装饰者模式

装饰者模式

作者: 朕要回幼儿园 | 来源:发表于2019-02-19 11:58 被阅读0次

    1、现有咖啡馆订单系统项目(示例项目)

    咖啡种类:Espresso、ShortBlack、LongBlack、Decaf

    调料:Milk、Soy、Chocolate

    实现客户点单时,点个Decaf+milk+Soy可以立刻知道它的价格和描述,点个Decaf需要知道它的价格和描述

    2、以传统的面向对象的原则设计这个项目。

    1)定义一个超类

    public abstract class Drink{

        protected Stringdescription ="";

        protected float price;

        protected abstract StringgetDescription();

        protected abstract float cost();

    }

    2)把所有的能单点的饮料都封装好描述和价格,继承Drink

    public class Coffee extends Drink {

        public Coffee(){

            this.price =5.0f;

            this.description ="coffee"+"-"+this.price;

        }

        @Override

        protected String getDescription() {

            return this.description;

        }

        @Override

        protected float cost() {

            return this.price;

        }

    }

    3)如果要多点的则把单点的饮料作为引用放在此类中

    public class CoffeeAndMilkextends Drink {

        private Coffeecoffee;

        private Milkmilk;

        public CoffeeAndMilk(Coffee coffee,Milk milk){

            this.coffee = coffee;

            this.milk = milk;

        }

    @Override

        protected StringgetDescription() {

            return this.coffee.getDescription()+"&&"+this.milk.getDescription();

        }

    @Override

        protected float cost() {

            return this.milk.cost()+this.coffee.cost();

        }

    }

    4)运行结果

    Coffee coffee =new Coffee();

    System.out.println("描述:"+coffee.getDescription());

    System.out.println("价格:"+coffee.cost());

    System.out.println("********************************************************");

    Milk milk =new Milk();

    System.out.println("描述:"+milk.getDescription());

    System.out.println("价格:"+milk.cost());

    System.out.println("********************************************************");

    CoffeeAndMilk coffeeAndMilk =new CoffeeAndMilk(coffee,milk);

    System.out.println("描述:"+coffeeAndMilk.getDescription());

    System.out.println("价格:"+coffeeAndMilk.cost());

    结果:

    描述:coffee-5.0

    价格:5.0

    ********************************************************

    描述:milk-3.0

    价格:3.0

    ********************************************************

    描述:coffee-5.0&&milk-3.0

    价格:8.0

    问题:这样设计的话,增删饮料种类就很有问题,添加新的混合饮料的话非常麻烦,需要改动两个地方,新增单品饮料,新增复合饮料。搭配品种更不方便,假如我现在coffee不和牛奶混合,coffee和啤酒混合,就必须新建个复合饮料coffeeAndBeer类,扩展性不好,维护起来麻烦。

    3、接下来我采用装饰者模式的方式改造代码

       1)封装一个抽象接口,在装饰者模式中的角色是抽象组件角色。

        public abstract class Drink{

            public Stringdescription ="";

            private float price =0f;

            public void setDescription(String description) {

                this.description = description;

            }

            public void setPrice(float price) {

                this.price = price;

            }

            public StringgetDescription() {

                return description+"-"+this.getPrice();

            }

           public float getPrice() {

                return price;

            }

            public abstract float cost();

    }

        2)为这个抽象接口提供具体实现类,这里我还封装了一个中间层,这个抽象接口的具体实现应该是这个中间层的具体实现

    Coffee中间层代码:

        public class Coffeeextends Drink {

            @Override

            public float cost() {

                return super.getPrice();

            }

        }

    具体实现Decaf代码:

    public class Decafextends Coffee {

        public Decaf(){

            super.setDescription("Decaf");

            super.setPrice(3.0f);

        }

    }

    3)创建抽象装饰接口Decorator,包含抽象接口Drink的引用

    public class Decorator extends Drink {

    private Drinkobj;

        public Decorator(Drink obj){

            this.obj = obj;

        }

        @Override

        public float cost() {

            return super.getPrice()+obj.cost();

        }

        @Override

        public StringgetDescription() {

            return super.description+"-"+super.getPrice()+"&&"+obj.getDescription();

        }

    }

    4)创建具体装饰接口的实现类Chocolate、Milk,负责具体的装饰

    Chocolate代码:

    public class Chocolate extends Decorator {

        public Chocolate(Drink obj) {

            super(obj);

            super.setDescription("Chocolate");

            super.setPrice(3.0f);

        }

    }

    Milk代码:

    public class Milkextends Decorator {

    public Milk(Drink obj) {

    super(obj);

            super.setDescription("Milk");

            super.setPrice(2.0f);

        }

    }

    5)运行。

    若你需要单品咖啡,你可以直接new

    Drink order =null;

    order =new Decaf();

    System.out.println("order one price:"+order.cost());

    System.out.println("order one desc:"+order.getDescription());

    运行结果:

    order one price:3.0

    order one desc:Decaf-3.0

    若你需要巧克力+牛奶+decaf,你可以现new一个单品(具体组件角色),然后利用装饰接口实现类进行装饰

    Drink order =null;

    order =new Decaf();

    order =new Chocolate(order);

    order =new Milk(order);

    System.out.println("order two price:"+order.cost());

    System.out.println("order two desc:"+order.getDescription());

    运行结果:

    order two price:8.0

    order two desc:Milk-2.0&&Chocolate-3.0&&Decaf-3.0

    以后需要怎样搭配就用装饰接口的实现类装饰具体饮料就行,这就解决了前面传统面向对象方式出现的问题,每新出一种混合饮料,都要新建一个混合饮料类的问题。

    4、装饰模式概念

    1)装饰者模式

        动态地将功能附加到对象上,在对象功能扩展方面,它比继承更有弹性。装饰者模式又叫做包装模式。通过一种对客户端透明的方式来扩展对象的功能,是继承关系的一种替代方案。

    2)装饰者模式的结构

    3)装饰者模式的角色和职责

        1、抽象组件角色:一个抽象接口,是被装饰类和被装饰类继承的父接口(本例中的抽象组件角色是Drink)

        2、具体组件角色:为抽象组件的实现类(本例中的具体组件角色是Coffee)

        3、抽象装饰角色:包含一个组件的引用,并定义了与抽象组件一致的接口(本例中的抽象装饰角色是Decorator)

        4、具体装饰角色:为抽象装饰角色的实现类。负责具体的装饰。(本例中的具体装饰角色是Milk和Chocolate)

    5、jdk中的装饰者模式

        我们来看下java中的I/O类

        

    接下来我们分析下,这些类充当的装饰者模式中的角色与职责

    1)InputStream:这无疑是抽象组件角色,顶级接口

    2)FileInputStream/StringBufferInputStream/ByteArrayInputStream:这些类是具体组件角色,是被装饰者角色

    3)FilterInputStream:这个类是抽象装饰角色,是装饰者角色

    4)BufferInputStream/DataInputStream/LineNumberInputStream:这些类是具体装饰角色,负责具体的装饰。

    接着我们自己实现一个具体的装饰角色,自己编写一个自己的I/O装饰者,这个装饰者的功能是读取文件的字符全部大写

    1)继承装饰者角色FilterInputStream

    2)重写int read()方法和int read(byte[] b, int offset, int len)方法

    public class UpperCaseInputStreamextends FilterInputStream {

    public UpperCaseInputStream(InputStream in) {

    super(in);

        }

    @Override

        public int read()throws IOException {

    int c =super.read();

            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

    b[i]=(byte)Character.toUpperCase((char)(b[i]));

            }

    return result;

        }

    }

    3)运行

    public static void main(String[] args) {

    int c =0;

        InputStream inputStream =null;

        try {

    inputStream =new UpperCaseInputStream(new BufferedInputStream(new FileInputStream("D:\\test.txt")));

            while((c=inputStream.read())>=0) {

    System.out.print((char)c);

            }

    }catch (IOException e) {

    e.printStackTrace();

        }finally {

    try {

    inputStream.close();

            }catch (IOException e) {

    e.printStackTrace();

            }

    }

    }

    运行结果

    test.txt文件中的内容

    abcdefghjzklmen

    运行结果:

    ABCDEFGHJZKLMEN

    相关文章

      网友评论

          本文标题:装饰者模式

          本文链接:https://www.haomeiwen.com/subject/abizeqtx.html