zoukankan      html  css  js  c++  java
  • 装饰器模式和代理模式的区别

    参考:

    https://www.cnblogs.com/yanggb/p/10952843.html

    https://www.cnblogs.com/xiaolovewei/p/7751332.html

    装饰器模式和代理模式的区别

    代理模式和装饰器模式很像,这里用【到咖啡馆喝咖啡】作例子来讲解。

    基础实现

    定义一个咖啡的接口。

    复制代码
    public interface Coffee {
        /**
         * 打印当前咖啡里有什么
         */
        void printMaterial();
    }
    复制代码

    定义一个苦咖啡的实现。

    复制代码
    public class BitterCoffee implements Coffee {
        @Override
        public void printMaterial() {
            System.out.println("咖啡");
        }
    }
    复制代码

    定义一个默认的点咖啡逻辑。

    @Test
    public void orderCoffee {
        Coffee coffee = new BitterCoffee();
        coffee.printMaterial(); // 咖啡
    }

    装饰器模式

    你喝了一口咖啡,觉得有点苦,于是你就想加点糖。

    定义一个咖啡装饰器(加糖)。

    复制代码
    public class CoffeeDecorator implements Coffee {
        /**
         * 持有一个咖啡对象
         */
        private final Coffee coffee;
    
        public CoffeeDecorator(Coffee coffee) {
            this.coffee = coffee;
        }
    
        @Override
        public void printMaterial() {
            System.out.println("糖");
            this.coffee.printMaterial();
        }
    }
    复制代码

    定义一个咖啡加糖的应用逻辑。

    复制代码
    @Test
    public void addSugerIntoCoffee {
        Coffee coffee = new BitterCoffee(); // 点了一杯苦咖啡
        coffee = new SugarDecorator(coffee); // 给咖啡加了点糖
        coffee.printMaterial(); // 糖 咖啡
    }
    复制代码

    装饰器适用场景:我有一个对象,但是这个对象的功能不能使我满意(咖啡太苦了),我就拿装饰器给他装饰一下(给咖啡加糖)。

    代理模式(静态代理)

    约好的朋友来了,要给她点一杯咖啡,你知道咖啡很苦,决定直接点一杯加了糖的咖啡给她。

    定义一个加糖咖啡的类。

    复制代码
    public class CoffeeProxy implements Coffee {
        private final Coffee coffee;
    
        public CoffeeProxy() {
            this.coffee = new BitterCoffee();
        }
    
        @Override
        public void printMaterial() {
            System.out.println("糖");
            this.coffee.printMaterial();
        }
    }
    复制代码

    然后定义一个点加糖咖啡的逻辑。

    @Test
    public void addSugerIntoCoffee {
        Coffee coffee = new CoffeeProxy();
        coffee.printMaterial(); // 糖 咖啡
    }

    装饰器和代理模式的区别

    对装饰器模式来说,装饰者(Decorator)和被装饰者(Decoratee)都实现一个接口。对代理模式来说,代理类(Proxy Class)和真实处理的类(Real Class)都实现同一个接口。此外,不论我们使用哪一个模式,都可以很容易地在真实对象的方法前面或者后面加上自定义的方法。

    在上面的例子中,装饰器模式是使用的调用者从外部传入的被装饰对象(coffee),调用者只想要你把他给你的对象装饰(加强)一下。而代理模式使用的是代理对象在自己的构造方法里面new的一个被代理的对象,不是调用者传入的。调用者不知道你找了其他人,他也不关心这些事,只要你把事情做对了即可。

    装饰器模式关注于在一个对象上动态地添加方法,而代理模式关注于控制对对象的访问。换句话说,用代理模式,代理类可以对它的客户隐藏一个对象的具体信息。因此当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例;当使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰器的构造器。

    装饰器模式和代理模式的使用场景不一样,比如IO流使用的是装饰者模式,可以层层增加功能。而代理模式则一般是用于增加特殊的功能,有些动态代理不支持多层嵌套。

    代理和装饰其实从另一个角度更容易去理解两个模式的区别:代理更多的是强调对对象的访问控制,比如说,访问A对象的查询功能时,访问B对象的更新功能时,访问C对象的删除功能时,都需要判断对象是否登陆,那么我需要将判断用户是否登陆的功能抽提出来,并对A对象、B对象和C对象进行代理,使访问它们时都需要去判断用户是否登陆,简单地说就是将某个控制访问权限应用到多个对象上;而装饰器更多的强调给对象加强功能,比如说要给只会唱歌的A对象添加跳舞功能,添加说唱功能等,简单地说就是将多个功能附加在一个对象上。

    所以,代理模式注重的是对对象的某一功能的流程把控和辅助,它可以控制对象做某些事,重心是为了借用对象的功能完成某一流程,而非对象功能如何。而装饰模式注重的是对对象功能的扩展,不关心外界如何调用,只注重对对象功能加强,装饰后还是对象本身。

    总结

    对于代理类,如何调用对象的某一功能是思考重点,而不需要兼顾对象的所有功能;对于装饰类,如何扩展对象的某一功能是思考重点,同时也需要兼顾对象的其他功能,因为再怎么装饰,本质也是对象本身,要担负起对象应有的职责,被装饰者的职责一旦增加,作为装饰类也需要有相应的扩展,必然会造成编码的负担。

    设计模式本身是为了提升代码的可扩展性,灵活应用即可,不必生搬硬套,非要分出个所以然来,装饰器模式和代理模式的区别也是如此。

    代理模式和装饰者模式的区别

    转载自:http://www.cnblogs.com/jaredlam/archive/2011/11/08/2241089.html

    学习AOP时,教材上面都说使用的是动态代理,可是在印象中代理模式一直都是控制访问什么的,怎么又动态增加行为了,动态增加行为不是装饰器模式吗?于是 找了很多资料,想弄清楚这两者之间到底有什么区别。结果发现这一篇英文文章讲的很清晰,就翻译一下,供参考。       

            首先,让我们先看一下下面的这两个UML类图,他们分别描述了装饰器模式和代理模式的基本实现。

                        

                         

            这两个图可能使我们产生困惑。这两个设计模式看起来很像。对装饰器模式来说,装饰者(decorator)和被装饰者(decoratee)都实现同一个 接口。对代理模式来说,代理类(proxy class)和真实处理的类(real class)都实现同一个接口。此外,不论我们使用哪一个模式,都可以很容易地在真实对象的方法前面或者后面加上自定义的方法。

            然而,实际上,在装饰器模式和代理模式之间还是有很多差别的。装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话 说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模 式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

            我们可以用另外一句话来总结这些差别:使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。    

    代理模式:

    复制代码
    复制代码
    //代理模式
    public class Proxy implements Subject{

    private Subject subject;
    public Proxy(){
    //关系在编译时确定
    subject = new RealSubject();
    }
    public void doAction(){
    ….
    subject.doAction();
    ….
    }
    }
    复制代码
    复制代码
    复制代码
    复制代码
    //代理的客户
    public class Client{
    public static void main(String[] args){
    //客户不知道代理委托了另一个对象
    Subject subject = new Proxy();

    }
    }
    复制代码
    复制代码

    装饰模式:

    复制代码
    复制代码
    //装饰器模式
    public class Decorator implements Component{
    private Component component;
    public Decorator(Component component){
    this.component = component
    }
    public void operation(){
    ….
    component.operation();
    ….
    }
    }
    复制代码
    复制代码
    复制代码
    复制代码
    //装饰器的客户
    public class Client{
    public static void main(String[] args){
    //客户指定了装饰者需要装饰的是哪一个类
    Component component = new Decorator(new ConcreteComponent());

    }
    }
    复制代码

     

  • 相关阅读:
    倒计时浏览器跳转JavaScript
    C#.NET中使用存储过程的方法及其优点
    利用GridView控件导出其他文件(导出Excel,导出Word文件)
    c#.net用JavaScript实现 时钟显示
    程序执行一半后可以跳出对话框选择是否继续执行
    FreeMarker 中文官方参考手册 For Freemarker 2.3.23
    Spring技术内幕之Spring Data JPA-自定义Repository实现
    ehcache入门基础示例
    SpringData JPA 接口和方法
    Springboot中使用缓存
  • 原文地址:https://www.cnblogs.com/xuwc/p/14022327.html
Copyright © 2011-2022 走看看