关于设计模式的一些实战总结 -- 常见的结构型设计模式

在设计模式里面,有一种叫做适配器的设计模式 Adapter Design Pattern ,这类适配器模式通常应用于做不同接口之间的适配和调整,常见的应用场景例如:

对一些不同实现的接口做统一整合,对一些接口的设计“缺陷”做一定的补救措施。

举个栗子来说,假设某个业务场景里面的遇到了一个人脸识别的功能:

公司内部接入了多个第三方的认证接口,具体的接口设计如下:

public interface IFaceRecognitionService {/*** 人脸识别** @param userId* @return*/Boolean recognition(Integer userId);
}

对应的人脸认证接口实现如下:

public class AFaceRecognitionService implements IFaceRecognitionService {@Overridepublic Boolean recognition(Integer userId) {System.out.println("this is AliFaceRecognitionService");return true;}
}public class BFaceRecognitionService implements IFaceRecognitionService {@Overridepublic Boolean recognition(Integer userId) {System.out.println("this is B FaceRecognitionService");return true;}
}

然后此时我们就有两类的认证接口,假设后边的业务愈发扩展,接入的第三方接口越来越多,这时候可以设计出一个灵活的适配器来进行代码的兼容:

public class FaceRecognitionAdaptorService implements IFaceRecognitionService {private IFaceRecognitionService faceRecognitionService;public FaceRecognitionAdaptorService(IFaceRecognitionService faceRecognitionService){this.faceRecognitionService = faceRecognitionService;}public FaceRecognitionAdaptorService(){}public IFaceRecognitionService select(int type){if(type==1){this.faceRecognitionService = new AFaceRecognitionService();}else{this.faceRecognitionService = new BFaceRecognitionService();}return this.faceRecognitionService;}@Overridepublic Boolean recognition(Integer userId) {return faceRecognitionService.recognition(userId);}public static void main(String[] args) {FaceRecognitionAdaptorService faceRecognitionAdaptorService = new FaceRecognitionAdaptorService();faceRecognitionAdaptorService.select(1).recognition(1001);faceRecognitionAdaptorService.select(2).recognition(1002);}
}

虽然说demo很简单,但是从代码的后期维护角度来说,我们可以得出以下两点经验:

  1. 使用了适配器模式其实有时候可以让两个独立的类各自发展,隔离他们之间的依赖,每当有类发生变化的时候只会影响到对应的类和适配器内部的代码,耦合程度可以大大降低。

  2. 而且在实际工作中,如果需要对系统的功能进行维护,可以通过采用适配器模式的方式来进行适配,从而减少对原先代码的侵入性。

代理模式的应用

什么是代理模式?

简单来讲就是在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。我们通过一个简单的例子来学习一下代理模式:

一个用户登录的接口代码如下所示:

public class UserService implements IUserService{@Overridepublic void login() {System.out.println("UserService login...");}
}

可以结合代理模式,使用jdk代理的方式来进行接口的时长统计:

public class MetricsProxy {public Object createProxy(Object proxyObj){Class<?>[] interfaces = proxyObj.getClass().getInterfaces();DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler(interfaces);return Proxy.newProxyInstance(proxyObj.getClass().getClassLoader(), interfaces, dynamicProxyHandler);}private class DynamicProxyHandler implements InvocationHandler {private Object proxiedObject;public DynamicProxyHandler(Object proxiedObject) {this.proxiedObject = proxiedObject;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {long begin = System.currentTimeMillis();Object result = method.invoke(proxiedObject, args);String apiName = proxiedObject.getClass().getName() + ":" + method.getName();long end = System.currentTimeMillis();System.out.println("接口耗时:"+(end - begin));return result;}}public static void main(String[] args) {MetricsProxy metricsProxy = new MetricsProxy();IUserService userService = (IUserService) metricsProxy.createProxy(new UserService());userService.login();}
}

除了上述的这种简单场景之外,实际上在我们工作中经常应用的rpc服务框架也有代理模式的影子。
例如说我们常用的dubbo框架,在进行远程化的服务调用过程中就是使用了代理模式的方式进行设计,使得用户端在调用接口的时候不需要去对底层的一些网络通信,数据编码做过多深入的了解。
为了更好地演示理解这个应用,下边我将通过一个实际案例来进行介绍:

首先是服务端代码:

public class RpcServer {public void export(Object service,int port){if(service==null || port<0 || port>65535){throw new RuntimeException("param is error");}try {ServerSocket serverSocket = new ServerSocket(port);while (true){final Socket socket = serverSocket.accept();new Thread(new Runnable() {@Overridepublic void run() {try {ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());String methodName = objectInputStream.readUTF();Class<?>[] parameterTypes = (Class<?>[]) objectInputStream.readObject();Object[] args = (Object[]) objectInputStream.readObject();Method method = service.getClass().getMethod(methodName,parameterTypes);Object result = method.invoke(service,args);output.writeObject(result);} catch (IOException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {e.printStackTrace();}}}).start();}} catch (IOException e) {e.printStackTrace();}}}

客户端代码

public class RpcClient {public <T> T refer(final Class<T> interfaceClass, final String host, final int port) throws Exception {if(interfaceClass==null){throw new RuntimeException("interfaceClass is null");}else if (host==null){throw new RuntimeException("host is null");}else if (port<0 || port>65535){throw new RuntimeException("port is invalid ");}return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Socket socket = new Socket(host,port);OutputStream outputStream = socket.getOutputStream();ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);objectOutputStream.writeUTF(method.getName());objectOutputStream.writeObject(method.getParameterTypes());objectOutputStream.writeObject(args);ObjectInputStream input = new ObjectInputStream(socket.getInputStream());try {Object result = input.readObject();if (result instanceof Throwable) {throw (Throwable) result;}return result;} finally {input.close();}}});}
}

接着是一些测试使用的模拟服务,代码如下所示:

public interface IUserService {String test();
}public class UserServiceImpl implements  IUserService {@Overridepublic String test() {System.out.println("this is test");return "success";}
}

借助了使用代理模式设计的服务调用方和服务提供方,这里通过建立了两端的demo案例进行模拟:
首先是服务端代码:

public class ServerDemo {public static void main(String[] args) {RpcServer rpcServer = new RpcServer();IUserService userService = new UserServiceImpl();rpcServer.export(userService,9090);}
}

接着是客户端代码:

public class ClientDemo {public static void main(String[] args) throws Exception {RpcClient rpcClient = new RpcClient();IUserService iUserService = rpcClient.refer(IUserService.class,"localhost",9090);iUserService.test();}
}

本文总结

1.适配器模式是用来做适配,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。而且在对于系统维护的时候,适配器模式还可以作为一种用于补偿机制的设计模式来供开发者选用。

2.代理模式主要是在不改变原有类设计的基础上边通过引入相应的代理类来给原始类做扩展功能。常见的代理有划分为静态代理和动态代理两类。但是由于静态代理的可扩展性不好,因此实际工作中更多的场景会考虑使用动态代理的设计思路。比较常见的动态代理实现技术有cglib和jdk两类技术。然而使用JDK实现的动态代理不能完成继承式的动态代理,如果遇到这样的场景,可以使用cglib来实现继承式的动态代理。

3.适配器模式和代理模式两者都有点”包装(Wrapper)“数据的味道,其实这也是他们之间的一些共同性。如果要用他们的共性来划分,其实这两类设计模式可以统一称呼为结构型设计模式。

END

Java面试题专栏

【71期】面试官:对并发熟悉吗?谈谈你对Java中常用的几种线程池的理解

【72期】面试官:对并发熟悉吗?说一下synchronized与Lock的区别与使用

【73期】面试官:Spring 和 Spring Boot 的区别是什么?

【74期】面试官:对多线程熟悉吗,来谈谈线程池的好处?

【75期】面试官:说说Redis的过期键删除策略吧!(高频)

【76期】面试官问:List如何一边遍历,一边删除?

【77期】这一道面试题就考验了你对Java的理解程度

【78期】别找了,Java集合面试问题这里帮你总结好了!

【79期】别找了,回答Spring中Bean的生命周期,这里帮你总结好了!

【80期】说出Java创建线程的三种方式及对比

我知道你 “在看”

结构型设计模式在公司项目中的运用实践相关推荐

  1. 策略模式在公司项目中的运用实践,看完又可以涨一波实战经验了!

    营销系统是一个动态的.有机地结合的系统,经常会随着业务的不断变化发生调整,因此从事这一业务的开发可让我头疼了. 之前在工作中就不乏一次遇到过随意调整营销策略的情况,在部分场景下由于使用了硬编码的方式来 ...

  2. 《精通python设计模式》读书笔记之——结构型设计模式

    结构型设计模式: 结构型设计模式处理一个系统中不同实体(比如,类和对象)之间的关系,关注的是提供一种简单的对象组合方式来创造新功能.可用于实现不兼容软件之间的接口兼容. ①.适配器模式 简介: 适配器 ...

  3. 从框架源码中学习结构型设计模式

    文章目录 从框架源码学习结构型设计模式 适配器模式 应用实例 案例一:dubbo框架日志适配器 Logger接口 日志实现类 Logger适配器接口 LoggerAdapter实现类 Logger日志 ...

  4. JavaScript 设计模式核⼼原理与应⽤实践 之 结构型设计模式

    JavaScript 设计模式核⼼原理与应⽤实践 之 结构型设计模式 结构型:装饰器模式--对象装上它,就像开了挂 装饰器模式,又名装饰者模式.它的定义是"在不改变原对象的基础上,通过对其进 ...

  5. JAVA设计模式第三讲:结构型设计模式

    设计模式(design pattern)是对软件设计中普遍存在的各种问题,所提出的解决方案.本文以面试题作为切入点,介绍了设计模式的常见问题.我们需要掌握各种设计模式的原理.实现.设计意图和应用场景, ...

  6. 结构型设计模式(7种)

    结构型设计模式(7种) 概述 结构型设计模式主要总结了一些类或对象组合在一起的经典结构,这些经典的结构可以解决特定应用场景的问题. 分类 结构型(7种): 介绍如何将对象和类组成较大的结构,并保持结构 ...

  7. 软件设计实验三 结构型设计模式实验

    一.实验目的 1.结合实例,熟练绘制常见的结构型设计模式结构图. 2.结合实例,熟练使用任意一种面向对象编程语言实现常见的结构型设计模式. 3.通过本实验,理解每一种结构型设计模式的模式动机,掌握模式 ...

  8. Java设计模式(三)结构型 设计模式

    设计模式(结构型) 结构型设计模式关注如何将现有的类或对象组织在一起形成更加强大的结构.并且根据我们前面学习的合成复用原则,我们该如何尽可能地使用关联关系来代替继承关系是我们本版块需要重点学习的内容. ...

  9. 技术图文:03 结构型设计模式(下)

    结构型设计模式(下) 本教程主要介绍一系列用于如何将现有类或对象组合在一起形成更加强大结构的经验总结. 知识结构: 组合模式 – 树形结构的处理 Sunny 软件公司欲开发一个杀毒(AntiVirus ...

最新文章

  1. python中的新式类与旧式类的一些基于descriptor的概念(上)
  2. 九零后的五年七次工作经历
  3. 学习JavaScript 的必备
  4. 湖南加速恢复,“望城速度”强势打样
  5. c malloc 头文件_C语言提高篇_malloc,realloc和calloc的区别
  6. 一个内核网络漏洞详解|容器逃逸
  7. 如何利用.NET Core搭建跨平台的控制台应用程序
  8. 【数位DP】CF 54C,509C,431D,628D,855E,1245F,95D
  9. getdate 日期间隔_日期getDate()方法以及JavaScript中的示例
  10. linux进程创建时间,linux进程创建时间计算
  11. 内存经销商穷困潦倒 七元午饭都赊账
  12. 货拉拉回应寻求5亿美元融资传言:持续关注资本市场 无具体上市时间表
  13. 暂不升级iOS 14.2:多款iPhone续航崩溃
  14. dell 服务器ghost系统,Dell 5000笔记本装win7 64位Ghost系统真正成功教程
  15. 基于ssm java医院病历管理系统
  16. WindowsServer2008R2安装中文语言包截图详细教程(附语言包下载资源)
  17. 三菱5uplc伺服电机指令_2020山东三菱PLCFX1S30MT回收回收价格公道
  18. 计算机局域网和广域网的特点是什么,局域网和广域网的区别
  19. status计算机语言,status 在c语言中的用法?请高手指教、、、、谢谢
  20. 第六章 现在给我道歉还来得及~修改登录cookie进后台上传muma

热门文章

  1. 文艺复兴?诺基亚再推翻盖手机 28天待机 仅售700元还带4G 真香就完事了!
  2. vivo Y90百元新机曝光:联发科A22+水滴屏 售价约690元
  3. 短视频成青少年教育新工具:抖音等三机构联合发起“青椒计划”
  4. 连厕所都不敢去了!这家公司使用AI监控员工工作效率 “摸鱼”就会被解雇
  5. 拳王虚拟项目公社:网上做什么兼职副业比较容易挣到钱?虚拟资源项目是赚钱的最佳选择
  6. R40使用GPIO中断实现按键功能【原创】
  7. 贝叶斯信念网络和马尔科夫链有什么区别
  8. Unity3D中JavaScript与C#对比
  9. 厦门one_厦门外代荣获ONE全球船舶操作中心颁发的Sapphire Award奖
  10. resultset不支持循环遍历_二叉树的各种遍历方法的简单解释