zoukankan      html  css  js  c++  java
  • 03 适配器 代理 外观 装饰者

    这 4 个模式很相近

    适配器模式:包装另一个对象,并提供不同的接口
    外观模式:包装许多对象,以简化他们的接口。
    装饰者模式:包装另一个对象,并提供额外的行为。(额外的行为可以是灵活的多个装饰器(class)完成的, 也就是可以 n:1)
    代理模式:包装另一个对象,并控制对它的访问. (只能 1:1 的代理一个对象)

    区别

    从它们的定义中可以总结出以下几点区别:

    1)适配器模式强调的是转换接口。举例:JDBC是java定义的数据库操作规范,其已经定义好了操作数据库的接口,假设一个数据库厂商提供了一套操作数据库的接口,但是该接口并不符合JDBC的规范,也就是说JDBC规范规定的接口和数据库厂商提供的接口不一致。这个时候就可以使用适配器模式将数据库厂商的接口转换成JDBC规范要求的接口。其他三种模式均不提供接口转换的功能。

    2)外观模式强调的是包装多个对象,以简化他们的接口。想象这样一种场景:你家的电器都是智能控制的,当你回家的时候你要打开空调、打开电视、打开热水器等等,这些都需要你自己一个个的去操作,这个时候你就会有这样一种需求,就是当你到家的时候只要进行一个操作,就能依次打开空调、电视、热水器等。这个时候就可以使用外观模式将空调、电视、热水器等的接口进行包装,只对外提供一个按钮。其他三种模式均强调的是对一个对象的包装。

    3)装饰者模式强调的是为被装饰对象增加额外的行为。举例:java.io包。

    4)代理模式强调的是对被代理对象进行控制。这些控制体现在很多方面,比如安全、权限控制等。

    适配器模式和外观模式容易和其他模式进行区分。下面重点比较装饰者模式和代理模式。

    5)装饰者模式和外观模式主要的区别就是,装饰者模式从来不创建被装饰的对象,它总是添加新功能到已经存在的对象上面;而代理模式在被代理对象不存在的时候会创建被代理对象。

    6)装饰者模式可以通过嵌套装饰添加多重额外功能,而代理模式一般不推荐使用嵌套代理。

    适配器模式

    JAVA.IO 的各种流之间的转换, 实际上是使用的适配器.  

    package com.leon.design;
    
    import java.lang.annotation.Target;
    
    public class ClientAdapter {
    
        public static void main(String[] args) {
            ClientAdapter clientAdapter = new ClientAdapter();    
            Adpatee adpatee = new Adpatee();        
            AdapterTarget target = new MyAdapter(adpatee);    
            // 客户本来的需求充电
            clientAdapter.charge(target);
        }
        public void charge(AdapterTarget t) {
            t.handleRequest();
        }
    }
    
    package com.leon.design;
    
    public interface AdapterTarget {
        void handleRequest();
    }
    
    package com.leon.design;
    
    /**
     * 适配器
     */
    public class MyAdapter implements AdapterTarget {
        private Adpatee adpatee;
        @Override
        public void handleRequest() {
            adpatee.request();
        }    
        public MyAdapter(Adpatee adpatee) {
            this.adpatee = adpatee;
        }
    }
    
    package com.leon.design;
    
    /**
     * 被适配的类
     *
     */
    public class Adpatee {
        
        // 欧洲的具体充电方法
        public void request() {
            System.out.println("欧洲充电功能");
        }
    }

    代理模式

     

    核心功能唱歌, 并没有改变, 代理帮助歌星做一些其他的地方, 面向切面编程AOP, 与装饰者模式的区别是, 装饰者是针对唱歌本身, 增加一些装饰, 为装饰对象增加一些额外的功能. 控制唱歌的方法, 要想让明星唱歌, 必须要先找代理谈价格.

    静态代理: 我们自己定义代理类

    动态代理: 有程序自动生成代理类

    静态代理:

    package com.leon.design;
    
    public class ClientStaticProxy {
    
        public static void main(String[] args) {
            StarPlay singer = new SingerStar("周杰伦");
            StarPlay dancer = new DancerStar("王天一");
            
            ProxyStar proxySinger = new ProxyStar(singer);
            ProxyStar proxyDance = new ProxyStar(dancer);
            
            proxySinger.play();
            proxyDance.play();
        }
    
    }
    
    package com.leon.design;
    
    public interface StarPlay {
        void play();
    }
    
    
    package com.leon.design;
    
    public class ProxyStar implements StarPlay{
        StarPlay target;
        
        // 代理人需要帮助star 收取费用和收尾工作
        @Override
        public void play() {
            System.out.println("Proxy: Play 之前, 先收钱");
            target.play();
            System.out.println("Proxy: Play 之后, 再收尾");
        }
    
        public ProxyStar(StarPlay target) {
            this.target = target;
        }
        
    }
    
    package com.leon.design;
    
    public class SingerStar implements StarPlay{
        private String singerName;
        @Override
        public void play() {
            System.out.println("I am " + singerName + ", i can sing.");
            
        }
        public SingerStar(String singerName) {
            this.singerName = singerName;
        }
    
    }
    
    package com.leon.design;
    
    public class DancerStar implements StarPlay{
        private String dancerName;
        @Override
        public void play() {
            System.out.println("I am "+ dancerName +", I can dance.");
            
        }
        
        public DancerStar(String dancerName) {
            this.dancerName = dancerName;
        }
    
    }

    动态代码:

    代理对象不需要实现接口, 但是目标对象(RealSubject) 仍然需要实现接口.

    代理对象动态生成,利用 JDK API 在内存中动态构建代理对象 

    package com.leon.design;
    
    public class ClientDynimicProxy {
    
        public static void main(String[] args) {
            // 目标对象, 也是被代理对象
            StarPlay target = new SingerStar("宋祖英");
            // 给目标对象创建代理, 可以强转为接口
            StarPlay proxyInstance = (StarPlay) new ProxyFactory(target).getProxyInstance();
            
            proxyInstance.play();
            
            // 输出是 proxyInstance: com.leon.design.SingerStar@6bc7c054
    //        System.out.println("proxyInstance: " + proxyInstance);
            // proxyInstance's type: class com.sun.proxy.$Proxy0
    //        System.out.println("proxyInstance's type: " + proxyInstance.getClass());
        }
    
    }
    
    package com.leon.design;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;;
    
    // 这里的代码完全是可以直接利用的, 目标对象是 Object 类型的
    public class ProxyFactory {
        private Object target;
        
        public ProxyFactory(Object target) {
            this.target = target;
        }
        
        // 给目标对象动态生成一个代理对象
        // 参数1 ClassLoader loader: 制定当前目标对象使用的类加载器
        // 参数2 Class<?>[] interface: 目标对象实现的接口类型, 使用泛型方式
        // 参数3 InvocationHandler h: 事件处理, 执行目标对象方法时, 会帮当前目标对象的方法作为参数传入
        public Object getProxyInstance() {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
                // 每次调用目标对象方法时, 都会调用这个invoke, 所以可以从这加代理内容
                // 如果有多个方法调用时, 每个方法加入什么内容, 从这个人理解可以增加 if,else 判断
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("JDK代理开始");
                    // 通过反射, 调用目标对象的方法
                    Object retureVal = method.invoke(target, args);
                    System.out.println("JDK代理结束");
                    return retureVal;
                }
            });
        }
    }
    
    package com.leon.design;
    
    public interface StarPlay {
        void play();
    }
    
    package com.leon.design;
    
    public class SingerStar implements StarPlay{
        private String singerName;
        @Override
        public void play() {
            System.out.println("I am " + singerName + ", i can sing.");
            
        }
        public SingerStar(String singerName) {
            this.singerName = singerName;
        }
    
    }
    
    package com.leon.design;
    
    public class DancerStar implements StarPlay{
        private String dancerName;
        @Override
        public void play() {
            System.out.println("I am "+ dancerName +", I can dance.");
            
        }
        
        public DancerStar(String dancerName) {
            this.dancerName = dancerName;
        }
    
    }

    还有一种叫做 Cglib 代理, 不需要实现接口, 就能实现代理. Cglib 包的底层时通过使用字节码处理框架 ASM 来转换字节码生成的新类.

    装饰者模式

    动态为一个对象增加一个新的功能, 被装饰新功能的方法, 可以先通过接口设计出来, 然后让被装饰的类和装饰类都实现这个接口.

    关键是可以灵活的添加装饰对象.

    角色:

    接口/抽象类: Component,定义对象的方法

    ConcreteComponent(被装饰类)(具体对象实现接口方法)

    Decorator: 装饰器, 装饰的是ConcreteComponent的方法, 它含有一个被装饰的对象

     举例: 星巴克 咖啡的问题: 有单品咖啡, 然后, 还有很多收费的调料, 比如加 牛奶, 加巧克力, 加草莓, 加冰块等等很多很多.

    那么,如果有客户要加 2份巧克力+1份牛奶+1份草莓, 我们不可能根据客户的需求都创建一个新的类(排列组合,很多种可能), 那怎么办呢?

    解决办法就是递归思维, 我们先用一种调料来“装饰”, 之后把装饰完之后的还继续当做是"单品咖啡",继续用milk 来装饰, 这样递归操作, 直到装饰完所有的调料.

    举例中的对象:

    abstract BasicCoffee (基础类)

      private String description

      private int cost

      description() 方法, 介绍这杯咖啡的情况, 例如, 单品咖啡,加糖咖啡等

      cost()  // 具体计算咖啡的价格

    ItalianCoffee(被装饰类)  单品意大利式咖啡, 继承 BasicCoffee

    USACoffee(被装饰类)  单品美国咖啡, 继承 BasicCoffee

    DecoratorCoffee(装饰器类) 用来装饰这个单品的, 持有单品的对象, 继承 BasicCoffee, 这个类可以定义为抽象类

    MilkDecorator(牛奶作料) 给单品加牛奶, 继承 DecoratorCoffee, 这才是真正的具体装饰类

    SugarDecorator(糖作料) 给单品加糖, DecoratorCoffee, 这才是真正的具体装饰类

    ChocolatesDecorator(巧克力作料) 给单品加巧克力 DecoratorCoffee, 这才是真正的具体装饰类

    现在需要2份巧克力 + 1份牛奶

    package com.leon.design;
    
    public interface BaseCoffee {
        void getCurrentDescription();
        int getCurrentCost();
    }
    
    package com.leon.design;
    
    public class ItalianCoffee implements BaseCoffee{
        private String description = "+ ItalianCoffee 1 cup";
        private int cost = 20;
        
        @Override
        public int getCurrentCost() {
            return cost;
        }
    
        @Override
        public void getCurrentDescription() {
            System.out.print(this.description + ":price=" + cost + ". ");    
        }
    }
    
    package com.leon.design;
    
    public class UASCoffee implements BaseCoffee{
        private String description = "+ USACoffee 1 cup";
        private int cost = 15;
    
        
        @Override
        public int getCurrentCost() {
            return cost;
        }
    
        @Override
        public void getCurrentDescription() {
            System.out.print(this.description + ":price=" + cost + ". ");
        }
    }
    
    package com.leon.design;
    
    public abstract class DecoratorCoffee implements BaseCoffee{
        BaseCoffee basecoffee;
        
        public DecoratorCoffee(BaseCoffee basecoffee) {
            this.basecoffee = basecoffee;
        }
    
        @Override
        public abstract void getCurrentDescription(); 
        
    
        @Override
        public abstract int getCurrentCost(); 
    }
    
    package com.leon.design;
    
    public class MilkDecorator extends DecoratorCoffee{
        private String description = "+ milk 1 time";
        private int cost = 5;
        
        public MilkDecorator(BaseCoffee basecoffee) {
            super(basecoffee);
        }
        
        @Override
        public void getCurrentDescription() {
            basecoffee.getCurrentDescription();
            System.out.print(this.description + ":price=" + this.getCurrentCost() + ". ");
        }
    
        @Override
        public int getCurrentCost() {
            return basecoffee.getCurrentCost() + this.cost;
        }
        
    }
    
    package com.leon.design;
    
    public class ChocolateDecorator extends DecoratorCoffee{
        private String description = "+ Chocolate 1 time";
        private int cost = 10;
        
        public ChocolateDecorator(BaseCoffee basecoffee) {
            super(basecoffee);
        }
        
        @Override
        public void getCurrentDescription() {
            basecoffee.getCurrentDescription();
            System.out.print(this.description + ":price=" + this.getCurrentCost() + ". ");
        }
    
        @Override
        public int getCurrentCost() {
            return basecoffee.getCurrentCost() + this.cost;
        }
        
    }
    
    package com.leon.design;
    
    public class DecoratorClient {
    
        public static void main(String[] args) {
            BaseCoffee coffee = new ItalianCoffee();
            
            // 放牛奶 1 次
            coffee = new MilkDecorator(coffee);
    
            // 放牛奶 1 次
            coffee = new MilkDecorator(coffee);
            // 放巧克力 1 次
            coffee = new ChocolateDecorator(coffee);
            coffee.getCurrentDescription();
        }
    
    }

    在 JDK 的 IO 流的 InputStream 是一个抽象类, 这里边的继承关系, 就是一种装饰类.

    外观模式(过程模式)

    对 sub system中的一组接口提供一个外部界面(外观). 一种封装的思想. 一般情况下, 都会潜移默化的使用它.

    家庭影院项目: 我们可以做一个 Facade(面板)类, 封装这个类的方法, 从而提供一个统一的接口给用户(ready, play, pause, end)

    DVD 播放器(开,关,播放, 暂停)

    投影仪(准备:插电源, 放画布 播放: 启动, 暂停:没有, 结束:拔电源, 收画布)

    立体声音响(准备:插电源, 调整音量 播放: 启动, 暂停:声音暂停, 结束:拔电源)

    在一组接口(ready, play, pause, end) 中分别调用子系统的对应方法.

    (代码简单,不敲了, 直接画图把)

  • 相关阅读:
    机器学习:SVM(核函数、高斯核函数RBF)
    机器学习:SVM(非线性数据分类:SVM中使用多项式特征和核函数SVC)
    LeetCode566. Reshape the Matrix
    LeetCode 128. Longest Consecutive Sequence
    # 线程安全 & 线程安全函数 & 线程不安全函数
    Linux进程状态
    C++ 4种强制类型转换
    TCP超时重传、滑动窗口、拥塞控制、快重传和快恢复
    LeetCode 69. Sqrt(x)
    LeetCode543. Diameter of Binary Tree
  • 原文地址:https://www.cnblogs.com/moveofgod/p/12508643.html
Copyright © 2011-2022 走看看