介绍Java结构型设计模式

介绍Java结构型设计模式

本文我们聚焦java中实现结构设计模式,讨论它们是什么以及它们之间的基本差异。

设计模式分类

根据GOF对设计模式的分类,有三种:

  1. 创建型模式
  2. 结果型模式
  3. 行为型模式

简言之,结构模式处理类和对象的组合。它们提供不同方式使用对象组合和继承创建一些抽象。属于结构型模式有代理模式,装饰模式,适配器模式,桥接模式,门面模式享元模式组合模式

代理模式

通过该模式,我们创建媒介作为其他资源的接口,如文件、连接。这种辅助方式作为真正组件的代理,隐藏底层的复杂性。

示例

假设有一个需要初始化配置的大对象(如JDBC连接或SessionFactory)。这样对象一旦被初始化后,希望后续调用可以重用:

uml

下面创建一个简单接口,用于配置该对象:

public interface ExpensiveObject {
    void process();
}

然后实现该接口,带有繁重的初始化配置:

public class ExpensiveObjectImpl implements ExpensiveObject {
 
    public ExpensiveObjectImpl() {
        heavyInitialConfiguration();
    }
     
    @Override
    public void process() {
        LOG.info("processing complete.");
    }
     
    private void heavyInitialConfiguration() {
        LOG.info("Loading initial configuration...");
    }
     
}

现在利用代理模式根据需要初始化对象:

public class ExpensiveObjectProxy implements ExpensiveObject {
    private static ExpensiveObject object;
 
    @Override
    public void process() {
        if (object == null) {
            object = new ExpensiveObjectImpl();
        }
        object.process();
    }
}

无论什么时间客户端调用process方法,它们仅能看到方法调用,而初始化配置总是被隐藏:

public static void main(String[] args) {
    ExpensiveObject object = new ExpensiveObjectProxy();
    object.process();
    object.process();
}

注意当调用process方法两次,背后初始化部分仅执行一次,即对象初始化。后续每次调用,该模式会跳过初始化过程,仅调用process方法:

Loading initial configuration...
processing complete.
processing complete.

什么时候使用代理模式

理解如何使用模式很重要,但理解什么时候使用时关键的。下面讨论什么时候使用代理模式:

  • 当我们想简化复杂或繁重对象时。这时我们可以使用框架对象表示原始对象,其根据需要加载原始对象,也称为懒加载。即所谓虚拟代理。

  • 当原始对象在不同的地址空间中,我们想在本地表示它。可以创建其代理负责执行所有必要的例行操作,如创建和维护连接、编码、解码等。当客户端访问它时,它存在本地地址空间中。这称为远程代理。

  • 当我们想要在原始基础对象上添加一层安全性,以便基于客户机的访问权限提供受控访问时。这称为保护代理。

关键差异

  • 代理提供与原理对象相同接口,其包括对原始对象的引用,但它不会以任何方式修改数据;与适配器和装饰器模式相反,它们两者分别更改和装饰已有实例的功能。

  • 代理通常在编译时拥有关于实际主题的信息,而装饰器和适配器则在运行时被注入,只知道实际对象的接口。

装饰模式

该模式用于增强一个对象的行为。详细内容请看这篇博文。

关键差异包括下面几点:

  • 虽然代理和装饰器模式有类似的结构,但它们的意图不同。代理模式首要目的是便于使用或控制访问,装饰模式是增加额外的功能。
  • 两者都持有原始对象的引用。
  • 装饰器模式中装饰元素可以递归使用,不限制次数,而其他模式不能。

适配器模式

适配器模式用于连接两个不兼容接口,否则两者不能直接连接。适配器使用新的接口包装已经存在的类,这样使其兼容称为我们需要的接口。详细描述可以查看介绍java适配器短文。

适配器与代理模式的主要差异点为:

  • 虽然代理模式提供了相同的接口,但适配器模式提供了与其客户机兼容的不同接口。
  • 适配器模式在应用组件已经设计好之后使用,因此可以不修改源码进行使用。这与桥接模式相反,其一般在组件设计之前使用。

桥接模式

GOF官方定义桥接模式为将抽象与其实现解耦,以便两者能够独立地变化。
这意味着使用OOP原则创建一个将职责划分为不同抽象类的桥接接口。

示例

对于Bridge模式,我们将考虑两个抽象层;一个是几何形状(如三角形和正方形),填充了不同的颜色(我们的第二个抽象层):

uml img

首先定义color接口:

public interface Color {
    String fill();
}

然后定义接口的实现类:

public class Blue implements Color {
    @Override
    public String fill() {
        return "Color is Blue";
    }
}

现在创建抽象Shape类,其包含一个color对象引用(桥接):

public abstract class Shape {
    protected Color color;
     
    //standard constructors
     
    abstract public String draw();
}

现在创建Shape的具体实现类,其也利用Color接口的方法:

public class Square extends Shape {
 
    public Square(Color color) {
        super(color);
    }
 
    @Override
    public String draw() {
        return "Square drawn. " + color.fill();
    }
}

测试该模式,下面测试代码返回true:

@Test
public void whenBridgePatternInvoked_thenConfigSuccess() {
    //a square with red color
    Shape square = new Square(new Red());
  
    assertEquals(square.draw(), "Square drawn. Color is Red");
}

这里我们使用桥接模式并传入期望的color对象。通过输出信息shape使用期望的color进行绘制:

Square drawn. Color: Red
Triangle drawn. Color: Blue

什么时候使用桥接模式

  • 当我们想要父抽象类定义一组基础规则,具体实现类增加额外规则时。
  • 当我们有一个抽象类,它有一个对象引用,并且它有抽象方法,这些方法将在每个具体类中重载。

关键差异点

  • 桥接模式仅在应用设计之前被实现。
  • 桥接模式允许抽象和实现独立更改,而适配器模式是不兼容类可以连接成为可能。

总结

本文我们聚焦结构型模式并对比分析它们之间差异。

相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页