3.代理模式、桥接模式、装饰器模式、适配器模式

代理模式

在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能

原来的实现,代码侵入性比较强

public class UserController {
           //...省略其他属性和方法...
        private MetricsCollector metricsCollector; // 依赖注入
        public UserVo login(String telephone, String password) {
            long startTimestamp = System.currentTimeMillis();
            // ... 省略login逻辑...
            long endTimeStamp = System.currentTimeMillis();
            long responseTime = endTimeStamp - startTimestamp;
            RequestInfo requestInfo = new RequestInfo("login", responseTime, startTimesta
                    metricsCollector.recordRequest(requestInfo);
            //...返回UserVo数据...
        }
        public UserVo register(String telephone, String password) {
            long startTimestamp = System.currentTimeMillis();
            // ... 省略register逻辑...
            long endTimeStamp = System.currentTimeMillis();
            long responseTime = endTimeStamp - startTimestamp;
            RequestInfo requestInfo = new RequestInfo("register", responseTime, startTime
                    metricsCollector.recordRequest(requestInfo);
           //...返回UserVo数据...
        }
    }
  • 代理类和原始类需要实现相同的接口
public interface IUserController {
        UserVo login(String telephone, String password);
    }
    public class UserController implements IUserController {
        //...省略其他属性和方法...
        @Override
        public UserVo login(String telephone, String password) {
        //...省略login逻辑...
        //...返回UserVo数据...
        }

    }
    public class UserControllerProxy implements IUserController {
        private MetricsCollector metricsCollector;
        private UserController userController;
        public UserControllerProxy(UserController userController) {
            this.userController = userController;
            this.metricsCollector = new MetricsCollector();
        }
        @Override
        public UserVo login(String telephone, String password) {
            long startTimestamp = System.currentTimeMillis();
            // 委托
            UserVo userVo = userController.login(telephone, password);
            long endTimeStamp = System.currentTimeMillis();
            long responseTime = endTimeStamp - startTimestamp;
            RequestInfo requestInfo = new RequestInfo("login", responseTime, startTimesta
                    metricsCollector.recordRequest(requestInfo);
            return userVo;
        }

    }
    //UserControllerProxy使用举例
    //因为原始类和代理类实现相同的接口,是基于接口而非实现编程
    //将UserController类对象替换为UserControllerProxy类对象,不需要改动太多代码
    IUserController userController = new UserControllerProxy(new UserController());
  • 如果不方便使用接口的情况下,可以使用继承实现
    public class UserControllerProxy extends UserController {
        private MetricsCollector metricsCollector;
        public UserControllerProxy() {
            this.metricsCollector = new MetricsCollector();
        }
        public UserVo login(String telephone, String password) {
            long startTimestamp = System.currentTimeMillis();
            UserVo userVo = super.login(telephone, password);
            long endTimeStamp = System.currentTimeMillis();
            long responseTime = endTimeStamp - startTimestamp;
            RequestInfo requestInfo = new RequestInfo("login", responseTime, startTimesta
                    metricsCollector.recordRequest(requestInfo);
            return userVo;
        }
        public UserVo register(String telephone, String password) {
            long startTimestamp = System.currentTimeMillis();
            UserVo userVo = super.register(telephone, password);
            long endTimeStamp = System.currentTimeMillis();
            long responseTime = endTimeStamp - startTimestamp;
            RequestInfo requestInfo = new RequestInfo("register", responseTime, startTime
                    metricsCollector.recordRequest(requestInfo);
            return userVo;
        }
    }
    //UserControllerProxy使用举例
    UserController userController = new UserControllerProxy();

但是如果有很多方法需要实现,那改动特别大,可以使用动态代理实现,就是我们不事先为每个原始类编写代理类,而是在运行的时候,动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。

动态代理

    public class MetricsCollectorProxy {
        private MetricsCollector metricsCollector;

        public MetricsCollectorProxy() {
            this.metricsCollector = new MetricsCollector();
        }

        public Object createProxy(Object proxiedObject) {
            Class<?>[] interfaces = proxiedObject.getClass().getInterfaces();
            DynamicProxyHandler handler = new DynamicProxyHandler(proxiedObject);
            return Proxy.newProxyInstance(proxiedObject.getClass().getClassLoader(), interfaces, handler);
        }

        private class DynamicProxyHandler implements InvocationHandler {
            private Object proxiedObject;

            public DynamicProxyHandler(Object proxiedObject) {
                this.proxiedObject = proxiedObject;
            }

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                long startTimestamp = System.currentTimeMillis();
                Object result = method.invoke(proxiedObject, args);
                long endTimeStamp = System.currentTimeMillis();
                long responseTime = endTimeStamp - startTimestamp;
                String apiName = proxiedObject.getClass().getName() + ":" + method.getName(
                        RequestInfo requestInfo = new RequestInfo(apiName, responseTime, startTimes
                                metricsCollector.recordRequest(requestInfo);
                return result;
            }
        }
    }

    //MetricsCollectorProxy使用举例
    MetricsCollectorProxy proxy = new MetricsCollectorProxy();
    IUserController userController = (IUserController) proxy.createProxy(new UserController());

动态代理的应用场景:

  1. 开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志。
  2. 代理模式在 RPC、缓存中的应用

桥接模式

将抽象和实现解耦,让它们可以独立变化。
一个类存在两个(或多个)独立变化的维度,我们通过组合的方式,让这两个(或多个)维度可以独立进行扩展。

针对 Notification 的代码,我们将不同渠道的发送逻辑剥离出来,形成独立的消息发送类(MsgSender 相关类)。其中,Notification 类相当于抽象,MsgSender 类相当于实现,两者可以独立开发,通过组合关系(也就是桥梁)任意组合在一起。所谓任意组合的意思就是,不同紧急程度的消息和发送渠道之间的对应关系,不是在代码中固定写死的,我们可以动态地去指定(比如,通过读取配置来获取对应关系)

    public interface MsgSender {
        void send(String message);
    }

    public class TelephoneMsgSender implements MsgSender {
        private List<String> telephones;

        public TelephoneMsgSender(List<String> telephones) {
            this.telephones = telephones;
        }

        @Override
        public void send(String message) {
//...
        }
    }

    public class EmailMsgSender implements MsgSender {
// 与TelephoneMsgSender代码结构类似,所以省略...
    }

    public class WechatMsgSender implements MsgSender {
// 与TelephoneMsgSender代码结构类似,所以省略...
    }

    public abstract class Notification {
        protected MsgSender msgSender;

        public Notification(MsgSender msgSender) {
            this.msgSender = msgSender;
        }

        public abstract void notify(String message);
    }

    public class SevereNotification extends Notification {
        public SevereNotification(MsgSender msgSender) {
            super(msgSender);
        }

        @Override
        public void notify(String message) {
            msgSender.send(message);
        }
    }

    public class UrgencyNotification extends Notification {
        // 与SevereNotification代码结构类似,所以省略...
    }

    public class NormalNotification extends Notification {
        // 与SevereNotification代码结构类似,所以省略...
    }

    public class TrivialNotification extends Notification {
        // 与SevereNotification代码结构类似,所以省略...
    }

装饰器模式

装饰器模式相对于简单的组合关系,还有两个比较特殊的地方

  • 第一个比较特殊的地方是:装饰器类和原始类继承同样的父类,这样我们可以对原始类“嵌
    套”多个装饰器类。
  • 装饰器类是对功能的增强,这也是装饰器模式应用场景的一个重要特点。
// 代理模式的代码结构(下面的接口也可以替换成抽象类)
    public interface IA {
        void f();
    }

    public class A impelements IA {
        public void f() { //...
        }
    }

    public class AProxy implements IA {
        private IA a;

        public AProxy(IA a) {
            this.a = a;
        }

        public void f() {
            // 新添加的代理逻辑
            a.f();
            // 新添加的代理逻辑
        }
    }

    // 装饰器模式的代码结构(下面的接口也可以替换成抽象类)
    public interface IA {
        void f();
    }

    public class A implements IA {
        public void f() { //...
        }
    }

    public class ADecorator implements IA {
        private IA a;

        public ADecorator(IA a) {
            this.a = a;
        }

        public void f() {
        // 功能增强代码
            a.f();
            // 功能增强代码
        }
    }

装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。它主要的作用是给原始
类添加增强功能。

适配器模式

它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以
一起工作。

类适配器

继承来实现

    public interface ITarget {
        void f1();

        void f2();

        void fc();
    }

    public class Adaptee {
        public void fa() { //... 
        }

        public void fb() { //... 
        }

        public void fc() { //... 
        }
    }

    public class Adaptor extends Adaptee implements ITarget {
        public void f1() {
            super.fa();
        }

        public void f2() {
            //...重新实现f2()...
        }
            // 这里fc()不需要实现,直接继承自Adaptee,这是跟对象适配器最大的不同点
    }

对象适配器

组合来实现

    public interface ITarget {
        void f1();

        void f2();

        void fc();
    }

    public class Adaptee {
        public void fa() { 
            //... 
        }

        public void fb() { 
            //...
        }

        public void fc() { 
            //... 
        }
    }

    public class Adaptor implements ITarget {
        private Adaptee adaptee;

        public Adaptor(Adaptee adaptee) {
            this.adaptee = adaptee;
        }

        public void f1() {
            adaptee.fa(); //委托给Adaptee
        }

        public void f2() {
            //...重新实现f2()...
        }

        public void fc() {
            adaptee.fc();
        }
    }
  • 如果 Adaptee 接口很多,而且 Adaptee 和 ITarget 接口定义大部分都相同,那我们推荐
    使用类适配器,因为 Adaptor 复用父类 Adaptee 的接口,比起对象适配器的实现方式,
    Adaptor 的代码量要少一些。
  • 如果 Adaptee 接口很多,而且 Adaptee 和 ITarget 接口定义大部分都不相同,那我们推
    荐使用对象适配器,因为组合结构相对于继承更加灵活。

适用场景

  1. 封装有缺陷的接口设计
public class CD { //这个类来自外部sdk,我们无权修改它的代码
        //...
        public static void staticFunction1() { //... 
        }

        public void uglyNamingFunction2() { //...
        }

        public void tooManyParamsFunction3(int paramA, int paramB, ...) { //...
        }

        public void lowPerformanceFunction4() { //...
        }
    }

    // 使用适配器模式进行重构
    public interface ITarget {
        void function1();

        void function2();

        void fucntion3(ParamsWrapperDefinition paramsWrapper);

        void function4();
//...
    }

    // 注意:适配器类的命名不一定非得末尾带Adaptor
    public class CDAdaptor extends CD implements ITarget {
        //...
        public void function1() {
            super.staticFunction1();
        }

        public void function2() {
            super.uglyNamingFucntion2();
        }

        public void function3(ParamsWrapperDefinition paramsWrapper) {
            super.tooManyParamsFunction3(paramsWrapper.getParamA(), ...);
        }

        public void function4() {
		//...reimplement it...
        }
    }
  1. 统一多个类的接口设计

某个功能的实现依赖多个外部系统(或者说类)。通过适配器模式,将它们的接口适配为统一
的接口定义,然后我们就可以使用多态的特性来复用代码逻辑。

    public class ASensitiveWordsFilter { // A敏感词过滤系统提供的接口
        //text是原始文本,函数输出用***替换敏感词之后的文本
        public String filterSexyWords(String text) {
            // ...
        }

        public String filterPoliticalWords(String text) {
            // ...
        }
    }

    public class BSensitiveWordsFilter { // B敏感词过滤系统提供的接口
        public String filter(String text) {
            //...
        }
    }

    public class CSensitiveWordsFilter { // C敏感词过滤系统提供的接口
        public String filter(String text, String mask) {
            //...
        }
    }

    // 未使用适配器模式之前的代码:代码的可测试性、扩展性不好
    public class RiskManagement {
        private ASensitiveWordsFilter aFilter = new ASensitiveWordsFilter();
        private BSensitiveWordsFilter bFilter = new BSensitiveWordsFilter();
        private CSensitiveWordsFilter cFilter = new CSensitiveWordsFilter();

        public String filterSensitiveWords(String text) {
            String maskedText = aFilter.filterSexyWords(text);
            maskedText = aFilter.filterPoliticalWords(maskedText);
            maskedText = bFilter.filter(maskedText);
            maskedText = cFilter.filter(maskedText, "***");
            return maskedText;
        }
    }

    // 使用适配器模式进行改造
    public interface ISensitiveWordsFilter { // 统一接口定义
        String filter(String text);
    }

    public class ASensitiveWordsFilterAdaptor implements ISensitiveWordsFilter {
        private ASensitiveWordsFilter aFilter;

        public String filter(String text) {
            String maskedText = aFilter.filterSexyWords(text);
            maskedText = aFilter.filterPoliticalWords(maskedText);
            return maskedText;
        }
    }

    //...省略BSensitiveWordsFilterAdaptor、CSensitiveWordsFilterAdaptor...
// 扩展性更好,更加符合开闭原则,如果添加一个新的敏感词过滤系统,
// 这个类完全不需要改动;而且基于接口而非实现编程,代码的可测试性更好。
    public class RiskManagement {
        private List<ISensitiveWordsFilter> filters = new ArrayList<>();

        public void addSensitiveWordsFilter(ISensitiveWordsFilter filter) {
            filters.add(filter);
        }

        public String filterSensitiveWords(String text) {
            String maskedText = text;
            for (ISensitiveWordsFilter filter : filters) {
                maskedText = filter.filter(maskedText);
            }
            return maskedText;
        }
    }
  1. 替换依赖的外部系统

当我们把项目中依赖的一个外部系统替换为另一个外部系统的时候,利用适配器模式,可以减
少对代码的改动。

    // 外部系统A
    public interface IA {
        //...
        void fa();
    }

    public class A implements IA {
        //...
        public void fa() { //...
        }
    }

    // 在我们的项目中,外部系统A的使用示例
    public class Demo {
        private IA a;

        public Demo(IA a) {
            this.a = a;
        }
//...
    }

    Demo d = new Demo(new A());

    // 将外部系统A替换成外部系统B
    public class BAdaptor implemnts IA {
        private B b;

        public BAdaptor(B b) {
            this.b = b;
        }

        public void fa() {
            //...
            b.fb();
        }
    }

    // 借助BAdaptor,Demo的代码中,调用IA接口的地方都无需改动,
    // 只需要将BAdaptor如下注入到Demo即可。
    Demo d = new Demo(new BAdaptor(new B()));
  1. 兼容老版本接口
    public class Collections {
        public static Emueration emumeration(final Collection c) {
            return new Enumeration() {
                Iterator i = c.iterator();

                public boolean hasMoreElments() {
                    return i.hashNext();
                }

                public Object nextElement() {
                    return i.next():
                }
            }
        }
    }
  1. 适配不同格式的数据
List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");

代理模式:代理模式在不改变原始类接口的条件下,为原始类定义一个代理类,主要目的是控
制访问,而非加强功能,这是它跟装饰器模式最大的不同。
桥接模式:桥接模式的目的是将接口部分和实现部分分离,从而让它们可以较为容易、也相对
独立地加以改变。
装饰器模式:装饰者模式在不改变原始类接口的情况下,对原始类功能进行增强,并且支持多
个装饰器的嵌套使用。
适配器模式:适配器模式是一种事后的补救策略。适配器提供跟原始类不同的接口,而代理模
式、装饰器模式提供的都是跟原始类相同的接口。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值