结构型模式

适配器

将一个接口转换成另一个接口,通过使用适配器模式,不兼容的类可以一起工作

  1. 解决接口不兼容问题:当现有的类接口与预期接口不匹配时,适配器模式可以通过转换接口,解决不兼容问题,让不同接口的类能够协同工作。
  2. 提高代码复用性:适配器模式通过封装现有类的接口,使其符合预期接口,从而避免了对现有代码的修改和重写,大大提高了代码的复用性。
  3. 系统扩展性:通过适配器模式,可以在不修改已有代码的情况下扩展系统功能,增强系统的灵活性
classDiagram    class Target {        +ops()    }    class Adapter {        +ops()    }    class Adaptee {        +run()    }    class ConcreteTarget {        +ops()    }    Target <|-- Adapter: 实现    Adaptee <--* Adapter: 组合    Target <|-- ConcreteTarget    Client --> Target
interface Target{    void ops();}class Adaptee{    public void run() { }}class Adapter implements Target{    private Adaptee adaptee;    public Adapter(Adaptee adaptee) {        this.adaptee = adaptee;    }    @Override    public void ops() {        // do something        adaptee.run();    }}// 使用Target target = new Adapter(new Adaptee());target.ops();

分类:

桥接

将抽象部分与实现部分分离,使它们都可以独立地变化

  1. 解决多维度变化的问题:当一个类存在多个独立变化的维度时,直接使用继承会导致类的数量呈指数级增长。例如,如果一个图形类既需要支持不同的形状(如圆形、方形)又需要支持不同的颜色(如红色、蓝色),使用继承会导致类的组合数量急剧增加。
  2. 提高系统的灵活性和可维护性:通过桥接模式,抽象部分和实现部分可以独立演化,减少了它们之间的耦合。这使得系统更易于扩展和维护,因为修改一个部分不会直接影响到另一个部分。
  3. 支持动态变化:桥接模式通过组合关系替代继承关系,使得在运行时可以动态地改变实现部分,而不需要修改抽象部分的代码。这为系统带来了更大的灵活性
abstract class Window {    //...    abstract setMenu(Menu menu)}interface Menu{}class LinuxWindow extends Window{...}class MacWindow extends Window{...}class PlainMenu implements Menu{...}class RichMenu implements Menu{...}

组合

将对象组合成树形结构的部分-整体层次结构,使得客户使用单个对象或组合对象都有一致性

classDiagram    class Component {        -string _name        +Add(Component) void        +Remove(Component) void        +Display(int) void    }    class Leaf {        +Add(Component) void        +Remove(Component) void        +Display(int) void    }    class Composite {        -List~Component~ children        +Add(Component) void        +Remove(Component) void        +Display(int) void    }    class Client    Component <|-- Leaf : 继承    Component <|-- Composite : 继承    Client --> Component : 依赖    note for Leaf "叶子节点,没有子节点\n叶子节点不能包含子对象,因此其Add和Remove没有实际意义"    note for Composite "有叶节点行为,用来存储子对象\n并实现Component的操作,比如Add和Remove\nchildren用来保存子对象"    note for Component "组合中的对象需要声明在\n父类中的操作在所有子类中都\n定义了公共操作的行为接口\n是构成Component的原始部件"
interface Route {    Route segement1, segement2;} // 路线class NationalHighway implements Route{} // 国道class CountryRoad implements Route {} // 乡道

组合模式很重要的一点就是客户端使用所有节点的方式都相同,同时这些节点内部又以组合的方式组合其他节点。组合模式特别适合那些能够用树形结构表示的数据,例如文件系统、组织架构等

装饰器

给一个对象添加额外的职责

为了应对继承关系过于复杂的问题。通过装饰器模式,可以通过组合替代继承,从而简化代码结构,并为原始类添加增强功能。如果仅使用继承的方式来扩展功能,可能会导致类的数量爆炸,继承层次变得复杂且难以维护

classDiagram    class Component {        +ops()    }    class ConcreteComponent {        +ops()    }    class Decorator {        +ops()    }    class ConcreteDecorator {        +ops()        -component Compoment    }    Component <|-- Decorator    Decorator <|-- ConcreteDecorator    Component <|-- ConcreteComponent

装饰器与被装饰的对象都拥有同一个接口,所以说,装饰器对客户来说是透明的

abstract class InputStream{...}class FileInputStream extends InpurStream{...}

外观

为子系统中的一组接口提供一个统一的高层接口,使子系统更容易使用

classDiagram    class Facade {        +ops()    }    class SystemA {        +opsA()    }    class SystemB {        +opsB()    }    Facade <-- SystemA    Facade <-- SystemB    Client --> Facade
class Facade{    private SubSystem1 subSystem1;    private SubSystem2 subSystem2;    void ops(){        subSystem1.ops1();        subSystem2.ops2();    }}// 使用Facade facade = new Facade();facade.ops();

外观模式解决的是接口易用性问题,通过封装减少使用者需要了解的信息

享元

共享系统中大量的细粒度对象

classDiagram    Flyweight <|-- ConcreteFlyweight    Flyweight <|-- UnsharedConcreteFlyweight    FlyweightFactory *--> Flyweight    class FlyweightFactory {        +getFlyweight(type)    }    Client --> FlyweightFactory    Client --> ConcreteFlyweight    Client --> UnsharedConcreteFlyweight
class MessageFactory{    Message getHeartBeatMeessage();}interface Message{...}class HeartBeatMessage implements Message{...}

代理

为其他对象提供一个代理访问控制

代理模式的主要作用是通过引入代理类,在不改变原始类代码的情况下,为原始类添加附加功能。代理模式可以帮助将非业务功能(如监控、日志、缓存等)与核心业务逻辑解耦,简化代码维护和开发。动态代理则进一步解决了静态代理中需要为每个类单独创建代理类的问题,动态地在运行时生成代理类,大大减少了重复代码和开发成本

classDiagram    class Subject {        +action()    }    class SubjectProxy {        -target: ConcreteSubject        +action()    }    Subject <|-- ConcreteSubject    SubjectProxy o--> ConcreteSubject    Client --> Subject

静态代理

interface Subject{    void run();}class Proxy implements Subject{    private Subject realObject = new RealSubject();    void run(){        //before        realObject.run();        //after    }}// 使用Subject subject = new Proxy();subject.run();

动态代理

SubjectImpl impl = new SubjectImpl();Subject proxy = (Subject) Proxy.newProxyInstance(impl.getClass().getClassLoader(),                 impl.getClass().getInterfaces(), (proxy1, method, args1) -> {    System.out.println("pre invoke");    return method.invoke(impl, args1);});proxy.request();

如果多个接口重名,则调用接口方法以第一个接口为主

Enhancer enhancer = new Enhancer();Object target = new Object();enhancer.setSuperclass(Object.class);enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> {    System.out.println(method+" invoke");    return method.invoke(target, args1);});Object o = enhancer.create();System.out.println(o.hashCode());

generation gap(生成模式)

该模式会生成代码导出行为 再通过编程来丰富程序行为 只修改或扩展一次 而可以生成代码多次 win下的GUI设计好像就这么做的

classDiagram    class Client {    }    class CoreClass {        +state        +operation()    }        class ExtensionClass {        +addedState        +operation()        +newOperation()    }        Client --> ExtensionClass    CoreClass <|-- ExtensionClass    CoreClass : operation() -- generated and/or default implementation    ExtensionClass : operation() -- modified and/or extended implementation

问题:

需求变化导致生成代码的变化

解决方式一是警告禁止修改 二是计算代码差异重新生成 三则是隐藏生成的代码的细节 隔离变与不变