上一篇文章设计模式初探-创建型模式之单例模式
中,我们引入了设计模式相关话题,开启了设计模式的新专题。篇幅所限,上一篇文章仅仅介绍了创建型模式中的单例模式。这篇文章,我们将继续介绍创建型模式之工厂模式。本文分为如下几个部分:
- 前言
- 工厂模式
2.1 为什么要有工厂模式
2.2 工厂模式分类 - 工厂模式的实现
3.1 简单工厂
3.1.1 简单工厂的定义
3.1.2 新增产品
3.2 工厂方法
3.2.1 工厂方法的定义
3.2.2 新增产品
3.3 抽象工厂
3.3.1 抽象工厂定义
3.3.2 新增产品 - 总结
1. 前言
再说工厂模式先,先思考两个问题:
日常coding中使用过哪些设计模式?
除了工厂和单例还使用过哪些设计模式?
工厂模式的分类及区别?
下面让我们带着问题开始工厂模式的探讨
2. 工厂模式
2.1 为什么要有工厂模式
任何事物的出现必然是因为有相应的需求。那为什么要有工厂模式呢?
先看下这样的代码
public void test(String type){
Mouse mouse=null;
if ("DELL".equalsIgnoreCase(type)){
mouse=new DellMouse();
}else if ("MICROSOFT".equalsIgnoreCase(type)){
mouse=new MicrosoftMouse();
}else if ("RAZER".equalsIgnoreCase(type)){
mouse=new RazerMouse();
}else {
throw new IllegalArgumentException("no such type !");
}
mouse.click();
}
public interface Mouse {
void click();
}
public class DellMouse implements Mouse {
@Override
public void click() {
System.out.println("DellMouse click");
}
}
public class MicrosoftMouse implements Mouse {
@Override
public void click() {
System.out.println("MicrosoftMouse click");
}
}
public class RazerMouse implements Mouse{
@Override
public void click() {
System.out.println("RazerMouse click");
}
}
当然这段代码,跑起来没有任何问题。但是这段代码有个隐藏问题,如果将来我们要新增一种鼠标类型的化,那么我们就需要修改test方法,在其中的if elseif 中加一段新的逻辑控制,而且mouse的创建和mouse的使用耦合在一起,很容易改错。那么工厂模式的作用也显而易见了。
将对象的创建单独拆分,可以避免代码的重复,便于后期维护。
将对象的创建和对象的使用拆分,使其符合开闭原则。
-
将对象的创建和对象的使用拆分,降低系统间的耦合度。
值得注意的是:无论工厂模式如何设计,最终的对象创建还是通过new关键字来创建的。工厂模式只是提供了一种设计模板,用来保护对象的创建,使其便于维护和已读。
2.2 工厂模式分类
类型 | 定义 | 是否符合开闭原则 | 是否方便新增子产品 | 是否方便新增产品家族 |
---|---|---|---|---|
简单工厂 | 专门定义一个工厂类来创建其它类的实例(只是简单的将创建对象的相关代码抽离到单独的一个类中) |
否 | 否 | 否 |
工厂方法 | 定义了一个创建对象的接口,由子类决定实例化的是哪个类,工厂方法将子类的实例化推迟到子类中 |
是 | 是 | 否 |
抽象工厂 | 定义了一个接口,用于创建相关对象和依赖的家族,而不需要指明具体类 |
是 | 是 | 是 |
3. 工厂模式的实现
上面我们介绍了工厂模式的分类
- 简单工厂
- 工厂方法
- 抽象工厂
下面我们来看下具体实现
3.1 简单工厂
3.1.1 简单工厂的定义
简单工厂,即单纯的将对象的创建提取到一个类中,严格意义上来说不属于23种设计模式的一种。先上类图
代码实现如下
public class SimpleMouseFactory {
public static Mouse createMouse(String type){
Mouse mouse=null;
if ("DELL".equalsIgnoreCase(type)){
mouse=new DellMouse();
}else if ("MICROSOFT".equalsIgnoreCase(type)){
mouse=new MicrosoftMouse();
}else if ("RAZER".equalsIgnoreCase(type)){
mouse=new RazerMouse();
}else {
throw new IllegalArgumentException("no such type !");
}
return mouse;
}
}
public void test(String type){
Mouse mouse=SimpleMouseFactory.createMouse(type);
mouse.click();
}
public static void main(String[] args) {
Test test=new Test();
test.test("dell");
}
上面的例子中,我们将创建Mouse的过程单独提取到类SimpleMouseFactory中,并提供一个静态方法createMouse来创建对象。这样我们就实现了简单工厂模式
,做到了对象的创建和对象的使用解耦。
3.1.2 新增产品
在上面的例子中,假设我们要新增一种华硕鼠标,那我们要怎么做呢?
- 新增新的实现,AsusMouse
- 修改工厂类SimpleMouseFactory.createMouse,加入创建AsusMouse的逻辑
public class SimpleMouseFactory {
public static Mouse createMouse(String type){
Mouse mouse=null;
if ("DELL".equalsIgnoreCase(type)){
mouse=new DellMouse();
}else if ("MICROSOFT".equalsIgnoreCase(type)){
mouse=new MicrosoftMouse();
}else if ("RAZER".equalsIgnoreCase(type)){
mouse=new RazerMouse();
}else if ("ASUS".equalsIgnoreCase(type)){
mouse=new AsusMouse();
}else {
throw new IllegalArgumentException("no such type !");
}
return mouse;
}
}
这么做显然违反了一个重要的设计原则开闭原则:程序应该对扩展开放,对修改关闭。
怎样才能符合开闭原则呢?工厂方法和抽象工厂应运而生。
3.2 工厂方法
3.2.1 工厂方法的定义
工厂方法,定义了一个创建对象的接口,由子类决定实例化的是哪个类,工厂方法将实例化推迟到子类中。
先上类图:
代码实现如下
- 新增MouseFactory 接口
public interface MouseFactory {
Mouse createMouse();
}
- 新增实现
public class AsusMouseFactory implements MouseFactory {
@Override
public Mouse createMouse() {
return new AsusMouse();
}
}
public class DellMouseFactory implements MouseFactory{
@Override
public Mouse createMouse() {
return new DellMouse();
}
}
public class MicrosoftMouseFactory implements MouseFactory {
@Override
public Mouse createMouse() {
return new MicrosoftMouse();
}
}
public class RazerMouseFactory implements MouseFactory {
@Override
public Mouse createMouse() {
return new RazerMouse();
}
}
- 调用的时候传入不同的MouseFactory
public void test(MouseFactory mouseFactory){
Mouse mouse=mouseFactory.createMouse();
mouse.click();
}
public static void main(String[] args) {
Test test=new Test();
test.test(new AsusMouseFactory());
}
我们可以看到,子类决定了实例化的是哪个产品。这里的决定,并不是由程序动态的指定,而是在编码的时候由程序员决定使用的是哪个具体工厂类,决定了具体工厂类自然决定了实例化哪个子类。
3.2.2 新增产品
- 新增一种新的鼠标-LenovoMouse只需要做如下步骤
- 新增LenovoMouse
public class LenovoMouse implements Mouse {
@Override
public void click() {
System.out.println("LenovoMouse click");
}
}
- 新增LenovoMouseFactory
public class LenovoMouseFactory implements MouseFactory {
@Override
public Mouse createMouse() {
return new LenovoMouse();
}
}
- 调用时传入不同的MouseFactory
public void test(MouseFactory mouseFactory){
Mouse mouse=mouseFactory.createMouse();
mouse.click();
}
public static void main(String[] args) {
Test test=new Test();
test.test(new LenovoMouseFactory());
}
可以看到,新增一个新的鼠标种类时,只需新增新的工厂类和新的鼠标实现即可。无需对原有的工厂和实现做任何改动,因此符合开闭原则
- 新增产品大类-KeyBoard
那么当我们要在工厂中添加一种新的产品大类键盘-keyboard时,我们应该怎么做呢?
- 添加新的键盘抽象及实现
- 修改原有的工厂抽象及实现
- 修改原有调用方法
可以看到工厂方法在新增新的产品子类时无需改动原有工厂方法,只需新增新的工厂,符合开闭原则。但是在新增新的产品大类时,需要改动原有的工厂实现,不符合开闭原则。
那么当我们需要新增产品大类的时候怎么办呢?抽象工厂应运而生。
3.3 抽象工厂
3.3.1 抽象工厂定义
所谓抽象工厂即定义了一个接口,来创建相关依赖的家族,而不需要指明具体的类。
抽象工厂允许客户使用抽象的接口来创建一组相关产品,而不需要知道(或关心)实际产出的具体产品是什么,将客户从具体的产品中解耦。
类图如下:
代码实现如下:
- 新增抽象PCFactory-提供一个接口用于创建依赖家族(createMouse/createKeyboard)
public interface PCFactory {
Mouse createMouse();
Keyboard createKeyboard();
}
- 新增抽象Keyboard及实现
public interface Keyboard {
void pressEnter();
}
public class AsusKeyboard implements Keyboard {
@Override
public void pressEnter() {
System.out.println("AsusKeyboard pressEnter");
}
}
- 新增工厂实现
public class AsusPcFactory implements PCFactory{
@Override
public Mouse createMouse() {
return new AsusMouse();
}
@Override
public Keyboard createKeyboard() {
return new AsusKeyboard();
}
}
- 调用的时候传入抽象工厂
public void test(PCFactory pcFactory){
Mouse mouse=pcFactory.createMouse();
Keyboard keyboard=pcFactory.createKeyboard();
mouse.click();
keyboard.pressEnter();
}
public static void main(String[] args) {
Test test=new Test();
test.test(new AsusPcFactory());
}
可以看到,与工厂方法相比,我们做了更进一步的抽象,抽象出PCfatory,Pcfactory提供接口来创建产品依赖的家族,这里指的是既能createMouse,又能createKeyboard。这样就避免了在工厂方法模式中,新增keyborad时需要修改原有工厂的尴尬,只需要新增新的PCFactory实现即可。
3.3.2 新增产品
- 现在要新增RazerKeyboard和RazerMouse的产线。要怎么做呢
- 新增RazerKeyboard的实现
public class RazerKeyboard implements Keyboard {
@Override
public void pressEnter() {
System.out.println("RazerKeyboard pressEnter");
}
}
- 新增RazerPCFactory的实现
public class RazerPCFactory implements PCFactory {
@Override
public Mouse createMouse() {
return new RazerMouse();
}
@Override
public Keyboard createKeyboard() {
return new RazerKeyboard();
}
}
- 调用时传入RazerPCFactory
public void test(PCFactory pcFactory){
Mouse mouse=pcFactory.createMouse();
Keyboard keyboard=pcFactory.createKeyboard();
mouse.click();
keyboard.pressEnter();
}
public static void main(String[] args) {
Test test=new Test();
test.test(new RazerPCFactory());
}
- 新增新的产品类型-显示器monitor
- 抽象Monitor
- 修改Pcfactory及其实现
通过上面的例子,我们可以得到如下结论
抽象工厂模式,可以很方便的新增产品家族(这里只需要新增新的PCFactory实现接口,无需修改原有的工厂),新增产品家族时符合开闭原则。
抽象工厂模式针对是多组产品,强调的是依赖的家族(这里的Mouse和Keybord),而工厂方法模式针对的是单组产品。
抽象工厂模式,同样无法很方便的新增新的产品种类(比如这里的monitor)。
- 总结
这里,我们探讨了工厂模式的三种实现,并对每种模式的优缺点进行介绍。
由于技术水平所限,文章难免有不足之处,欢迎大家指出。希望每位读者都能有新的收获,我们下一篇文章-设计模式初探-创建型模式之建造者模式再见.....
参考文章
head first 设计模式