什么是工厂模式?
定义: 工厂模式又称为创建模式,它是建对象的一种最佳方式。工厂模式的本质就是用工厂方法代替new操作创建一种实例化对象的方式。一句话中总结就是方便创建 同种类型接口产品 的 复杂对象。
核心:
- 实现了创建者和调用者分离
- 实例对象不用new,而用工厂方法代替
- 将选择实现类,创建对象统一管理和控制。从而将调用者和我们的实现类解耦
三种模式:
- 简单工厂模式:用来生产同一等级结构的任意商品(对于增加新的商品,需要修改已有代码)
- 工厂方法模式:用来生产同一等级结构中固定商品(支持增加任意产品)
- 抽象工厂模式:围绕一个超级工厂创建其他工厂,该超级工厂又城其他工厂的工厂。
应用实例: 1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。 2、Hibernate 换数据库只需换方言和驱动就可以。
优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点: 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
使用场景: 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,“POP3”、“IMAP”、“HTTP”,可以把这三个作为产品类,共同实现一个接口。
注意事项: 作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
简单工厂模式
在介绍简单工程模式前,我们现需要回顾一下OOP的七大原则,因为工厂模式也是需要符合这七大原则的。
OOP七大原则:
- 开闭原则:一个软件的实体应该对扩展开放,对修改关闭
- 依赖倒转原则:要针对接口编程,不要针对实现编程
- 迪米特法则:直与你直接的朋友通信,避免和陌生人通信
然后我们写一个车的接口
public interface Car {
void Name();
}
再写两个车的实现类
public class Wuling implements Car{
@Override
public void Name() {
System.out.println("五菱宏光!");
}
}
public class TesLa implements Car{
@Override
public void Name() {
System.out.println("特斯拉!");
}
}
再过去我们想要得到Wuling和Tesla的对象,最常用的方法就是直接new。
//消费者(测试类)
public class Consumer {
public static void main(String[] args) {
Car car1=new Wuling();
Car car2=new TesLa();
car1.Name();car2.Name();
}
}
但是上面这种操作,我们需要知道接口和所有的实现类才能得到对象。就好像你想要一台车,然后你自己造(new)了一辆车,但是现实生活中,如果你想要的一台车,你会怎么做?去工厂买,你不需要知道工厂怎么造车。
工厂模式的核心就是实例对象不用new,而用工厂方法代替,我们用代码实现一个车工厂
public class CarFactory {
public static Car getCar(String car){
if (car.equals("五菱宏光"))
return new Wuling();
else if (car.equals("特斯拉"))
return new TesLa();
else
return null;
}
}
这样我们的消费者需要车,直接去工厂买就可以了,不需要自己造了。需要什么车(Car),直接给车工厂(CarFactory)传一个字符串参数就可以了,不需要知道里面的细节,甚至不需要知道实现类(符合OOP七大原则之依赖倒转原则:要针对接口编程,不要针对实现编程)。
//用工厂实现
Car car1 = CarFactory.getCar("五菱宏光");
Car car2 = CarFactory.getCar("特斯拉");
问题来了,如果我们需要增加一个实现类呢?
public class DaZhong implements Car{
@Override
public void Name() {
System.out.println("大众!");
}
}
我们需要修改我们的车工厂(CarFactory),在里面加一个判断
public class CarFactory {
public static Car getCar(String car){
if (car.equals("五菱宏光"))
return new Wuling();
else if (car.equals("特斯拉"))
return new TesLa();
else if (car.equals("大众"))
return new DaZhong();
else
return null;
}
}
这样就违反了OOP七大原则(开闭原则:一个软件的实体应该对扩展开放,对修改关闭),所以简单工厂模式是有一点小问题的,不能动态的增加实现类,无法在不修改车工厂代码的前提下增加实现类,而且简单工厂里面车工厂的方法都是静态的,因此简单工厂模式又叫静态工厂模式。
但是事实上,看源码可知,我们使用的最多的还是简单工厂模式,因为如果想做满足开闭原则,你将会付出很大代价,代码量将会增大很多,就比如下面的 工厂方法模式。
工厂方法模式
工厂方法模式是思路很简单,就是再写一个车工厂的接口。
public interface CarFactory {
Car getCar();
}
让每个车有自己的工厂,每个工厂再实现车工厂的接口。
//五菱车工厂
public class WulingFactory implements CarFactory{
@Override
public Car getCar() {
return new Wuling();
}
}
//特斯拉车工厂
public class TeslaFactory implements CarFactory{
@Override
public Car getCar() {
return new TesLa();
}
}
消费者如果想要车,只需去对应的车工厂去买车就可以了。要五菱去五菱车工厂,要特斯拉去特斯拉车工厂。
Car car1 = new WulingFactory().getCar();
Car car2 = new TeslaFactory().getCar();
这样的话,即使再增加实现类,也只需要继承车工厂的接口写一个实现类即可,不需要修改车工厂的代码了。和简单工厂模式相比,简单工厂模式只有一个车工厂类,所有的车都是在那里面生成。工厂方法模式有多个车工厂,每个车都有自己的车工厂,但是所有的车工厂又都实现车工厂的接口,可以实现动态的增加车的实现类。
比如我们增加一个摩拜单车
public class Mobai implements Car{
@Override
public void Name() {
System.out.println("摩拜单车!");
}
}
只需要增加摩拜单车的车工厂,完全不需要改动其他类的代码。
public class MobaiFactory implements CarFactory{
@Override
public Car getCar() {
return new Mobai();
}
}
消费者想要摩拜只需要去摩拜车工厂去买即可。
Car car3 = new MobaiFactory().getCar();
既然工厂方法模式可以动态的增加实现类还符合开闭原则,那为什么在源码中简单工程模式使用的更多呢?
简单工厂模式和工厂方法模式进行对比
- 结构复杂度上,simple(简单工厂模式)更简单
- 代码复杂度上,simple(简单工厂模式)更简单
- 编程复杂度上,simple(简单工厂模式)更简单
- 管理复杂度上,simple(简单工厂模式)更简单
所以根据设计原则我们应该选择工厂方法模式,但是在实际开发中,简单工程模式使用更多。(当然不是想偷懒)
抽象工厂模式
抽象工厂模式提供了一个 创建一系列相关或相互依赖对象的接口,无需指定他们具体的类。
在介绍抽象工厂之前,我们需要先清除一个概念,产品族和产品等级。就拿小米和华为两家公司生产的手机和路由器来举例:小米手机和小米路由器都是小米的产品,属于同一个产品族,而小米手机和华为手机都是手机,属于同一个产品等级。
我们先定义手机和路由器的接口
//手机产品接口
public interface IphoneProduct {
void start();//开机
void shutdown();//关机
void callup();//打电话
void sendSMS();//发信息
}
//路由器产品接口
public interface IRouterProduct {
void start();//开机
void shutdown();//关机
void openWifi();//打开WiFi
void setting();//设置
}
因为小米和华为都有自己的手机和路由器,所以需要分别实现他们的实现类。
//小米手机
public class XiaomiIphone implements IphoneProduct{
@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("用小米手机发信息");
}
}
//华为手机
public class HuaweiIphone implements IphoneProduct{
@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("用华为手机发信息");
}
}
//小米路由器
public class XiaomiIRouter implements IRouterProduct{
@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("打开小米设置");
}
}
//华为路由器
public class HuaweiIRouter implements IRouterProduct{
@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("打开华为设置");
}
}
现在产品都已经有了,我们需要去写制造他们的工厂,小米工厂和华为工厂。无论是小米还是华为,工厂都是要生产手机和路由器的,所以我们可以先写一个抽象工厂接口,小米和华为都去继承这个抽象工厂,不过小米生产小米的手机和路由器,华为生产华为的手机和路由器。
//抽象工厂
public interface IProductFactory {
//生产手机
IphoneProduct iphoneproduct();
//生产路由器
IRouterProduct irouterproduct();
}
//华为工厂
public class HuaweiFactory implements IProductFactory{
@Override
public IphoneProduct iphoneproduct() {
return new HuaweiIphone();//生产华为手机
}
@Override
public IRouterProduct irouterproduct() {
return new HuaweiIRouter();//生产华为路由器
}
}
//小米工厂
public class XiaomiFactory implements IProductFactory{
@Override
public IphoneProduct iphoneproduct() {
return new XiaomiIphone();//生产小米手机
}
@Override
public IRouterProduct irouterproduct() {
return new XiaomiIRouter();//生产小米路由器
}
}
做完这些后,我们如果想要小米的产品就去小米工厂制造,如果想要华为的产品就去华为工厂制造。
public static void main(String[] args) {
System.out.println("======================小米系列产品======================");
XiaomiFactory xiaomiFactory = new XiaomiFactory();
IphoneProduct Xiaomiiphone = xiaomiFactory.iphoneproduct();
IRouterProduct Xiaomiirouter = xiaomiFactory.irouterproduct();
Xiaomiiphone.callup();Xiaomiirouter.openWifi();
System.out.println("======================华为系列产品======================");
HuaweiFactory huaweiFactory = new HuaweiFactory();
IphoneProduct Huaweiiphone = huaweiFactory.iphoneproduct();
IRouterProduct Huaweiirouter = huaweiFactory.irouterproduct();
Huaweiiphone.callup();Huaweiirouter.openWifi();
}
最后的所有类的结构图就是这个样子,IProductFactory就是抽象工厂,围绕着抽象工厂建造其他工厂,所以抽象工厂又称为工厂的工厂。
如果华为和小米的产品族里面还有笔记本,那我们就需要添加一个产品接口,再修改抽象工厂和实现的类,加入笔记本的制造方法。这样就违反OOP的开闭原则(一个软件的实体应该对扩展开放,对修改关闭),但是如果我们的产品是长期稳定不变的,就可以接受。和前两种工厂 模式相比,抽象工厂把同一产品族放到了一起,一个工厂可以生产一个产品族。
总结
简单工厂模式虽然违反了OOP七大原则,但是在实际中还是使用最多的。
工厂方法模式通过增加工厂在不违反OOP开闭原则的前提下实现动态扩展,但是代码量大,编程和管理复杂反而没有简单工厂使用多。
抽象工厂模式围绕一个抽象工厂去创建其他工厂,一个工厂可以生产一个产品族而不只是一个产品,但是不适合产品经常变动的情况。