设计模式:工厂方法模式(Factory Method)和抽象工厂模式(Abstact Factory)

简介:  在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的。但是在一些情况下, new操作符直接生成对象会带来一些问题。举例来说, 许多类型对象的创造需要一系列的步骤: 你可能需要计算或取得对象的初始设置; 选择生成哪个子对象实例; 或在生成你需要的对象之前必须先生成一些辅助功能的对象。

 在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的。但是在一些情况下, new操作符直接生成对象会带来一些问题。举例来说, 许多类型对象的创造需要一系列的步骤: 你可能需要计算或取得对象的初始设置; 选择生成哪个子对象实例; 或在生成你需要的对象之前必须先生成一些辅助功能的对象。 在这些情况,新对象的建立就是一个 “过程”,不仅是一个操作,像一部大机器中的一个齿轮传动。


分类

 工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
 工厂模式可以分为三类:

  1. 简单工厂模式(Simple Factory)
  2. 工厂方法模式(Factory Method)
  3. 抽象工厂模式(Abstract Factory)

 这三种模式从上到下逐步抽象,并且更具一般性。
 GOF在《设计模式》一书中将工厂模式分为两类:工厂方法模式(Factory Method)与抽象工厂模式(Abstract Factory)。将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。


简单工厂模式

 简单工模式时类的创建模式,又叫做静态工厂方法(static Factory Method)。简单工厂模式是一个工厂对象决定创建出哪一种产品类的实例。它存在的目的很简单:定义一个创建对象的接口。
 组成:

  1. 工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。在java中它往往由一个具体类实现。
  2. 抽象产品角色:它一般是具体产品继承的父类或者实现的接口。在java中由接口或者抽象类来实现。
  3. 具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现。

 举个简单例子:
1 抽象产品角色

public interface IProduct
{
    public void method();
}

2 具体产品角色(可以把方法定义为static的)

public class ProductA implements IProduct
{
    @Override
    public void method()
    {
        System.out.println("I'm ProductA!");
    }
}

public class ProductB implements IProduct
{
    @Override
    public void method()
    {
        System.out.println("I'm ProductB!");
    }
}

3 工厂类角色

public class SimpleFactory
{
    public IProduct produce(String type)
    {
        if("A".equals(type))
        {
            return new ProductA();
        }
        else if("B".equals(type))
        {
            return new ProductB();
        }
        else
        {
            System.out.println("请输入正确的类型");
            return null;
        }
    }
}

4 测试类

public class MainTest
{
    public static void main(String args[])
    {
        SimpleFactory factory = new SimpleFactory();
        IProduct product = factory.produce("A");
        product.method();
    }
}

输出:I’m ProductA!

 在Java中java.text.DateFormat就是简单工厂模式的典型案例。
优点:专门定义一个工厂类负责创建其他类的实例,最大的优点在于工厂类中包含了必要的逻辑,根据客户需要的条件动态实例化相关的类。
缺点:当需要增加一种产品时,比如ProductC就需要修改简单工厂类SimpleFactory(增加if-else块),这违背了开闭原则。

TIPS
 其实如果采用反射机制实现简单工厂并没有违背开闭原则。
 利用反射机制,将简单工厂类改成:

public class SimpleFactory
{
    public IProduct produce(Class<? extends IProduct> c) throws Exception
    {
        return (IProduct)Class.forName(c.getName()).newInstance();
//        return (IProduct)c.newInstance();        //或者采用这种方法
    }
}

 测试类:

public class MainTest
{
    public static void main(String args[]) throws Exception
    {
        SimpleFactory factory = new SimpleFactory();
        IProduct product = factory.produce(ProductA.class);
        product.method();
    }
}

 这样当有新的产品时,其实并不需要修改工厂类。《Effective Java(Second Edition)》中明确指出:通常,普通应用程序在运行时不应该以反射方式访问对象。所以本篇文章建立在不采用反射机制的情况下,在下面介绍的工厂方法模式其实也可以改用反射机制实现,博主就不展示了。至于《Effective Java(Second Edition)》为什么不推荐反射机制,可以参考此书的“接口优先于反射机制”这一主题,这里不赘述。


工厂方法模式

 工厂方法模式是简单工厂模式的进一步抽象化和推广,工厂方法模式里不再只由一个工厂类决定那一个产品类应当被实例化,这个决定被交给抽象工厂的子类去做。
这里写图片描述
 来看下它的组成:
1. 抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
2. 具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。
3. 抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。
4. 具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。
 举个简单例子:
1 抽象工厂角色

public interface IFactory
{
    public IProduct produce();
}

2 具体工厂角色

public class ConcreteFactoryA implements IFactory
{
    @Override
    public IProduct produce()
    {
        return new ProductA();
    }
}

public class ConcreteFactoryB implements IFactory
{
    @Override
    public IProduct produce()
    {
        return new ProductB();
    }
}

3 抽象产品角色(和简单工厂的一样)

public interface IProduct
{
    public void method();
}

4 具体产品角色(和简单工厂的一样)

public class ProductA implements IProduct
{
    @Override
    public void method()
    {
        System.out.println("I'm ProductA!");
    }
}

public class ProductB implements IProduct
{
    @Override
    public void method()
    {
        System.out.println("I'm ProductB!");
    }
}

5 测试代码:

public class MainTest
{
    public static void main(String[] args)
    {
        IFactory factoryA = new ConcreteFactoryA();
        IProduct product1 = factoryA.produce();
        product1.method();
    }
}

输出:I’m ProductA!

工厂方法模式和简单工厂模式在定义上的不同是很明显的。工厂方法模式的核心是一个抽象工厂类,而不像简单工厂模式, 把核心放在一个实类上。工厂方法模式可以允许很多实的工厂类从抽象工厂类继承下来, 从而可以在实际上成为多个简单工厂模式的综合,从而推广了简单工厂模式。
工厂方法相比于简单工厂模式的优点是增加一个产品,只需要增加一个具体工厂类和具体产品类,没有修改原先的工厂类,符合开闭原则。缺点是客户端的代码会需要修改(简单工厂模式的客户端不需要修改),随着产品的继续增加,所要实现的类的个数也会随之增大。


抽象工厂模式

 在抽象工厂模式中,抽象产品 (AbstractProduct) 可能是一个或多个,从而构成一个或多个产品族(Product Family)。 在只有一个产品族的情况下,抽象工厂模式实际上退化到工厂方法模式。
这里写图片描述

工厂方法模式 VS 抽象工厂模式
工厂方法模式:一个抽象产品类,可以派生出多个具体产品类。每个具体工厂类只能创建一个具体产品类的实例。
抽象工厂模式:多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。一个抽象工厂类可以派生出多个具体工厂类。每个具体工厂类可以创建多个具体产品的实例。
区别:工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。

举个简单例子:
1 抽象产品角色

public interface AbstractProductA
{
    public void produceA();
}
public interface AbstractProductB
{
    public void produceB();
}

2 抽象工厂角色

public interface AbstractFactory
{
    public AbstractProductA CreateProductA();
    public AbstractProductB CreateProductB();
}

3 具体产品角色

public class ProductA1 implements AbstractProductA
{
    @Override
    public void produceA()
    {
        System.out.println("Im ProductA1!");
    }
}
public class ProductA2 implements AbstractProductA
{
    @Override
    public void produceA()
    {
        System.out.println("Im ProductA2!");
    }
}
public class ProductB1 implements AbstractProductB
{
    @Override
    public void produceB()
    {
        System.out.println("Im ProductB1!");
    }
}
public class ProductB2 implements AbstractProductB
{
    @Override
    public void produceB()
    {
        System.out.println("Im ProductB2!");
    }
}

4 具体工厂角色

public class ConcreteFactory1 implements AbstractFactory
{
    @Override
    public AbstractProductA CreateProductA()
    {
        return new ProductA1();
    }

    @Override
    public AbstractProductB CreateProductB()
    {
        return new ProductB1();
    }
}
public class ConcreteFactory2 implements AbstractFactory
{
    @Override
    public AbstractProductA CreateProductA()
    {
        return new ProductA2();
    }

    @Override
    public AbstractProductB CreateProductB()
    {
        return new ProductB2();
    }
}

5 测试代码

public class MainTest
{
    public static void main(String[] args)
    {
        AbstractFactory factory = new ConcreteFactory1();
        AbstractProductA product1 = factory.CreateProductA();
        AbstractProductB product2 = factory.CreateProductB();
        product1.produceA();
        product2.produceB();
    }
}

输出结果:

Im ProductA1!
Im ProductB1!

 抽象工厂的优点:抽象工厂模式除了具有工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束。所谓的产品族,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描述,而不必专门引入一个新的类来进行管理。
 抽象工厂的缺点:产品族的扩展将是一件十分费力的事情,假如产品族中需要增加一个新的产品,则几乎所有的工厂类都需要进行修改。所以使用抽象工厂模式时,对产品等级结构的划分是非常重要的。

Jdk中的工厂方法
java.lang.Object#toString();
java.lang.Class#newInstance();
java.lang.Class#forName();
java.lang.Boolean#valueOf();
java.lang.Proxy#newProxyInstance();
java.lang.reflect.Array#newInstance();
java.lang.reflect.Constructor#newInstance();
java.util.concurrent.Executors#newCachedThreadPool()等


总结

 无论是简单工厂模式,工厂方法模式,还是抽象工厂模式,他们都属于工厂模式,在形式和特点上也是极为相似的,他们的最终目的都是为了解耦。在使用时,我们不必去在意这个模式到底工厂方法模式还是抽象工厂模式,因为他们之间的演变常常是令人琢磨不透的。经常你会发现,明明使用的工厂方法模式,当新需求来临,稍加修改,加入了一个新方法后,由于类中的产品构成了不同等级结构中的产品族,它就变成抽象工厂模式了;而对于抽象工厂模式,当减少一个方法使的提供的产品不再构成产品族之后,它就演变成了工厂方法模式。所以,在使用工厂模式时,只需要关心降低耦合度的目的是否达到了。


参考资料:
1. 《设计模式(一)工厂模式Factory(创建型)
2. 《学习:java设计模式—工厂模式
3. 《23种设计模式(3):抽象工厂模式
4. 《Effective Java(Second Edition)》Joshua Bloch.
5. 《细数JDK里的设计模式

目录
相关文章
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
1月前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
|
1月前
|
设计模式 安全 Java
Kotlin - 改良设计模式 - 构建者模式
Kotlin - 改良设计模式 - 构建者模式
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
42 1
|
3月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
2月前
|
设计模式 Java Kotlin
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
本教程详细讲解Kotlin语法,适合希望深入了解Kotlin的开发者。对于快速学习Kotlin语法,推荐查看“简洁”系列教程。本文重点介绍了构建者模式在Kotlin中的应用与改良,包括如何使用具名可选参数简化复杂对象的创建过程,以及如何在初始化代码块中对参数进行约束和校验。
32 3
|
3月前
|
设计模式 算法 安全
设计模式——模板模式
模板方法模式、钩子方法、Spring源码AbstractApplicationContext类用到的模板方法
设计模式——模板模式
|
3月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:如何提高代码的可维护性与扩展性在软件开发领域,PHP 是一种广泛使用的服务器端脚本语言。随着项目规模的扩大和复杂性的增加,保持代码的可维护性和可扩展性变得越来越重要。本文将探讨 PHP 中的设计模式,并通过实例展示如何应用这些模式来提高代码质量。
设计模式是经过验证的解决软件设计问题的方法。它们不是具体的代码,而是一种编码和设计经验的总结。在PHP开发中,合理地使用设计模式可以显著提高代码的可维护性、复用性和扩展性。本文将介绍几种常见的设计模式,包括单例模式、工厂模式和观察者模式,并通过具体的例子展示如何在PHP项目中应用这些模式。
|
3月前
|
设计模式 Java Spring
spring源码设计模式分析-代理设计模式(二)
spring源码设计模式分析-代理设计模式(二)
|
2月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
43 0