• 设计模式之——Decorator模式


    Decorator模式又叫装饰者模式,这种模式是为了满足Java开发的“面向扩展开放,面向修改闭源”的开发原则设计出来的。

    在装饰者模式中,不修改源类的代码,却能修改源类中方法的功能。下面就以Angelababy化妆为例,详细介绍一下为什么需要装饰者模式,以及装饰者模式怎么实现:

    • 先介绍angelababy类
    package site.wangxin520.gof.decorator;
    
    /**
     * 装饰者模式
     * baby化妆类
     * @author wangXgnaw
     *
     */
    public class Angelababy {
    
        /**
         * 画眉毛
         */
        public void meimao(){
            System.out.println("baby自己画眉毛");
        }
        
        /**
         * 画眼影
         */
        public void yanying(){
            System.out.println("baby自己画眼影");
        }
        
        /**
         * 点腮红
         */
        public void saihong(){
            System.out.println("baby自己画腮红");
        }
        
        /**
         * 画口红
         */
        public void kouhong(){
            System.out.println("baby自己涂口红");
        }
        
    }

    在baby类里面,有四个方法,分别对应着baby自己给自己化妆的步骤。

    • 装饰者模式测试类,首先是baby给自己化妆
    package site.wangxin520.gof.decorator;
    
    /**
     * 装饰者模式的测试类
     * @author wangXgnaw
     *
     */
    public class Test {
    
        public static void main(String[] args) {
            
            Angelababy ab =new Angelababy();
            
            //baby给自己化妆
            ab.meimao();
            ab.yanying();
            ab.saihong();
            ab.kouhong();
            
        }
        
    }
    • 结果是:

    image

    由此可见,baby自己给自己化妆成功。

    虽然这样自己是能给自己化妆的,不过怎么也没有专业的化妆师给自己化妆的效果好。所以baby找来了一个专业的化妆师A。

    • DresserA装饰者模式的的第一种实现,继承
    package site.wangxin520.gof.decorator;
    
    /**
     * 装饰者模式的装饰者
     * 化妆师A
     * @author wangXgnaw
     *
     */
    public class DresserA extends Angelababy{
    
        /**
         * 重写了画眼影的方法,在画眼影之后,又加上了刷睫毛
         */
        @Override
        public void yanying() {
            super.yanying();
            //后面输出有误,注意
            System.out.println("给anglebaby刷睫毛");
        }
    
        /**
         * 重写了涂口红的方法
         * 在涂口红之前,给她涂了桃红色红
         */
        @Override
        public void kouhong() {
            System.out.println("给anglebaby涂了桃红色的口红");
            super.kouhong();
        }
    
        
        
    }
    • 测试类
    package site.wangxin520.gof.decorator;
    
    /**
     * 装饰者模式的测试类
     * @author wangXgnaw
     *
     */
    public class Test {
    
        public static void main(String[] args) {
            
            //化妆师a给baby化妆
            Angelababy ab=new DresserA();
            
            //anglebaby化妆
            ab.meimao();
            ab.yanying();
            ab.saihong();
            ab.kouhong();
            
        }
        
    }
    • 结果:

    image

    由此可见,baby的眼影和口红都不是自己画的了,而是别人帮她画的。这就是第一种继承的方法。不过这种方法是有局限性的,如果anglebaby类是final修饰的不可继承的话,那就尴尬了。另外,化妆师实际上是不能去继承被化妆的那个人的,如果化妆师继承了anglebaby而不是小海绵继承的话,黄晓明可能会哭晕在厕所!

    下面就介绍第二种方法:面向接口开发

    首先提取出一个公共接口,化妆的interface接口

    • MakeUp 化妆的接口
    package site.wangxin520.gof.decorator;
    
    /**
     * 化妆的接口
     * @author wangXgnaw
     *
     */
    public interface MakeUp {
    
        /**
         * 画眉毛
         */
        public void meimao();
        
        /**
         * 画眼影
         */
        public void yanying();
        
        /**
         * 点腮红
         */
        public void saihong();
        /**
         * 画口红
         */
        public void kouhong();
        
    }
    • 让anglebaby实现该接口
    package site.wangxin520.gof.decorator;
    
    /**
     * 装饰者模式
     * baby化妆类
     * @author wangXgnaw
     *
     */
    public class Angelababy implements MakeUp{
    
        /**
         * 画眉毛
         */
        @Override
        public void meimao(){
            System.out.println("baby自己画眉毛");
        }
        
        /**
         * 画眼影
         */
        @Override
        public void yanying(){
            System.out.println("baby自己画眼影");
        }
        
        /**
         * 点腮红
         */
        @Override
        public void saihong(){
            System.out.println("baby自己画腮红");
        }
        
        /**
         * 画口红
         */
        @Override
        public void kouhong(){
            System.out.println("baby自己涂口红");
        }
        
    }
    • DresserB 化妆师b,同样也实现这个接口。不过在构造化妆师B的时候,得把anglebaby传进来
    package site.wangxin520.gof.decorator;
    
    /**
     * 化妆师B
     * @author wangXgnaw
     *
     */
    public class DresserB implements MakeUp{
    
        //用于保存化妆师B的顾客
        private MakeUp makeUp=null;
        
        /**
         * 在构造化妆师B的类的时候,传入顾客
         * @param makeUp
         */
        public DresserB(MakeUp makeUp) {
            this.makeUp=makeUp;        
        }
        
        /**
         * 实现化妆的类,当调用顾客对应的类的时候,实现的还是顾客的功能
         * 当需要增加新功能时候,只需要在对应的方法前面或者后面实现即可
         */
        @Override
        public void meimao() {
            makeUp.meimao();
        }
    
        
        /**
         * 化妆师B觉得涂眼影不好看,所以不涂了
         * 只给baby贴了双眼皮贴
         */
        @Override
        public void yanying() {
            System.out.println("贴双眼皮贴");
        }
    
        /**
         * 化妆师B想在baby涂腮红之前,就加上摸粉底
         */
        @Override
        public void saihong() {
            System.out.println("摸粉底");
            makeUp.saihong();
        }
    
        @Override
        public void kouhong() {
            makeUp.kouhong();
        }
    
    }
    • 测试类
    package site.wangxin520.gof.decorator;
    
    /**
     * 装饰者模式的测试类
     * @author wangXgnaw
     *
     */
    public class Test {
    
        public static void main(String[] args) {
            
            //化妆师B给baby化妆
            
            Angelababy ab=new Angelababy();
            
            DresserB db=new DresserB(ab); 
            
            db.kouhong();
            db.yanying();
            db.saihong();
            db.meimao();
            
        }
        
    }
    • 结果为:

    image

    由此可见,装饰者模式生效了。

    不过面向接口的这种装饰者模式也有一个小问题,那就是必须重写接口的实现类,并且在实现类里面调用传入参数的所有方法。

    当接口中抽象方法很多的时候,这就很麻烦。

    为了解决这种麻烦,就提出了一种新的设计模式,即当需要使用哪种方法的时候,就修饰哪种方法,否则就不改变。这种新的模式就是动态代理模式。后面我们将详细介绍!

  • 相关阅读:
    数组中的每一个对象执行一次方法:makeObjectsPerformSelector
    $.each() each
    JQ js选择节点操作
    Sublime Text 3 快捷键
    TotoiseSVN的基本使用方法
    Hbuilder快捷键
    获取网页内容区域各种高/宽汇总
    TP操作
    xhr接收php://output的二进制文件,并转换成excel表格
    Go语言的%d,%p,%v等占位符的使用
  • 原文地址:https://www.cnblogs.com/wangxinblog/p/7684507.html
Copyright © 2020-2023  润新知