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

Posted 朱小厮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式:工厂方法模式(Factory Method)和抽象工厂模式(Abstact Factory)相关的知识,希望对你有一定的参考价值。


欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。


欢迎跳转到本文的原文链接:https://honeypps.com/design_pattern/factory/

 在面向对象编程中, 最通常的方法是一个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里的设计模式

欢迎跳转到本文的原文链接:https://honeypps.com/design_pattern/factory/

欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。


以上是关于设计模式:工厂方法模式(Factory Method)和抽象工厂模式(Abstact Factory)的主要内容,如果未能解决你的问题,请参考以下文章

设计模式——工厂方法模式(Factory Method)

设计模式工厂方法模式(Factory Method)

工厂方法模式 (Factory Method)

工厂模式--抽象工厂模式(Abstract Factory)

工厂模式(Factory)

工厂方法模式(Factory Method)