从抽象工厂模式到JDK源码:设计模式让代码更优雅

在日常开发中,我们往往容易忽视设计模式的重要性。这可能是由于项目时间紧迫,或者对设计模式缺乏深入了解。实际上,在很多情况下,我们已经在不经意间使用了一些设计模式。关键在于我们需要有意识地学习并运用它们,从而让代码变得更加优雅和高效。或许现在正是时候重新审视我们的编程实践,将设计模式真正融入其中。

为了更好地理解设计模式,我们从最基础的——抽象工厂模式——开始,一起来“重学设计模式”。

灵魂拷问

当我们需要增加新的产品时,通过扩展新的工厂类并在客户端中使用该工厂类来创建对象,与直接在客户端中实例化对象,有什么本质区别呢?

一、不采用抽象工厂模式

假设我们正在设计一个系统,用于创建不同配置的电脑,例如PC和服务器。每种类型的电脑都有各自不同的CPU和内存。

抽象产品


/**
 * 抽象产品 - CPU
 */
public interface CPU {
    void compute();
}

/**
 * 抽象产品 - 内存
 */
public interface Memory {
    void store();
}

具体产品

/**
 * 具体产品 - PC 的 CPU 实现
 */
public class PCCPU implements CPU {
    @Override
    public void compute() {
        System.out.println("PC CPU is computing");
    }
}

/**
 * 具体产品 - PC 的内存实现
 */
public class PCMemory implements Memory {
    @Override
    public void store() {
        System.out.println("PC Memory is storing data");
    }
}

/**
 * 具体产品 - 服务器的 CPU 实现
 */
public class ServerCPU implements CPU {
    @Override
    public void compute() {
        System.out.println("Server CPU is computing with high performance");
    }
}

/**
 * 具体产品 - 服务器的内存实现
 */
public class ServerMemory implements Memory {
    @Override
    public void store() {
        System.out.println("Server Memory is storing data with high capacity");
    }
}

测试类 - 不用抽象工厂模式

/**
 * 不使用抽象工厂模式
 */
public class Client {
    public static void main(String[] args) {
        // 创建 PC 的组件
        CPU pcCPU = new PCCPU();
        Memory pcMemory = new PCMemory();
        pcCPU.compute();
        pcMemory.store();

        // 创建服务器的组件
        CPU serverCPU = new ServerCPU();
        Memory serverMemory = new ServerMemory();
        serverCPU.compute();
        serverMemory.store();
    }
}

新增笔记本的CPU和内存

/**
 * 具体产品 - 新增笔记本电脑的 CPU 实现
 */
public class LaptopCPU implements CPU {
    @Override
    public void compute() {
        System.out.println("Laptop CPU is computing efficiently");
    }
}

/**
 * 具体产品 - 笔记本电脑的内存实现
 */
public class LaptopMemory implements Memory {
    @Override
    public void store() {
        System.out.println("Laptop Memory is storing data efficiently");
    }
}

在Client测试类中新增

// 创建笔记本的组件
CPU laptopCPU = new LaptopCPU();
laptopCPU.compute();
LaptopMemory laptopMemory = new LaptopMemory();
laptopMemory.store();

这个过程看起来确实很方便。

二、引入抽象工厂模式进行优化

抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,提供了一个接口来创建一组相关或相互依赖的对象,而无需指定它们的具体类。通过使用抽象工厂模式,客户端可以通过接口创建一系列相关对象,而无需知道这些对象的具体实现。

抽象工厂模式通常由以下几个部分组成:

  • 抽象工厂接口(Abstract Factory):定义创建产品的接口。

  • 具体工厂类(Concrete Factory):实现抽象工厂接口,生成具体的产品。

  • 抽象产品接口(Abstract Product):定义产品的接口。

  • 具体产品类(Concrete Product):实现具体的产品。

  • 客户端(Client):通过工厂接口来使用产品,而不依赖于具体产品的实现。

抽象工厂接口​​​​​​​

/**
 * 抽象工厂
 */
public interface ComputerFactory {
    CPU createCPU();
    Memory createMemory();
}

具体工厂类

/**
 * 具体工厂 - PC 工厂
 */
public class PCFactory implements ComputerFactory {
    @Override
    public CPU createCPU() {
        return new PCCPU();
    }

    @Override
    public Memory createMemory() {
        return new PCMemory();
    }
}

/**
 * 具体工厂 - 服务器工厂
 */
public class ServerFactory implements ComputerFactory {
    @Override
    public CPU createCPU() {
        return new ServerCPU();
    }

    @Override
    public Memory createMemory() {
        return new ServerMemory();
    }
}

测试类 - 抽象工厂模式​​​​​​​

public class AbstractFactoryClient {
    public static void main(String[] args) {
        // 使用 PC 工厂创建 PC 组件
        ComputerFactory pcFactory = new PCFactory();
        CPU pcCPU = pcFactory.createCPU();
        Memory pcMemory = pcFactory.createMemory();
        pcCPU.compute();
        pcMemory.store();

        // 使用服务器工厂创建服务器组件
        ComputerFactory serverFactory = new ServerFactory();
        CPU serverCPU = serverFactory.createCPU();
        Memory serverMemory = serverFactory.createMemory();
        serverCPU.compute();
        serverMemory.store();
    }
}

新增笔记本电脑的具体工厂类

public class LaptopFactory implements ComputerFactory {
    @Override
    public CPU createCPU() {
        return new LaptopCPU();
    }

    @Override
    public Memory createMemory() {
        return new LaptopMemory();
    }
}

在测试类中新增

// 使用笔记本工厂
ComputerFactory laptopFactory = new LaptopFactory();
CPU laptopCPU = laptopFactory.createCPU();
Memory laptopMemory = laptopFactory.createMemory();
laptopCPU.compute();
laptopMemory.store();

三、两者有何本质区别?

  1. 解耦:不使用抽象工厂模式时,客户端代码(Client)直接依赖于具体的产品类(如PCCPUPCMemoryServerCPU等)。当我们需要增加新类型的电脑或修改现有配置时,必须修改客户端代码。而抽象工厂模式引入了工厂接口(ComputerFactory),客户端通过工厂接口创建对象,而不直接实例化具体类。这样,客户端与具体产品实现解耦,代码更加灵活,且产品的创建逻辑集中在具体工厂中,客户端只需关心如何使用产品。

  2. 扩展性:不使用工厂模式时,如果要添加一个新产品(如平板电脑的CPU和内存),需要修改客户端代码以增加新的产品实例化逻辑,这违反了开闭原则(对扩展开放,对修改关闭)。抽象工厂模式允许通过创建新的工厂(如LaptopFactory)来生成新产品,而无需修改客户端代码。新增产品时只需添加新的工厂类,无需修改现有代码,降低了维护成本,增强了系统的灵活性。

  3. 单一职责原则:在没有使用工厂模式的情况下,客户端代码不仅负责业务逻辑,还负责产品的创建逻辑,违反了单一职责原则。而使用抽象工厂模式后,客户端的职责更加单一,只负责业务逻辑处理,产品的创建由工厂负责。通过将产品的创建与使用分离,代码变得更加清晰、职责明确。

四、在JDK源码中,抽象工厂模式的应用

在JDK源码中,抽象工厂模式被广泛用于创建一组相关的对象。一个典型的例子是 javax.xml.parsers.DocumentBuilderFactory 和 javax.xml.parsers.SAXParserFactory。它们都是JDK中抽象工厂模式的具体实现,用于创建与XML解析相关的对象(如 DocumentBuilder 和 SAXParser),而客户端无需了解这些对象的具体实现。

1. DocumentBuilderFactory 在 JDK 中的应用

(1)抽象工厂类:DocumentBuilderFactory

DocumentBuilderFactory 是一个抽象工厂,用于创建 DocumentBuilder 对象,负责解析XML文档。

newInstance() 方法返回一个具体的工厂实现(例如 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl),该工厂负责创建具体的 DocumentBuilder 对象。

package javax.xml.parsers;

public abstract class DocumentBuilderFactory {
    // 用于创建 DocumentBuilder 实例
    public abstract DocumentBuilder newDocumentBuilder() throws ParserConfigurationException;

    // 工厂方法,用于获取 DocumentBuilderFactory 的实例
    public static DocumentBuilderFactory newInstance() {
        return FactoryFinder.find(DocumentBuilderFactory.class, "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
    }
}

(2)具体工厂类:DocumentBuilderFactoryImpl

DocumentBuilderFactoryImpl 是 DocumentBuilderFactory 的一个具体实现,定义了如何创建 DocumentBuildernewDocumentBuilder() 返回一个 DocumentBuilderImpl 实例,负责实际的XML解析。​​​​​​​

package org.apache.xerces.jaxp;

public class DocumentBuilderFactoryImpl extends DocumentBuilderFactory {
    @Override
    public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
        return new DocumentBuilderImpl();
    }
}

(3)客户端使用

在客户端代码中,通过 DocumentBuilderFactory 来创建 DocumentBuilder 对象,而无需关心其具体实现。​​​​​​​

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

public class XMLParserExample {
    public static void main(String[] args) {
        try {
            // 获取 DocumentBuilderFactory 实例(抽象工厂)
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            
            // 使用工厂创建 DocumentBuilder(具体产品)
            DocumentBuilder builder = factory.newDocumentBuilder();
            
            // 使用 DocumentBuilder 解析 XML 文档
            // 示例代码忽略了具体 XML 文件的解析逻辑
            System.out.println("DocumentBuilder created successfully!");
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

通过 DocumentBuilderFactory.newInstance(),客户端获取一个具体的 DocumentBuilderFactory 实例,然后利用该工厂创建 DocumentBuilder 对象。客户端无需关心 DocumentBuilder 的具体实现,只依赖抽象工厂接口,这正是抽象工厂模式的设计思想。

2. SAXParserFactory 的应用

SAXParserFactory 是 JDK 中另一个典型的抽象工厂模式实现,用于创建 SAXParser 对象以解析XML文档。

(1)抽象工厂类:SAXParserFactory

SAXParserFactory 是一个抽象工厂类,定义了创建 SAXParser 的方法。

package javax.xml.parsers;

public abstract class SAXParserFactory {
    // 创建 SAXParser 的抽象方法
    public abstract SAXParser newSAXParser() throws ParserConfigurationException, SAXException;

    // 获取 SAXParserFactory 实例
    public static SAXParserFactory newInstance() {
        return FactoryFinder.find(SAXParserFactory.class, "org.apache.xerces.jaxp.SAXParserFactoryImpl");
    }
}

(2)具体工厂类:SAXParserFactoryImpl

SAXParserFactoryImpl 是 SAXParserFactory 的具体实现类,负责创建 SAXParser 实例。

package org.apache.xerces.jaxp;

public class SAXParserFactoryImpl extends SAXParserFactory {
    @Override
    public SAXParser newSAXParser() throws ParserConfigurationException, SAXException {
        return new SAXParserImpl();
    }
}

(3)客户端使用

客户端通过 SAXParserFactory 创建 SAXParser,解析XML文档。​​​​​​​

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

public class SAXParserExample {
    public static void main(String[] args) {
        try {
            // 获取 SAXParserFactory 实例(抽象工厂)
            SAXParserFactory factory = SAXParserFactory.newInstance();
            
            // 使用工厂创建 SAXParser(具体产品)
            SAXParser parser = factory.newSAXParser();
            
            // 使用 SAXParser 解析 XML 文档
            // 示例代码忽略了具体 XML 文件的解析逻辑
            System.out.println("SAXParser created successfully!");
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
3. 这些应用解决了什么问题?
  • 解耦客户端与具体实现:客户端代码无需知道 DocumentBuilder 或 SAXParser 的具体实现类,而是只依赖于抽象工厂类和抽象产品类。这使得XML解析库可以在不同实现之间自由切换(例如从Apache Xerces切换到其他实现),而无需修改客户端代码。

  • 易于扩展:当需要增加新的 DocumentBuilder 或 SAXParser 实现时,只需添加新的工厂类,而无需修改客户端代码。通过抽象工厂模式,可以轻松地扩展和适应新的需求。

  • 集中控制对象创建:工厂类集中管理对象的创建,统一了对象的创建逻辑,避免了客户端直接负责实例化的复杂性,使代码更加灵活和可维护。

4. 总结

在JDK中,DocumentBuilderFactory 和 SAXParserFactory 等类广泛应用了抽象工厂模式。这种模式确保了客户端代码与具体实现的解耦,同时增强了系统的可扩展性和灵活性。通过使用抽象工厂模式,JDK能够在不同的XML解析器之间自由切换,而不会影响客户端代码。这种设计方式不仅提升了代码的复用性,还使系统更容易维护和扩展。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值