工厂模式是设计模式中创建型模式的一种大类。我们平常需要一个对象的时候,一般是通过new关键字来创建一个对象,但是在某些情况下,需要创建的对象需要一系列复杂的初始化操作,比如:数据库连接的创建需要配置连接地址、账户、密码等各种配置。这个时候如果我们在每个需要使用的地方都手工去创建显得非常繁琐,也会极大的影响代码的可读性,当对象的实现类需要进行替换时,改造量也非常大。这个时候我们就可以考虑使用工厂模式,封装一个专门用于创建复杂对象的工厂类。
工厂模式包含:简单工厂(不在23种设计模式中)、工厂方法、抽象工厂。这三种模式的功能是从简单到丰富,对应的代码量也是从简单到复杂,我们可以通过一个简单的例子来一一进行理解学习。
首先说一下环境的一个准备,既然是创建型模式,那对应就是要创建出指定的对象,我这里拿键盘进行举例,提前创建好了键盘接口以及对应的实现类,并放在product包下:
然后我们假设product包下的实现类的创建除了new以外还需要其他大量的配置操作,只是我们在代码实现中仅用一行new进行代替。(如果没有这个前提的话,压根不需要使用工厂模式,直接new不好吗)
简单工厂(SimpleFactory)
简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类需要继承自一个父类或接口)。实现起来就是封装一个类,通过ifelse或者switch语句进行不同子类的选择。
public class SimpleFactory {
public Keyboard getKeyboard(String brand) {
if ("lenovo".equals(brand)) {
return new LenovoKeyboard();
} else if ("dell".equals(brand)) {
return new DellKeyboard();
} else {
throw new RuntimeException("不支持该品牌:" + brand);
}
}
}
//联想
keyboard = factory.getKeyboard("lenovo");
keyboard.typing();
//戴尔
keyboard = factory.getKeyboard("dell");
keyboard.typing();
//其他牌子
keyboard = factory.getKeyboard("other");
keyboard.typing();
可以看出简单工厂的实现非常简单,但是他有一个缺点:当我们需要新增一个惠普品牌的键盘时(其他子类对象),需要对SimpleFactory的代码进行改动,这种做法扩展性差,违背了开闭原则,也影响了可读性。所以这种方式只适用于业务较简单,工厂类不会经常更改的情况下使用。
工厂方法(FactoryMethod)
为了弥补简单工厂的不足,我们可以通过对工厂类抽象,并将不同品牌键盘对象的创建放到对应的品牌工厂子类中去,通过子类的形式来改写ifelse。
创建一个抽象的键盘工厂类,并对应不同的品牌,分别实现其子类工厂。
/**
* 键盘工厂
*/
public interface KeyboardFactory {
Keyboard getKeyboard();
}
/**
* 联想键盘工厂
*/
public class LenovoKeyboardFactory implements KeyboardFactory {
@Override
public Keyboard getKeyboard() {
return new LenovoKeyboard();
}
}
/**
* 戴尔键盘工厂
*/
public class DellKeyboardFactory implements KeyboardFactory {
@Override
public Keyboard getKeyboard() {
return new DellKeyboard();
}
}
在使用的时候,只需要替换对应的工厂实现类,即可实现产品端品牌的替换。
//联想
factory = new LenovoKeyboardFactory();
keyboard = factory.getKeyboard();
keyboard.typing();
//戴尔
factory = new DellKeyboardFactory();
keyboard = factory.getKeyboard();
keyboard.typing();
在使用工厂方法之后,每一个产品对象都对应了一个工厂对象,在后续有新产品时,只需要新增对应的新产品工厂即可,具有很高的扩展性。但是如果产品类型越来越多,系统中类的个数也会越来越多,代码复杂度变高。
抽象工厂(AbstractFactory)
为了减少工厂子类的数量,我们可以考虑对产品进行分组,比如说,联想可以生产键盘,也可以生产鼠标;戴尔同样可以。这个时候我们可以通过品牌来分组,总共分成联想工厂和戴尔工厂,通过不同的方法来获取键盘和鼠标。
/**
* 抽象工厂
*/
public interface AbstractFactory {
Keyboard getKeyboard();
Mouse getMouse();
}
/**
* 联想工厂
*/
public class LenovoFactory implements AbstractFactory {
@Override
public Keyboard getKeyboard() {
return new LenovoKeyboard();
}
@Override
public Mouse getMouse() {
return new LenovoMouse();
}
}
/**
* 戴尔工厂
*/
public class DellFactory implements AbstractFactory {
@Override
public Keyboard getKeyboard() {
return new DellKeyboard();
}
@Override
public Mouse getMouse() {
return new DellMouse();
}
}
使用代码
//联想系列
factory = new LenovoFactory();
keyboard = factory.getKeyboard();
mouse = factory.getMouse();
keyboard.typing();
mouse.click();
//戴尔系列
factory = new DellFactory();
keyboard = factory.getKeyboard();
mouse = factory.getMouse();
keyboard.typing();
mouse.click();
我的理解是,抽象工厂其实就是工厂方法的超集,通过对产品进行分组和抽象成不同的方法来减少工厂子类的数量。
总结
工厂方法模式:
1、一个抽象产品类,可以派生出多个具体产品类。
2、一个抽象工厂类,可以派生出多个具体工厂类。
3、每个具体工厂类只能创建一个具体产品类的实例。
抽象工厂模式:
1、多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。
2、一个抽象工厂类,可以派生出多个具体工厂类。
3、每个具体工厂类可以创建多个具体产品类的实例。
区别:
1、工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。
2、工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。
完整测试代码见:https://gitee.com/lqccan/blog-demo/tree/master/DesignPattern/src/main/java/com/example/demo/factory