绝对干货-讲讲设计模式之结构型设计模式

发布时间:2023年12月23日

经典的23种设计模式种属于结构型设计模式的是装饰模式,适配器模式,代理模式,组合模式,桥接模式,外观模式,享元模式。

如果说创建型设计模式解决的是创建对象的问题,那么结构型模式就是通过类和对象的组合解决开发过程中的老代码功能扩展,通过代码结构设计降低代码耦合,针对扩展开放,针对改变关闭,新老功能适配,减少递归函数使用,减少内存使用,提高代码性能等等

? ? ? ? 废话讲了太多,直接上干货-各结构型设计模式的本质

? ? ? ? 首先我想把装饰模式和代理模式放在一起讲

? ? ? ? 有没有人发现透明模式的装饰模式和运用组合思想实现的静态代理非常相识,但是本质上有很大区别,这种区别其实也是装饰模式与代理模式的本质区别,其实说到底两者都在给原有代码做功能扩展的事,但是它们所作用的维度不一样,装饰模式(装饰模式要求装饰器和被装饰对象都实现相同的接口或抽象类,这也是它实现装饰链的基础,可以通过包装(decorating)对象来扩展其功能,同时保持对象接口不变,使用组合的方式,将装饰器对象嵌套在被装饰对象之中,形成一个装饰器链。装饰模式(半透明)类图:主要是在通过拓展具体装饰类来实现对象功能的扩展,而代理模式更倾向于用一个通用功能来给目标类做功能扩展,,举个反例:现在已有一个运用组合思想实现的静态代理类来给老功能做逻辑扩展,,后面发现新的需求需要新的扩展逻辑,,那我们就要再去重新写一个静态代理类,,然后后面发现新需求又来了,又要再重新添加扩展逻辑在写一个静态代理类,,该死的新需求老是需要新的扩展逻辑,这样导致我们针对同一个类写了好多代理类,发现这么多静态代理类特别难管理,没法对代码的规范方法命名什么做限制,很明显这种功能扩展的场景很不适合用静态代理模式,相反这种新需求累加的情况 非常适合用具有的组合性特点的装饰模式动态代理模式就更不用说了,两种动态代理模式(jdk动态代理基于字节码生成,通过在运行时动态创建一个实现代理接口的匿名类来实现代理与cglib动态代理通过生成目标类的子类来实现代理)本质都是运行时切面编程,在给一个固定的扩展逻辑动态创建更多的代理类来适配更多的目标类,只要你愿意的话,你可以将代理生成器中的目标类设置成Object

? ? ? ? 其实代理模式只是不太适合给代码逻辑扩展比较频繁的场景做功能扩展,但是他又的确是代码功能扩展的一剂良药(基本没有什么前置设计限制,除了jdK动态代理的目标类有个必须是接口实现类的限制和cglib动态代理的目标类不可是final类的限制),装治疑难杂症(静态代理基本是想收拾谁收拾谁(当然最好是被代理类能实现一个接口或继承某个抽象类,这样更易于静态代理的逻辑复用,以及遵循依赖倒置原则),动态代理更是在rpc远程调用,延迟加载,日志记录,权限控制等等方面应用广泛),虽然会有些代价比如上面说代理类的增加,以及动态代理反射机制代理的性能开销

? ? ? ? 上面我们主要讲了装饰模式与代理模式使用场景维度的区别,一个是装饰类强调的拓展逻辑的维度,另一个是代理类强调的逻辑复用的维度。

????????接下来我们讲讲桥接模式将一个类的抽象部分与其实现部分分离,使它们可以独立地变化,简单讲就是设计之初就考虑到抽象类的非抽象方法的可变逻辑,将可变逻辑提取出来用接口属性中的方法去执行,这样就不需要通过不断的继承重写父类方法来实现逻辑拓展,注意这就是它本质解决的问题,两种桥接模式构造注入桥接模式set注入桥接模式构造注入桥接模式通过构造方法注入接口,单纯从调用者角度上看起来是否有点像继承的思想实现的静态代理,但其实正好相反,构造注入桥接模式是往目标类里构造注入接口实现对象,而继承的思想实现的静态代理是在代理类中通过构造被代理的父类对象并通过重写父类方法加扩展逻辑的方式在来实现对被代理父类对象的逻辑扩展,注意这里大家重点观察扩展逻辑写在哪,构造注入桥接模式自然是写在接口实现类中,而继承的思想实现的静态代理的扩展逻辑是写在代理类中啊,这样看来很明显对象桥接模式强调的是拓展逻辑的维度,同时应该注意构造注入桥接模式构造方法传递的只能是接口,而这种构造方式往往在抽象父类的构造方法中写死,而继承的思想实现的静态代理传递的是被代理对象构造方法需要的值甚至可以是无参构造(毕竟子类可以继承父类的所有方法可以很方便的通过重写来实现逻辑扩展),构造注入桥接模式其实就是通过构造注入的方式实现接口属性的注入,而set注入桥接模式采用set注入的方式实现接口属性的注入,这也是两种桥接模式的本质区别,但这并不影响set注入桥接模式在拓展逻辑上应用,反而使得代码变的更加灵活,构造注入只能在对象创建前注入,而set注入,可以在对象创建后注入,如果在只考虑单线程的情况下,我们可以用一个对象来应对各种扩展逻辑的变化,当然这不太符合单一职责原则,谨慎使用为妙!

? ? ? ??下面我们讲讲适配器模式本质,适配器解决是接口不兼容问题,将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。

? ? ? ? 适配器模式也分为类适配器模式(类适配器模式可以适配多个被适配者类,但是被适配者类的方法必须是可访问的,本质就是通过继承的方式实现;),对象适配器模式(对象适配器模式可以适配多个被适配者类,且被适配者类的方法可以是私有的,而且适配器类可以与被适配者类独立变化,本质就是通过组合的方式实现;),和接口适配器模式(接口适配器模式可以适配多个目标接口,因为适配器类是抽象类,可以定义多个抽象方法和默认实现),双向适配器模式(其实就是在适配器中组合双向适配的接口,然后在具体实现中完成方法调用的转换),单接口/缺省适配器模式(当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求。它适用于不想使用一个接口中的所有方法的情况)。,,没啥好讲的,适配器模式可能是结构型模式中最好理解的设计模式了。如过场景可以在组合和继承的实现方式中选择一种优先选组合方式来实现适配器-组合/聚合优先原则(注意这里的组合可不是组合模式)

? ? ? ? 后面我们谈谈组合模式组合模式本质是将树形结构的一些需要递归遍历的操作通过结构的设计省去调用递归函数的逻辑,组合模式的关键是定义了一个抽象构件类,它既可以代表叶子,又可以代表容器。客户端针对该抽象构件类进行编程,无须知道它到底表示的是叶子还是容器,可以对其进行统一处理。同时容器对象与抽象构件类之间还建立一个聚合关联关系,在容器对象中既可以包含叶子,也可以包含容器,以此实现递归组合,形成一个树形结构。

? ? ? ? 组合模式又分透明组合模式如图(抽象构件中声明了所有用于管理成员对象的方法)

和安全组合模式如图

(抽象构件中只声明叶子节点和树枝节点的共有方法,因为叶子构件和容器构件具有不同的方法,且容器构件中那些用于管理成员对象的方法没有在抽象构件类中定义,客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件)

一般情况下这里最佳的选择方案应该是在透明组合模式的基础上对叶子节点实现类的管理成员对象的方法做一些安全校验与异常处理与提示。

? ? ? ??再后面我们谈谈享元模式(定义:运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,是一种对象结构型模式。享元模式本质就是通过共享技术实现相同或相似对象的重用从而降低程序运行时对jvm内存的消耗,

实现享元模式关键在于区分内部状态外部状态,不同于桥接模式的抽象与实现的分离,享元模式通过将对象的内部状态共享在缓存中,外部状态由创建时或创建后指定

享元模式类图

??复合享元模式类图

? ? ? ?

???????? 最后讲讲外观模式(外部与一个子系统的通信通过一个统一的外观角色进行,为子系统中的一组接口提供一个一致的入口。外观模式定义了一个高层接口,这个接口使得子系统更加容易使用。外观模式又称为门面模式,它是一种对象结构型模式。)

在软件开发中,有时候为了完成一项较为复杂的功能,一个类需要和多个其他业务类交互,而这些需要交互的业务类经常会作为一个完整的整体出现,由于涉及的类比较多,导致使用时代码较为复杂。此时,特别需要一个类似服务员一样的角色,由它来负责和多个业务类进行交互,而使用这些业务类的类只需和该类交互即可。外观模式通过引入一个新的外观类来实现该功能。外观类多个业务类的调用提供了一个统一的入口,简化了类与类之间的交互。

?

? ? ? ??

文章来源:https://blog.csdn.net/weixin_42206151/article/details/134815610
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。