三种工厂模式

工厂模式

提供一个用于创建对象的接口(工厂接口),让其实现类(工厂实现类)决定实例化哪一个类(产品类),并且由该实现类创建对应类的实例。

工厂方法使一个类的实例化延迟到其子类。如图所示,Product抽象类负责定义产品的共性,实现对事物最抽象的定义,Creator为抽象工厂类,具体如何创建产品类由具体的实现工厂ConcreteCreator来完成。

通用模板代码

1
2
3
4
5
6
7
8
public abstract class Product {

public void method() { //产品类的公共方法,已经实现
//实现了公共的逻辑
}

public abstract void method2(); //非公共方法,需要子类具体实现
}

具体产品类可以有多个,都继承与抽象类Product,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ConcreateProduct1 extends Product {

@Override
public void method2() {
//product1的业务逻辑
}
}
public class ConcreateProduct2 extends Product {

@Override
public void method2() {
//product2的业务逻辑
}
}

抽象工厂类负责定义产品对象的产生,代码如下:

1
2
3
4
public abstract class Creator {
//创建一个产品对象,其输入参数类型可以自行设置
public abstract <T extends Product> T createProduct(Class<T> clazz);
}

这里用的是泛型,传入的对象必须是Product抽象类的实现类。具体如何产生一个产品的对象,是由具体工厂类实现的,具体工厂类继承这个抽象工厂类:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ConcreteCreator extends Creator {

@Override
public <T extends Product> T createProduct(Class<T> clazz) {
Product product = null;
try {
product = (Product) Class.forName(clazz.getName()).newInstance();
} catch (Exception e) { //异常处理
e.printStackTrace();
}
return (T) product;
}
}

通过这样的设计,我们就可以在测试类中随意生产产品了,看下面的测试类:

1
2
3
4
5
6
7
8
9
10
11
public class FactoryTest {

public static void main(String[] args) {
Creator factory = new ConcreteCreator();
Product product1 = factory.createProduct(ConcreteProduct1.class); //通过不同的类创建不同的产品
Product product2 = factory.createProduct(ConcreteProduct2.class);
/*
* 下面继续其他业务处理
*/
}
}

多个工厂模式

每个具体的工厂都已经非常明确自己的职责:创建自己负责的产品类对象

1
2
3
public abstract class Creator {
public abstract Product createProduct();
}

注意抽象方法中已经不需要再传递相关类的参数了,因为每个具体的工厂都已经非常明确自己的职责:创建自己负责的产品类对象。所以不同的工厂实现自己的createProduct方法即可:

1
2
3
4
5
6
7
8
9
10
public class Concrete1Creator extends Creator {
public Product createProduct() {
return new ConcreteProduct1();
}
}
public class Concrete2Creator extends Creator {
public Product createProduct() {
return new ConcreteProduct2();
}
}

这样多个不同的工厂就产生了,每个工厂对应只生产自己对应的产品,分工协作,各不影响了!

1
2
3
4
5
6
public class FactoryTest {
public static void main(String[] args) {
Human blackMan = new ConcreteCreator1().createProduct();
Human yellowMan = new ConcreteCreator2().createProduct();
}
}

这种工厂模式的好处是职责清晰,结构简单,但是给扩扩展性和可维护性带来了一定的影响,因为如果要扩展一个产品类,就需要建立一个相应的工厂类,这样就增加了扩展的难度。因为工厂类和产品类的数量是相同的,维护时也需要考虑两个对象之间的关系。但是这种模式还是很常用的。

替代单例模式

可以使用静态工厂加反射实现单例模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class SingletonFactory {
private static Singleton instance;
static {
try {
Class clazz = Class.forName(Singleton.class.getName());
//获取无参构造方法
Constructor constructor = clazz.getDeclaredConstructor();
//设置无参构造方法可访问
constructor.setAccessible(true);
//产生一个实例对象
instance = (Singleton) constructor.newInstance();
} catch (Exception e) {
//异常处理
}
}
public static Singleton getInstance() {
return instance;
}
}

优点

1.工厂模式具有良好的封装性,代码结构清晰,也有利于扩展。在增加产品类的情况下,只需要适当地修改具体的工厂类或扩展一个工厂类,就可以完成“拥抱变化”。
2.工厂模式可以屏蔽产品类。这一点非常重要,产品类的实现如何变化,调用者都不用关系,只需要关心产品的接口,只要接口保持不变,系统的上层模块就不需要发生变化。
3.工厂模式是典型的解耦框架。高层模块只需要知道产品的抽象类,其他的实现类都不用关心。

简单/静态工厂模式

如果只需要一个工厂就可以把Product生产出来,我干嘛要具体的工厂对象呢?只要使用静态方法就好了。这样一想,把Creator抽象类去掉了,只保留了ConcreteCreator类,同时把method2方法设置成了static类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ConcreteCreator {

@Override
public static <T extends Product> T createProduct(Class<T> clazz) {
Product product = null;
try {
product = (Product) Class.forName(clazz.getName()).newInstance();
} catch (Exception e) { //异常处理
e.printStackTrace();
}
return (T) product;
}
}

在实际项目中,根据需求可以设置成静态工厂类,但是缺点是扩展比较困难。如果就一个工厂,不需要扩展,可以这么设计,仍然是很常用的。

抽象工厂模式

为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。

抽象工厂模式与工厂方法模式的区别

抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。他与工厂方法模式的区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的是多个产品等级结构。在编程中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类

在抽象工厂模式中,有一个产品族的概念:所谓的产品族,是指位于不同产品等级结构中功能相关联的产品组成的家族。抽象工厂模式所提供的一系列产品就组成一个产品族;而工厂方法提供的一系列产品称为一个等级结构。

优缺点

优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。此外还具有工厂方法模式的优点。

缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。

适用场景:
当需要创建的对象是一系列相互关联或相互依赖的产品族时,便可以使用抽象工厂模式。说的更明白一点,就是一个继承体系中,如果存在着多个等级结构(即存在着多个抽象类),并且分属各个等级结构中的实现类之间存在着一定的关联或者约束,就可以使用抽象工厂模式。假如各个等级结构中的实现类之间不存在关联或约束,则使用多个独立的工厂来对产品进行创建,则更合适一点。

示例

cpu接口和实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface Cpu {
void run();

class Cpu650 implements Cpu {
@Override
public void run() {
//625 也厉害
}
}

class Cpu825 implements Cpu {
@Override
public void run() {
//825 处理更强劲
}
}
}

屏幕接口和实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface Screen {

void size();

class Screen5 implements Screen {

@Override
public void size() {
//5寸
}
}

class Screen6 implements Screen {

@Override
public void size() {
//6寸
}
}
}

工厂接口

1
2
3
4
5
6
public interface PhoneFactory {

Cpu getCpu();//使用的cpu

Screen getScreen();//使用的屏幕
}

具体工厂实现类:小米手机工厂

1
2
3
4
5
6
7
8
9
10
11
public class XiaoMiFactory implements PhoneFactory {
@Override
public Cpu getCpu() {
return new Cpu.Cpu825();//高性能处理器
}

@Override
public Screen getScreen() {
return new Screen.Screen6();//6寸大屏
}
}

具体工厂实现类:红米手机工厂

1
2
3
4
5
6
7
8
9
10
11
12
public class HongMiFactory implements PhoneFactory {

@Override
public Cpu getCpu() {
return new Cpu.Cpu650();//高效处理器
}

@Override
public Screen getScreen() {
return new Screen.Screen5();//小屏手机
}
}

以上例子可以看出,抽象工厂可以解决一系列的产品生产的需求,对于大批量,多系列的产品,用抽象工厂可以更好的管理和扩展。

总结

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

所以,在使用工厂模式时,只需要关心降低耦合度的目的是否达到了。

参考资料

https://blog.csdn.net/eson_15/article/details/51223124
https://www.jianshu.com/p/38493eb4ffbd