最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 设计模式-装饰者模式

    正文概述 掘金(lightingsui)   2021-02-23   342

    JDK中,使用到了很多设计模式,例如装饰者模式、观察者模式,它广泛存在于Java I/O中,说实话,刚学习Java I/O时有点懵,他的类是在太多了,可供选择的就十多种,还有很多叫不上名字的IO类,当时一度怀疑自己不是学编程的选手。

    今天重点所说的就是装饰者模式,也算是一个总结吧

    场景

    这个场景很简单,为某冷饮店制造一个收费系统。

    他的业务是这样的,主要的为饮品收费,其他的收费项目为配料收费,例如在点了饮品的基础上增加了珍珠、椰果等配料,然后计算总的费用。

    问题解决

    大家的想法可能是这样:当购买哪一种类的饮料时,就创建出来这个对象,如果想另外增加配料,再创建出来配料类的对象,最后计算出价格。但是怎么实现呢,要讲弹性,以方便日后的扩展

    最初可以设计出一个抽象类,将饮料抽象出来

    public abstract class Beverage {
        public String getDescription() {
            return "unknown beverage";
        }
        
        public abstract float cost();
    }
    

    接下来就是围绕这个类展开,从而引出装饰者模式。在往下进行之前,先说一个设计原则:

    对修改关闭,对扩展开放(开放封闭原则)

    新创建的饮料可以完全继承自Beverage类,并且实现其中的cost()和覆盖getDescription(),我们创建两个饮料类拿铁(Latte)和卡布奇诺(Cappuccino)

    public class Latte extends Beverage{
        @Override
        public float cost() {
            return 12.1f;
        }
    
        @Override
        public String getDescription() {
            return "Latte";
        }
    }
    
    public class Cappuccino extends Beverage{
        @Override
        public float cost() {
            return 7.21f;
        }
    
        @Override
        public String getDescription() {
            return "Cappuccino";
        }
    }
    

    在装饰者模式中,这两类被称为被装饰者,而这个场景中,真正的装饰者是调料,我们看一下调料的抽象类

    public abstract class Flavour extends Beverage {
        @Override
        public String getDescription() {
            return "Flavour";
        }
    }
    

    另外再去实现两种调料珍珠(Pearl)、椰肉(Coconut)

    public class Pearl extends Flavour{
    
        private Beverage beverage;
    
        public Pearl(Beverage beverage) {
            this.beverage = beverage;
        }
    
        @Override
        public float cost() {
            return beverage.cost() + 2.1f;
        }
    
        @Override
        public String getDescription() {
            return beverage.getDescription() + " Pearl";
        }
    }
    
    public class Coconut extends Flavour{
    
        private Beverage beverage;
    
        public Coconut(Beverage beverage) {
            this.beverage = beverage;
        }
    
        @Override
        public float cost() {
            return beverage.cost() + 4.2f;
        }
    
        @Override
        public String getDescription() {
            return beverage.getDescription() + " Coconut";
        }
    }
    

    目前我们就完成了所有所需的类,UML类图如下

    设计模式-装饰者模式

    其中PearlCoconut装饰者类,负责装饰LatteCappuccino,且装饰者与被装饰者应该有相同的类型,即Beverage

    如果我想点一份Latte,并且加上Pearl配料,应该如何计算金钱呢

    public class Consumption {
        public static void main(String[] args) {
            Latte latte = new Latte();
    
            Pearl pearl = new Pearl(latte);
    
            System.out.println("total cost: " + pearl.cost());
            System.out.println(pearl.getDescription());
        }
    }
    

    最后打印出

    total cost: 14.200001
    Latte Pearl
    

    这样的扩展是否很灵活呢,通过装饰者对被装饰者的装饰,在不修改原有代码的前提下,完成了如此具有弹性的功能。

    此时我想点一份Cappuccino加两份Coconut

    public class Consumption {
        public static void main(String[] args) {
            Cappuccino cappuccino = new Cappuccino();
    
            Coconut coconut = new Coconut(cappuccino);
            coconut = new Coconut(coconut);
    
            System.out.println("total cost: " + coconut.cost());
            System.out.println(coconut.getDescription());
        }
    }
    

    输出

    total cost: 15.61
    Cappuccino Coconut Coconut
    

    I/O类中的装饰者模式

    设计模式-装饰者模式

    以输入流举例(输出流也是同理),InputStream可以看成是抽象的组件,FilterInputStream可以看成是抽象装饰者,而她下面的类就可以看成是具体的装饰者,他们都是对以及InputStream的装饰

    既然说的这么细了,那就进到FilterInputStream的源码中看一看

    public class FilterInputStream extends InputStream {
       	// 被装饰的对象
        protected volatile InputStream in;
    
        protected FilterInputStream(InputStream in) {
            this.in = in;
        }
    
        public int read() throws IOException {
            // 调用被装饰的对象方法
            return in.read();
        }
        
        // 部分省略
    }
    

    他和咱们上面饮品的例子结构差不多吧

    Java I/O也引出来了装饰者模式的一个缺点,利用装饰者模式,常常造成设计中有大量的小类,数量实在太多,可能会给使用此API的程序员造成困扰。

    我们在知道了Java I/O类的原理后,可以自己动手写一个Java I/O类的API,功能是将文件中的全部大写转换为小写

    public class LowerFileInputStream extends FileInputStream {
    
        private FileInputStream fileInputStream;
    
        @Override
        public int read() throws IOException {
            int read = super.read();
            return read == -1 ? read : Character.toLowerCase((char)read);
        }
    
        @Override
        public int read(byte[] b) throws IOException {
            int read = super.read(b);
    
            for (int i = 0; i < read; i++) {
                b[i] = (byte) Character.toLowerCase((char) b[i]);
            }
    
            return read;
        }
    
        public LowerFileInputStream(String name) throws FileNotFoundException {
            super(name);
            fileInputStream = new FileInputStream(name);
        }
    }
    

    测试

    public static void main(String[] args) throws Exception {
        LowerFileInputStream lowerFileInputStream = new LowerFileInputStream("lower.txt");
    
        int read = 0;
    
        StringBuilder stringBuilder = new StringBuilder();
    
        while((read = lowerFileInputStream.read()) != -1) {
            stringBuilder.append((char)read);
        }
    
        lowerFileInputStream.close();
    
        System.out.println(stringBuilder.toString());
    }
    

    输出

    # 原始文件内容
    I am Programmer
    
    # 输出内容
    i am programmer
    

    总结

    本篇文章以某饮料公司的消费系统为场景,引出了装饰者模式,并且得到了一个重要的设计原则:

    类只对扩展开放,对修改关闭(开放封闭原则)

    还总结出Java IO中存在着大部分的装饰者模式,同时也指出了装饰者模式的不足之处。

    最后自己动手实现了一个依靠装饰者模式自定义的IO类。


    起源地下载网 » 设计模式-装饰者模式

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元