工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。一般来说,xxxxxAdapter和xxxxx
工厂模式有分为三种:
简单工厂
拿手机为例,手机有很多品牌,比如华为、小米、苹果等。
首先定义一个手机的接口
1 2 3 4
| public interface Phone { void brand(); }
|
再定义两种品牌的手机,分别实现手机的接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class HuaWeiPhone implements Phone { @Override public void brand() { System.out.println("华为手机..."); } }
public class XiaoMiPhone implements Phone { @Override public void brand() { System.out.println("小米手机..."); } }
|
再定义一个专门生产手机的工厂
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class PhoneFactory { public Phone createPhone(String phoneType){ Phone phone = null; switch (phoneType){ case "xm" : phone = new HuaWeiPhone(); break; case "hw" : phone = new XiaoMiPhone(); break; } return phone; } }
|
客户端测试代码
1 2 3 4 5 6 7 8 9
| public class test { public static void main(String[] args) { PhoneFactory factory = new PhoneFactory(); Phone xm = factory.createPhone("xm"); Phone hw = factory.createPhone("hw"); xm.brand(); hw.brand(); } }
|
输出打印
可以看到,当我们给工厂不同的参数的时候,我们就能获得不同品牌的手机,但是有发现了一个问题,如果我们的工厂还想能生成苹果手机,那么我们就必须要改动工厂类的代码逻辑,这就违背了开闭原则。
工厂方法
在上面的简单工厂中,如果要创建的手机类型较多,且各个手机创建的过程不尽相同,则一个工厂类职责会变得越来越多,不符合单一职责原则。另外简单工厂也不符合开闭原则。要新增一种手机需要修改原来的工厂类。因此,工厂方法模式中,将生产各种类型的产品的工厂也做了抽象分离。我们对上面的简单工厂进行改造。
首先创建统一的工厂接口
1 2 3
| public interface PhoneFactory { Phone createPhone(); }
|
再分别创建不同手机品牌的工厂,比如有小米手机工厂,华为手机工厂等等
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class XiaoMiFactory implements PhoneFactory { @Override public Phone createPhone() { return new XiaoMiPhone(); } }
public class HuaWeiFactory implements PhoneFactory { @Override public Phone createPhone() { return new HuaWeiPhone(); } }
|
客户端测试截图
如果这时候需要我们还能生成苹果手机,就比较容易了,我们只需要再添加一个苹果手机工厂类,实现手机工厂就行了,无需再更改工厂类的代码。
增加苹果手机类,实现手机接口
1 2 3 4 5 6
| public class IPhone implements Phone { @Override public void brand() { System.out.println("苹果手机..."); } }
|
增加苹果手机工厂类,实现手机工厂接口
1 2 3 4 5 6
| public class IPhoneFactory implements PhoneFactory { @Override public Phone createPhone() { return new IPhone(); } }
|
客户端测试截图
当我们有其他手机需要生产时,只需要先定义手机类,实现Phone
接口,再定义手机工厂类,实现PhoneFactory
接口,就能实现不用更改其他地方的代码就能生产出不同品牌的手机,完全符合开闭原则。但是还有一个问题,当我们新增一种品牌的手机时需要新增两个类,如果有很多手机品牌时,就会造成有很多类,造成类爆炸,而且当我们需要其他产品的时候,发现没有生产其他产品的工厂,此时我们可以把产品组合起来,一个工厂生产所有的产品,比如小米工厂要能生产手机和路由器。
抽象工厂模式
先定义一个手机产品的顶级接口,有手机的一些功能
1 2 3 4 5 6 7 8 9 10 11 12 13
| public interface Phone { void start();
void shutdown();
void callup(); void sendSMS(); }
|
再定义一个路由器的顶级接口,有路由器的一些功能
1 2 3 4 5 6 7 8 9 10 11 12 13
| public interface Router { void start();
void shutdown();
void openwifi();
void setting(); }
|
再写华为和小米的产品的4个实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class HuaWeiPhone implements Phone{ @Override public void start() { System.out.println("华为手机开机..."); }
@Override public void shutdown() { System.out.println("华为手机关机..."); }
@Override public void callup() { System.out.println("华为手机打电话..."); }
@Override public void sendSMS() { System.out.println("华为手机发送短信..."); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class HuWeiRouter implements Router{ @Override public void start() { System.out.println("华为路由器开机..."); }
@Override public void shutdown() { System.out.println("华为路由器关机..."); }
@Override public void openwifi() { System.out.println("华为路由器打开wifi..."); }
@Override public void setting() { System.out.println("华为路由器设置..."); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class XiaoMiPhone implements Phone{ @Override public void start() { System.out.println("小米手机开机..."); }
@Override public void shutdown() { System.out.println("小米手机关机..."); }
@Override public void callup() { System.out.println("小米手机打电话..."); }
@Override public void sendSMS() { System.out.println("小米手机发送短信..."); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class XiaoMiRouter implements Router{ @Override public void start() { System.out.println("小米路由器开机..."); }
@Override public void shutdown() { System.out.println("小米路由器关机..."); }
@Override public void openwifi() { System.out.println("小米路由器打开wifi..."); }
@Override public void setting() { System.out.println("小米路由器设置..."); } }
|
再定义一个顶级的产品接口,能生产手机和路由器。
1 2 3 4 5 6 7 8 9
| public interface ProductFactory {
Phone phoneProduct();
Router routerProduct();
}
|
再写两个华为工厂和小米工厂的实现类,各自都实现产品接口。
1 2 3 4 5 6 7 8 9 10 11
| public class HuaWeiFactory implements ProductFactory{ @Override public Phone phoneProduct() { return new HuaWeiPhone(); }
@Override public Router routerProduct() { return new HuWeiRouter(); } }
|
1 2 3 4 5 6 7 8 9 10 11
| public class XiaoMiFactory implements ProductFactory{ @Override public Phone phoneProduct() { return new XiaoMiPhone(); }
@Override public Router routerProduct() { return new XiaoMiRouter(); } }
|
客户端,通过 ProductFactory
创建各自的工厂,通过工厂拿到对应的产品
看一下类图
如果这时候还想要增加产品等级机会很简单了,比如想新增一个苹果产品。我们只需要新增一个类IPhoneFactory
实现ProductFactory
,苹果工厂就能生产手机和路由器了,要想生产具体的苹果手机和苹果路由器还得新增一个IPhone
手机类,和IRouter
路由器类,分别实现Phone
接口和Router
接口,就可以生产具体的苹果手机和苹果路由器了。下面来看一下新增苹果产品后的类图:
我们发现新增一个产品等级还是很容易的,但是新增一个产品族就会很难,比如我现在想要所有工程都能生产笔记本电脑,那么就会改动到ProductFactory
接口的代码,因为这个接口现在定义了只生产手机和路由器,不能生产笔记本,相应的实现了HuaWeiFactory
、XiaoMiFactory、IPhoneFactory也会改动,这又违背了开闭原则。所以说抽象工厂方法也会有优缺点。
优点:
- 一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象(将一个系列的产品统一一起创建)。
缺点:
- 产品族扩展非常困难,要增加一个系列的某一产品,既要修改工厂抽象类里加代码,又修改具体的实现类里面加代码。
- 增加了系统的抽象性和理解难度。