本文转载自微信公众号《微技术》,作者:汤姆哥。转载本文请联系微科公众号。大家好,我是汤姆大哥~面对复杂的业务场景和不断变化的客户需求,如何以最小的开发成本去适应变化并快速实施,同时保证系统具有低复杂度并且能够保证系统后续不断迭代的能力让系统具有很高的可扩展性。这些都是一个合格的架构师必须修炼的基本内功,但是如何修炼这门神功呢???我总结了常用的软件设计模式。目录如下:(考虑到内容较大,为方便起见,将软件设计模式系列(共23篇)拆分为四篇,每篇讲解六种设计模式,以不同颜色区分,方便阅读快速消化记忆)本文主要讲解桥接模式、组合模式、装饰模式、Facade模式、代理模式、责任链模式1、桥接模式自然界一般由实体和行为组成。当然,为了提高系统的可扩展性,可以将它们两者分别抽象出来,然后在抽象类中描述两者的依赖关系。定义:将抽象与其实现分开,以便它们都可以独立变化。什么场景使用桥接模式?一个类有两个(或更多)维度独立变化,这两个(或更多)维度需要独立扩展。桥接模式特别适用于那些不想使用继承或者由于多级继承导致系统类数量激增的系统。核心思想:抽象实体:定义的抽象分类。例如:Person具体实体:继承抽象实体的子类实体。例如:中国人、美国人、韩国人抽象行为:在抽象实体中定义多种行为。例如:学中文,吃汉堡具体行为:实现抽象行为的具体算法。例如:中国人学中文,美国人吃汉堡代码示例:/***@作者微信公众号:微技术*抽象实体*/publicabstractclassAbstractEntity{protectedAbstractBehaviorabstractBehavior;publicAbstractEntity(AbstractBehaviorabstractBehavior){this.abstractBehavior=abstractBehavior;}publicabstractvoidout();}/***抽象行为*/publicinterfaceAbstractBehavior{publicStringaction(Stringname);}/***关于食物行为*/publicclassFoodBehavioriimplementsAbstractBehavior{@OverridepublicStringaction(Stringname){if("Chinese".equals(name)){return"eatdumplings";}elseif("American".equals(name)){return"eathamburger";}returnnull;}}桥接模式是抽象与抽象分离,具体实现类依赖于抽象。抽象的分离间接完成了具体类与具体类之间的解耦,它们通过抽象来组合或聚合,而不是依赖多重继承。本质是将一个对象的实体和行为分离,然后基于这两个维度进行独立的演化。适用场景:拆分复杂类对象时。当一个类包含大量的对象和方法时,既不方便阅读,也不方便修改。当您想要在多个独立维度中进行缩放时。例如,系统功能和非功能视角,业务或技术视角等。在运行时,将不同的组件组合起来。2.组合模式的定义:组合模式也称为整体模式,它将一组相似的对象视为一个单个对象,然后将对象组合成树结构来表示整个层次结构。这里有两个关键点:1.分层树形结构2.业务统一简化操作核心思想:抽象组件(AbstractNode):定义需要实现的统一操作。CompositeNode:抽象组件的派生子类,包含若干个子节点(其他复合节点或叶节点)。LeafNode:抽象组件的子类,但它下面没有子节点。代码示例:publicabstractclassAbstractNode{publicabstractvoidadd(AbstractNodeabstractNode);publicabstractvoidremove(AbstractNodeabstractNode);publicabstractvoidaction();}publicclassCompositeNodeextendsAbstractNode{privateLongnodeId;privateListchildNodes;//存放子节点列表publicCompositeNode(LongnodeId,ListchildNodes){this.nodeId=nodeId;this.childNodes=childNodes;}@Overridepublicvoidadd(AbstractNodeabstractNode){childNodes.add(abstractNode);}@Overridepublicvoidremove(AbstractNodeabstractNode){childNodes.remove(abstractNode);}@Overridepublicvoidaction(){for(AbstractNodechildNode:childNodes){childNode.action();}}}publicclassLeafNodeextendsAbstractNode{privateLongnodeId;publicLeafNode(LongnodeId){this.nodeId=nodeId;}@Overridepublicvoidadd(AbstractNodeabstractNode){//无子节点,无需处理return;}@Overridepublicvoidremove(AbstractNnode){///无子节点,无需要处理return;}@Overridepublicvoidaction(){System.out.println("叶子节点编号:"+nodeId);}}叶子节点不能添加或删除子节点,所以对应的方法为空。组合模式本质上是封装了复杂结构内部的变化,让用户通过一个统一的整体来使用对象之间的结构。在数据结构方面,它支持树结构、环结构和网络结构。比如我们常见的深度优先搜索和广度优先搜索都是采用这种模式。适用场景:按照一定的层次结构管理一组对象。如:管理文件夹和文件,管理订单下的商品。复杂结构中的对象需要以统一的行为进行处理快速扩展对象组合。起初,手机是按品牌分类的。现在业务增加了价格维度分类,我们只需要引入新的分支节点,根据新的维度建立组合关系即可。3.装饰模式的定义:在不改变现有对象结构的情况下,动态地为其添加新的职责和行为,相当于对现有对象进行包装。核心思想:抽象组件(Component):装饰器基类,定义组件的基本功能具体组件(ConcreteComponent):抽象组件的具体实现装饰器的子类,重写组件接口方法,可以增加额外的功能.代码示例:publicabstractclassComponent{publicabstractvoidexecute();}publicclassConcreteComponenttextendsComponent{@Overridepublicvoidexecute(){System.out.println("ConcreteComponentinvoke!");}}publicclassDecoratorextendsComponent{protectedComponentcomponent;publicDecorator(Componentthis.component){component}{component}@Overridepublicvoidexecute(){component.execute();}}publicclassConcreteDecoratorextendsDecorator{publicConcreteDecorator(Componentcomponent){super(component);}@Overridepublicvoidexecute(){System.out.println("装饰子类ConcreteDecoratorinvoke!");super.execute();}}装饰模式本质上就是给已有的不可修改的类增加新的功能,同时又可以很容易的撤销。适用场景:不修改代码就可以使用对象,运行时想给对象增加额外的功能将业务逻辑组织成层级,可以为每一层创建一个装饰,运行时应用各种逻辑组合成对象。由于这些对象遵循一个公共接口,客户端代码可以以相同的方式使用这些对象。不支持继承扩展类的场景。比如:final关键字限制了某个类的进一步扩展,可以通过装饰器封装,使其具有扩展的能力。4、门面模式的定义:门面模式提供了一个高层接口,要求子系统外部与内部的通信必须通过统一的对象进行,使子系统更易于使用。门面模式要求我们使用统一的标准与系统进行交互。比如我们在打印日志的时候,基本都会选择slf4j框架,它集成了log4j、log4j2、CommonLog等日志框架,简化了我们的开发成本。核心思想:门面系统。接收外部请求并将请求转发给相应的子系统进行处理。表示某个领域功能的实现,或者具体子接口的实现,比如订单、支付等,具体处理门面系统分配的任务。简单来说,就是引入外观角色,简化客户端与子系统的交互,为复杂的子系统调用提供统一的入口。很多人可能会有疑问,这不是代理模式吗?门面模式可以代理多个接口,而代理模式通常只代理一个接口。业务场景:我们都习惯了在移动互联网上进行在线支付。相信很多人在支付的时候都听说过“微信支付或者支付宝”这句话。商家根据用户反馈选择支付渠道。是不是很麻烦?为了解决这个问题,市场上出现了聚合支付(这个领域最好的就是收钱)。整个业务模型就是本节要讲的门面模型。无论您使用什么软件支付,只需打开支付宝二维码即可,底层收款栏对二维码进行识别分析,并根据扫描结果自动适配相应的支付渠道,完成用户扣费行动,真正带来了良好的用户体验。优点:简化复杂的系统,提供统一的接口规范。例如:JPA提供了统一的Java持久层API,底层适配多样化的存储系统。复杂的业务逻辑由内部子系统消化。只要对外接口规范不变,外部调用者就不需要频繁修改。扩展性好,类似于SPI架构,支持横向扩展。高平滑过渡。例如:我们要升级旧的系统架构,开发一系列新的界面来替代原来的旧界面。过渡期间需要新旧灰度测试、流量切换、平滑升级。可以采用这种模式。Facade模式在多系统兼容和系统重构方面是一个强大的工具。5.代理模式定义:为其他对象提供一个代理来控制对这个对象的访问真实场景:房地产中介承包商核心思想:抽象主题类(AbstractSubject):定义接口方法供客户使用主题实现类(RealSubject):implements抽象主题类Proxy的接口方法:实现了抽象主题类的接口方法,里面包含了主题实现类的逻辑,也包含了一些自扩展的操作。代理模式类似于适配器模式。但是adapter模式是转换为新的接口,proxy模式不会改变原来的接口。代码示例:/***@author微信公众号:微科技*/publicinterfaceAbstractSubject{voidexecute();}publicclassRealSubjectimplementsAbstractSubject{@Overridepublicvoidexecute(){System.out.println("我是阿汤哥,我要努力!");}}publicclassProxyimplementsAbstractSubject{privateAbstractSubjectabstractSubject;publicProxy(AbstractSubjectabstractSubject){this.abstractSubject=abstractSubject;}@Overridepublicvoidexecute(){System.out.println("老板给汤姆分配了一份工作...");abstractSubject.execute();}}根据使用职责分为静态代理和动态代理。静态代理,代理类需要自己写代码来完成。动态代理,通过Proxy#newProxyInstance(ClassLoaderloader,Class[]interfaces,InvocationHandlerh)方法生成代理类。JDK实现的代理,不管是静态代理还是动态代理,都是面向接口的编程。CGLib不必局限于一个接口。优点:职责明确可扩展性强,只要实现了接口,就可以使用代理智能、动态代理减少对象的直接耦合适用场景:远程代理。不能直接操作远程对象。比如:Dubbo,gRPC,提供远程服务。客户端调用时,需要进行参数组装、序列化、网络传输等操作。这些通用逻辑可以封装在代理中。客户端调用代理对象访问远程服务,就像调用本地对象一样方便。保护剂。当客户端通过代理对象访问原始对象时,代理对象会根据规则判断客户端是否有访问权限。例如:防火墙日??志代理。比如:日志监控,在正常业务访问的时候,调用代理,增加一些额外的日志记录功能。虚拟代理,适用于延迟初始化,用小对象代表大对象,减少资源消耗,提高运行速度。当不想改变原有对象,但又需要增加权限控制、日志记录、流量控制等功能时,可以使用代理模式。6.责任链模式的定义:责任链模式是一种行为设计模式,它通过前一个对象记住下一个对象的引用,将所有请求处理程序连接成一个链。收到请求后,每个处理器都可以处理请求或将其传递给链中的下一个处理器。责任链模式是链表结构在数据结构中的一种具体应用。核心思想:抽象处理程序(Handler):定义一个接口,其中包含处理方法和下一个节点的引用对象。处理,否则跳过,将请求转发给下一个节点。优点:减少对象之间的耦合。链上每个节点各司其职,通过上下文传递数据,避免直接依赖。增强系统的可扩展性。如果有新的业务需求,只需要在合适的位置增加一个链节点即可,满足开闭原则。弹性强。如果业务发生变化,需要调整工作流程,只需要动态调整链上节点的顺序即可。甚至为了满足多元化业务的多样化需求,我们可以针对不同的业务类型定义自己专属的执行顺序。简化了对象之间的连接。每个对象只需要保存对下一个节点的引用,而不是所有节点。责任明确。每个节点只需处理自己的工作,如果没有,则将其传递给下一个对象。明确各类型的职责范围,符合类单一职责的原则。这种模式推荐用于我们常见的网关架构。通过服务编排,可以在任意位置自由增删节点,满足一系列个性化功能。写在最后,很多人都研究过设计模式,但是在项目真正实战的时候总是头晕目眩。原因是他们不明白它的核心是什么,底层逻辑是什么。,并封装了将发生变化的概念。软件架构的本质:发现变化,封装变化。业务千变万化,没有固定的编码答案,不要硬套设计模式。无论选择哪种设计模式,尽量满足SOLID原则,并自检是否满足业务的持续扩展性。俗话说,“不管黑猫白猫,抓到老鼠就是好猫”。