MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java结构型设计模式分析

2023-02-113.3k 阅读

一、代理模式

代理模式是一种结构型设计模式,它允许通过代理对象来控制对真实对象的访问。代理对象与真实对象实现相同的接口,客户端通过代理对象间接访问真实对象。

1.1 静态代理

静态代理在编译时就已经确定了代理类,代理类与被代理类实现相同的接口。

// 定义接口
interface Subject {
    void request();
}

// 真实主题类
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("真实主题执行请求");
    }
}

// 代理类
class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
        System.out.println("代理类预处理");
        realSubject.request();
        System.out.println("代理类后处理");
    }
}

使用静态代理:

public class StaticProxyExample {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Proxy proxy = new Proxy(realSubject);
        proxy.request();
    }
}

静态代理的优点是实现简单,容易理解。缺点是如果接口方法过多,代理类代码会变得臃肿,且每个被代理类都需要单独创建代理类,不易维护。

1.2 动态代理

动态代理是在运行时动态生成代理类。Java 提供了 java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler 来实现动态代理。

// 定义接口
interface Subject {
    void request();
}

// 真实主题类
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("真实主题执行请求");
    }
}

// 调用处理器
class DynamicProxyHandler implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("动态代理预处理");
        Object result = method.invoke(target, args);
        System.out.println("动态代理后处理");
        return result;
    }
}

生成动态代理实例:

import java.lang.reflect.Proxy;

public class DynamicProxyExample {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        DynamicProxyHandler handler = new DynamicProxyHandler(realSubject);
        Subject proxy = (Subject) Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(),
                handler);
        proxy.request();
    }
}

动态代理的优点是可以在运行时动态生成代理类,灵活性高。缺点是相对静态代理,实现较为复杂,需要对反射机制有深入理解。

二、适配器模式

适配器模式用于将一个类的接口转换成客户希望的另一个接口。它使得原本由于接口不兼容而不能一起工作的类可以协同工作。

2.1 类适配器

类适配器通过继承适配者类,实现目标接口。

// 目标接口
interface Target {
    void request();
}

// 适配者类
class Adaptee {
    public void specificRequest() {
        System.out.println("适配者类的特定请求");
    }
}

// 类适配器
class ClassAdapter extends Adaptee implements Target {
    @Override
    public void request() {
        specificRequest();
    }
}

使用类适配器:

public class ClassAdapterExample {
    public static void main(String[] args) {
        Target target = new ClassAdapter();
        target.request();
    }
}

类适配器的优点是实现简单,通过继承可以复用适配者的部分代码。缺点是 Java 不支持多重继承,如果适配者类已经有父类,则无法使用类适配器。

2.2 对象适配器

对象适配器通过组合的方式,持有适配者类的实例,并实现目标接口。

// 目标接口
interface Target {
    void request();
}

// 适配者类
class Adaptee {
    public void specificRequest() {
        System.out.println("适配者类的特定请求");
    }
}

// 对象适配器
class ObjectAdapter implements Target {
    private Adaptee adaptee;

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

    @Override
    public void request() {
        adaptee.specificRequest();
    }
}

使用对象适配器:

public class ObjectAdapterExample {
    public static void main(String[] args) {
        Adaptee adaptee = new Adaptee();
        Target target = new ObjectAdapter(adaptee);
        target.request();
    }
}

对象适配器的优点是灵活性高,通过组合可以适配不同的适配者对象,且避免了多重继承的问题。缺点是需要额外的对象组合,相对类适配器代码稍显复杂。

三、桥接模式

桥接模式将抽象部分与它的实现部分分离,使它们都可以独立地变化。它通过将抽象和实现之间的耦合关系解耦,提高系统的可扩展性和可维护性。

// 抽象化角色
abstract class Abstraction {
    protected Implementor implementor;

    public Abstraction(Implementor implementor) {
        this.implementor = implementor;
    }

    public abstract void operation();
}

// 扩展抽象化角色
class RefinedAbstraction extends Abstraction {
    public RefinedAbstraction(Implementor implementor) {
        super(implementor);
    }

    @Override
    public void operation() {
        System.out.println("扩展抽象化操作");
        implementor.operationImpl();
    }
}

// 实现化角色
interface Implementor {
    void operationImpl();
}

// 具体实现化角色
class ConcreteImplementorA implements Implementor {
    @Override
    public void operationImpl() {
        System.out.println("具体实现化 A 的操作");
    }
}

class ConcreteImplementorB implements Implementor {
    @Override
    public void operationImpl() {
        System.out.println("具体实现化 B 的操作");
    }
}

使用桥接模式:

public class BridgePatternExample {
    public static void main(String[] args) {
        Implementor implementorA = new ConcreteImplementorA();
        Abstraction abstraction = new RefinedAbstraction(implementorA);
        abstraction.operation();

        Implementor implementorB = new ConcreteImplementorB();
        abstraction = new RefinedAbstraction(implementorB);
        abstraction.operation();
    }
}

桥接模式的优点是分离了抽象和实现,使得它们可以独立变化,提高了系统的可维护性和可扩展性。缺点是增加了系统的理解和设计难度,需要仔细分析抽象和实现之间的关系。

四、装饰器模式

装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。它通过创建一个装饰器类,将原始对象包装起来,并在保持接口一致的情况下添加新的行为。

// 抽象组件
abstract class Component {
    public abstract void operation();
}

// 具体组件
class ConcreteComponent extends Component {
    @Override
    public void operation() {
        System.out.println("具体组件的操作");
    }
}

// 抽象装饰器
abstract class Decorator extends Component {
    protected Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public void operation() {
        component.operation();
    }
}

// 具体装饰器
class ConcreteDecoratorA extends Decorator {
    public ConcreteDecoratorA(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        super.operation();
        System.out.println("具体装饰器 A 添加的功能");
    }
}

class ConcreteDecoratorB extends Decorator {
    public ConcreteDecoratorB(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        super.operation();
        System.out.println("具体装饰器 B 添加的功能");
    }
}

使用装饰器模式:

public class DecoratorPatternExample {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        Component decoratedComponentA = new ConcreteDecoratorA(component);
        decoratedComponentA.operation();

        Component decoratedComponentB = new ConcreteDecoratorB(decoratedComponentA);
        decoratedComponentB.operation();
    }
}

装饰器模式的优点是可以动态地给对象添加功能,灵活性高,符合开闭原则。缺点是多层装饰会导致系统复杂度增加,调试困难。

五、外观模式

外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

// 子系统类 A
class SubSystemA {
    public void operationA() {
        System.out.println("子系统 A 的操作");
    }
}

// 子系统类 B
class SubSystemB {
    public void operationB() {
        System.out.println("子系统 B 的操作");
    }
}

// 子系统类 C
class SubSystemC {
    public void operationC() {
        System.out.println("子系统 C 的操作");
    }
}

// 外观类
class Facade {
    private SubSystemA subSystemA;
    private SubSystemB subSystemB;
    private SubSystemC subSystemC;

    public Facade() {
        subSystemA = new SubSystemA();
        subSystemB = new SubSystemB();
        subSystemC = new SubSystemC();
    }

    public void operation() {
        subSystemA.operationA();
        subSystemB.operationB();
        subSystemC.operationC();
    }
}

使用外观模式:

public class FacadePatternExample {
    public static void main(String[] args) {
        Facade facade = new Facade();
        facade.operation();
    }
}

外观模式的优点是简化了子系统的使用,提高了子系统的独立性和可维护性。缺点是不符合开闭原则,如果需要修改子系统的功能,可能需要修改外观类的代码。

六、享元模式

享元模式运用共享技术有效地支持大量细粒度的对象。它通过共享已经存在的对象来减少对象的创建,从而降低内存消耗。

// 享元接口
interface Flyweight {
    void operation(String extrinsicState);
}

// 具体享元类
class ConcreteFlyweight implements Flyweight {
    private String intrinsicState;

    public ConcreteFlyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }

    @Override
    public void operation(String extrinsicState) {
        System.out.println("具体享元操作,内部状态:" + intrinsicState + ",外部状态:" + extrinsicState);
    }
}

// 享元工厂类
class FlyweightFactory {
    private static final Map<String, Flyweight> flyweights = new HashMap<>();

    public static Flyweight getFlyweight(String key) {
        if (!flyweights.containsKey(key)) {
            flyweights.put(key, new ConcreteFlyweight(key));
        }
        return flyweights.get(key);
    }
}

使用享元模式:

public class FlyweightPatternExample {
    public static void main(String[] args) {
        Flyweight flyweight1 = FlyweightFactory.getFlyweight("A");
        Flyweight flyweight2 = FlyweightFactory.getFlyweight("A");
        flyweight1.operation("外部状态 1");
        flyweight2.operation("外部状态 2");
    }
}

享元模式的优点是减少了对象的创建,节省内存,提高系统性能。缺点是增加了系统的复杂度,需要分离内部状态和外部状态,且享元对象的共享状态可能被多个对象修改,需要注意线程安全问题。

七、组合模式

组合模式将对象组合成树形结构以表示“部分 - 整体”的层次结构。它使得用户对单个对象和组合对象的使用具有一致性。

// 抽象组件
abstract class Component {
    protected String name;

    public Component(String name) {
        this.name = name;
    }

    public abstract void add(Component component);
    public abstract void remove(Component component);
    public abstract void display(int depth);
}

// 叶子组件
class Leaf extends Component {
    public Leaf(String name) {
        super(name);
    }

    @Override
    public void add(Component component) {
        System.out.println("叶子节点不能添加子节点");
    }

    @Override
    public void remove(Component component) {
        System.out.println("叶子节点不能移除子节点");
    }

    @Override
    public void display(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("-");
        }
        System.out.println(name);
    }
}

// 容器组件
class Composite extends Component {
    private List<Component> children = new ArrayList<>();

    public Composite(String name) {
        super(name);
    }

    @Override
    public void add(Component component) {
        children.add(component);
    }

    @Override
    public void remove(Component component) {
        children.remove(component);
    }

    @Override
    public void display(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("-");
        }
        System.out.println(name);
        for (Component child : children) {
            child.display(depth + 2);
        }
    }
}

使用组合模式:

public class CompositePatternExample {
    public static void main(String[] args) {
        Composite root = new Composite("根节点");
        Composite branch1 = new Composite("分支 1");
        Composite branch2 = new Composite("分支 2");
        Leaf leaf1 = new Leaf("叶子 1");
        Leaf leaf2 = new Leaf("叶子 2");
        Leaf leaf3 = new Leaf("叶子 3");

        root.add(branch1);
        root.add(branch2);
        branch1.add(leaf1);
        branch2.add(leaf2);
        branch2.add(leaf3);

        root.display(0);
    }
}

组合模式的优点是清晰地定义了分层次的复杂对象,表示对象的“部分 - 整体”关系,使得客户端能够一致地处理单个对象和组合对象。缺点是设计较为复杂,当树形结构复杂时,维护和调试困难,且可能会导致性能问题,特别是在遍历树形结构时。

通过对这些 Java 结构型设计模式的分析和代码示例,希望能帮助开发者在实际项目中根据具体需求选择合适的模式,提高代码的可维护性、可扩展性和复用性。在实际应用中,需要综合考虑项目的规模、复杂度、性能要求等因素,灵活运用这些设计模式。同时,不断实践和总结经验,才能更好地掌握和应用它们,打造高质量的 Java 应用程序。