1、工厂模式介绍
工厂模式是一种创建型设计模式,其主要目的是将对象的创建过程抽象出来,以便在需要的时候由子类来实现。这种模式提供了一种方法,通过调用一个共同的接口来创建一组相关或依赖的对象,而无需指定其具体的类。
工厂模式通常涉及两个主要角色:
- 产品(Product): 产品是由工厂创建的对象,它们通常都实现了一个共同的接口或抽象类。
- 工厂(Factory): 工厂是负责创建产品对象的接口或抽象类。它包含一个或多个方法,用于创建不同类型的产品。
2、关键思想:
工厂模式的关键思想是将对象的创建过程进行抽象,以便客户端代码不需要直接实例化具体的类,而是通过调用工厂的方法来获取所需的对象。这种抽象使得系统更加灵活、可维护和可扩展。
以下是工厂模式的关键思想:
- 抽象产品类或接口(Product): 定义了产品的共同接口或抽象类,客户端通过这个接口与产品进行交互,而不是直接依赖于具体的产品类。
- 具体产品类(Concrete Product): 实现了抽象产品接口或继承了抽象产品类的具体类,是工厂模式中实际被创建的对象。
- 抽象工厂类或接口(Factory): 定义了创建产品的方法的接口或抽象类。这个工厂可以是一个单独的类(简单工厂模式)或一个工厂方法的接口(工厂方法模式)。
- 具体工厂类(Concrete Factory): 实现了抽象工厂接口,负责创建具体的产品对象。在工厂方法模式中,每个具体工厂类通常对应一个具体产品类。
- 客户端(Client): 负责使用工厂创建产品的对象。客户端通过工厂的接口或方法来请求产品,而不需要直接实例化具体的产品类。
通过这种方式,工厂模式将对象的创建过程封装在工厂类中,使得客户端代码与具体产品的实现解耦。这不仅使得系统更容易扩展,添加新的产品和工厂,而且提高了代码的可维护性和灵活性。同时,工厂模式也符合设计模式中的开放-封闭原则,使得系统更易于修改和扩展。
3、实现方式:
工厂模式有不同的实现方式,其中包括简单工厂模式和工厂方法模式。下面将分别介绍这两种实现方式的代码示例:
1. 简单工厂模式(Simple Factory Pattern):
在简单工厂模式中,通常只有一个工厂类,负责根据传入的参数创建不同类型的产品。
示例代码:
// 产品接口 interface Product { void display(); } // 具体产品A class ConcreteProductA implements Product { public void display() { System.out.println("这是产品A"); } } // 具体产品B class ConcreteProductB implements Product { public void display() { System.out.println("这是产品B"); } } // 简单工厂类 class SimpleFactory { // 工厂方法,根据传入的类型参数创建不同的产品对象 public static Product createProduct(String type) { if ("A".equals(type)) { return new ConcreteProductA(); } else if ("B".equals(type)) { return new ConcreteProductB(); } // 如果传入的类型不是A或B,则返回null,可以根据实际情况抛出异常或提供默认产品 return null; } } // 客户端代码 public class Client { public static void main(String[] args) { // 使用简单工厂创建产品A Product productA = SimpleFactory.createProduct("A"); // 调用产品A的display方法 productA.display(); // 使用简单工厂创建产品B Product productB = SimpleFactory.createProduct("B"); // 调用产品B的display方法 productB.display(); } }
要点:
- 抽象产品接口或类: 定义产品的通用接口或抽象类,确保所有具体产品都实现了这个接口或继承了这个抽象类。
- 具体产品类: 实现了抽象产品接口或继承了抽象产品类的具体类,是简单工厂模式中实际被创建的对象。
- 简单工厂类: 包含一个或多个工厂方法,根据客户端的需求创建具体产品对象。这个类通常是静态的。
- 客户端代码: 调用简单工厂类的方法创建产品对象,而不直接实例化具体的产品类。
- 类型判断和创建逻辑: 在简单工厂类的工厂方法中,通过条件语句判断传入的类型参数,并实例化相应的具体产品对象。这部分逻辑需要维护和扩展。
- 异常处理: 在简单工厂模式中,如果传入的类型参数无效,工厂方法可能返回null或提供一个默认产品。在实际应用中,可以考虑通过抛出异常来明确表示无效的类型,使得调用者能够更好地处理错误情况。
注意事项:
- 违反开放-封闭原则: 添加新的产品只需创建对应的具体产品类,而不需要修改客户端代码。但这也意味着如果要添加新的产品,需要修改简单工厂类的代码,可能违反开放-封闭原则。
- 单一职责原则: 简单工厂类的职责应该单一,即只负责创建一种产品。如果简单工厂类负责创建多种产品,可能导致类的复杂性增加。
- 灵活性和可维护性: 简单工厂模式通常在产品类型相对稳定,且不需要频繁变化时使用。如果系统需要频繁添加新的产品类型,可能需要考虑使用其他工厂模式。
- 类型参数的合法性: 简单工厂类通常通过判断传入的类型参数来创建具体产品对象,需要确保类型参数的合法性。否则,可能导致返回 null 或其他非预期的结果。
- 可测试性: 由于简单工厂类通常是静态的,可能导致难以进行单元测试。考虑通过依赖注入等方式提高可测试性。
优点:
- 封装对象的创建逻辑: 简单工厂模式将对象的创建逻辑封装在一个工厂类中,使得客户端无需了解具体的对象创建过程,降低了客户端与具体产品类的耦合。
- 客户端简化: 客户端只需知道产品的接口或抽象类以及通过简单工厂方法获取产品的方式,无需关心具体产品的实现。
- 集中管理产品类型: 简单工厂模式集中了对象的创建逻辑,便于集中管理和维护产品类型,对于某些需要频繁变动的系统,可减少修改的工作量。
缺点:
- 扩展性受限: 当需要新增产品类型时,需要修改简单工厂类的创建逻辑,违反了开放-封闭原则。这可能导致简单工厂类变得庞大,难以维护。
- 违反单一职责原则: 简单工厂类通常负责多个产品的创建逻辑,可能导致类的职责不够单一,不利于后期维护和扩展。
- 类型判断逻辑: 简单工厂类通常包含了根据类型参数判断创建哪个具体产品的逻辑,这样的实现可能不够灵活和可扩展。
应用场景:
- 产品稳定不变: 适用于产品的类型相对稳定,不太会有新的产品类型添加或产品类型的变动。
- 客户端无需知道具体实现: 当客户端无需关心具体产品的实现细节,只需通过接口或抽象类与产品进行交互时,可以考虑使用简单工厂模式。
- 对象创建逻辑相对简单: 对象的创建逻辑相对简单,并且不需要在运行时动态地根据不同情况创建不同类型的产品。
总体而言,简单工厂模式是一种简单而直接的创建型设计模式,简单工厂模式适用于一些相对简单的场景适用于对于产品创建逻辑要求较为简单,且产品类型不经常变化的情况。但在面对频繁变化的产品类型和复杂的创建逻辑时,可能需要考虑使用其他工厂模式,如工厂方法模式或抽象工厂模式。
2. 工厂方法模式(Factory Method Pattern):
在工厂方法模式中,每个产品对应一个工厂,工厂类的接口或抽象类定义了创建产品的方法,具体的产品由具体的工厂类实现。
示例代码:
// 产品接口 interface Product { void display(); } // 具体产品A class ConcreteProductA implements Product { public void display() { System.out.println("这是产品A"); } } // 具体产品B class ConcreteProductB implements Product { public void display() { System.out.println("这是产品B"); } } // 工厂接口 interface Factory { // 工厂方法,用于创建产品对象 Product createProduct(); } // 具体工厂A class ConcreteFactoryA implements Factory { // 具体工厂A的工厂方法,创建具体产品A public Product createProduct() { return new ConcreteProductA(); } } // 具体工厂B class ConcreteFactoryB implements Factory { // 具体工厂B的工厂方法,创建具体产品B public Product createProduct() { return new ConcreteProductB(); } } // 客户端代码 public class Client { public static void main(String[] args) { // 创建具体工厂A Factory factoryA = new ConcreteFactoryA(); // 由具体工厂A创建具体产品A Product productA = factoryA.createProduct(); // 调用产品A的display方法 productA.display(); // 创建具体工厂B Factory factoryB = new ConcreteFactoryB(); // 由具体工厂B创建具体产品B Product productB = factoryB.createProduct(); // 调用产品B的display方法 productB.display(); } }
在工厂方法模式中,每个具体工厂类负责创建特定类型的产品,从而实现了对象创建和使用的分离,提高了系统的可扩展性。
要点:
- 抽象产品接口或类: 定义产品的共同接口或抽象类,确保所有具体产品都实现了这个接口或继承了这个抽象类。
- 具体产品类: 实现了抽象产品接口或继承了抽象产品类的具体类,是工厂模式中实际被创建的对象。
- 工厂接口或类: 定义了创建产品的方法的接口或抽象类,该方法返回一个抽象产品对象。可以是抽象类或接口。
- 具体工厂类: 实现了工厂接口或继承了工厂抽象类,负责创建具体产品对象。每个具体工厂类对应一个具体产品类。
- 客户端代码: 调用工厂的接口或方法来请求产品,而不需要直接实例化具体的产品类。
- 类型判断和创建逻辑: 在工厂类的创建方法中,通过条件语句判断传入的类型参数,并实例化相应的具体产品对象。这部分逻辑需要维护和扩展。
- 封装和解耦: 工厂模式的主要目的是封装对象的创建过程,使客户端代码与具体产品的实现解耦,提高系统的灵活性和可维护性。
注意事项:
- 遵循开放-封闭原则: 设计要充分考虑到系统的可扩展性,当需要添加新的产品类型时,不应该修改已有的代码,而是通过扩展来实现。
- 单一职责原则: 工厂类的职责应该单一,即只负责创建一种产品。如果工厂类负责创建多种产品,可能导致类的复杂性增加。
- 考虑异常处理: 工厂类在创建对象时,可能面临传入的类型参数无效的情况,需要考虑如何处理这种异常情况,例如抛出异常或返回默认产品。
- 灵活性和可维护性: 良好的设计应该具备足够的灵活性和可维护性,使得系统能够在需求变化时轻松扩展和修改。
- 适用场景: 工厂模式适用于对象的创建逻辑相对稳定,而客户端需要通过抽象接口与具体产品进行交互的情况。
- 选择合适的工厂模式: 工厂模式有多种变体,包括简单工厂模式、工厂方法模式、抽象工厂模式等。根据需求选择合适的工厂模式。
优点:
- 符合开放-封闭原则: 工厂模式使得系统在增加新产品时变得更加容易,不需要修改已有的代码,只需要添加新的工厂类或产品类。
- 客户端与具体产品解耦: 客户端通过工厂接口或类来创建产品,不需要关心具体产品的实现,从而降低了客户端与具体产品的耦合性。
- 可扩展性强: 新增产品类型只需要添加对应的具体工厂类和产品类,而不需要修改已有代码,提高了系统的可扩展性。
- 隐藏复杂的创建逻辑: 工厂类封装了对象的创建细节,对客户端屏蔽了具体对象的创建过程,使得客户端更加专注于业务逻辑。
缺点:
- 类的数量增加: 每增加一个新产品类型,就需要增加一个对应的具体工厂类和产品类,可能导致类的数量较多。
- 违反了单一职责原则: 在某些情况下,具体工厂类可能会负责多个产品的创建,违反了单一职责原则。
- 不容易支持新种类产品的添加: 如果系统需要支持新种类的产品,除了添加新的产品类和工厂类外,还需要修改工厂接口或抽象工厂类,可能会影响已有代码。
应用场景:
- 对象的创建过程比较复杂: 当对象的创建逻辑比较复杂,包含多个步骤或涉及到一些复杂的判断时,可以使用工厂模式封装创建过程。
- 客户端不需要知道具体产品的类名: 当客户端不需要知道具体产品的类名,只需要通过接口或抽象类与产品进行交互时,可以使用工厂模式。
- 系统中的产品类经常变化: 如果系统中的产品类经常变化,需要灵活支持新增产品类型,工厂模式是一种适合的设计模式。
- 组件的替换性: 工厂模式可以通过配置或者依赖注入,实现组件的替换,从而实现系统的灵活性和可维护性。
总体来说,工厂模式适用于需要创建对象的场景,尤其是当对象的创建过程复杂、需要隐藏创建逻辑、需要灵活支持新增产品类型时。在具体选择工厂模式的变体时,可以根据具体情况选择简单工厂模式、工厂方法模式或抽象工厂模式。