博主声明:
转载请在开头附加本文链接及作者信息,并标记为转载。本文由博主 威威喵 原创,请多支持与指教。
工厂模式是比较常用的设计模式之一,通常用于对象的创建操作。联想我们实际生活中的工厂,在制造产品的时候,使用是一种严格的流程,用机器把复杂的工序简单化,而我们就不需要去了解这个东西如何被创建出来,只需要把创建的物品,通过简单的加工、包装一下就可以使用。
工厂模式就是这个样子的,我们不需要知道那个物品究竟如何被创建出来的,更不需知道经过哪些复杂的操作步骤,只要我们知道如何使用即可。工厂模式的定义:属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
工厂模式的写法就是:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。工厂模式分三种:简单工厂、工厂方法、抽象工厂。每一种都有细微的变化,需要根据实际场景决定如何选择。
简单工厂写法
接下来,我们通过一个 茶厂 的案例来使用简单工厂、工厂方法、抽象工厂这三种方式写一下代码。步入正题,话说某地有一个制作茶叶的公司,它的旗下有一个规模不大的茶厂,这个茶厂中拥有三台机器,分别是小型、中型、大型机器,每台机器的产量都是不一样的,而且耗电也有差距,所以为了节省资金,公司都会根据当天待加工的茶叶量进行选择运作的机器。
机器是一个枚举类型,机器代码:
package com.xww.dp.factory;
/**
* 制茶的机器
*
* @author xww
*
*/
public enum Mechine {
SmallMechine, MiddleMechine, BigMechine
}
这家公司规模比较小,由于技术资金和技术有限,目前只能生产红茶、绿茶、白茶、龙井茶这四个品种,它是一个接口:
package com.xww.dp.factory;
/**
* 茶公司的主要生产茶品种情况
*
* @author xww
*
*/
public interface TeaCompany {
void hongCha();// 红茶
void lvCha();// 绿茶
void baiCha();// 白茶
void longJing();// 龙井
}
因为茶厂中有三台机器,每一台的产量和耗电量都是不同的,那么机器就需要实现这个接口,只有实现接口才能执行上面所说的不同品种的茶的生产功能。
小型机器:
package com.xww.dp.factory;
/**
* 小型机器
*
* @author xww
*
*/
public class SmallMechineFactory implements TeaCompany {
public SmallMechineFactory() {
}
@Override
public void hongCha() {
System.out.println("生产了 0.5t 红茶");
}
@Override
public void lvCha() {
System.out.println("生产了 0.6t 绿茶");
}
@Override
public void baiCha() {
System.out.println("生产了 0.4t 白茶");
}
@Override
public void longJing() {
System.out.println("生产了 0.2t 龙井茶");
}
}
中型机器:
package com.xww.dp.factory;
/**
* 中型机器
*
* @author xww
*
*/
public class MiddleMechineFactory implements TeaCompany {
public MiddleMechineFactory() {
}
@Override
public void hongCha() {
System.out.println("生产了 1t 红茶");
}
@Override
public void lvCha() {
System.out.println("生产了 1.2t 绿茶");
}
@Override
public void baiCha() {
System.out.println("生产了 0.8t 白茶");
}
@Override
public void longJing() {
System.out.println("生产了 0.5t 龙井茶");
}
}
大型机器:
package com.xww.dp.factory;
/**
* 大型机器
*
* @author xww
*
*/
public class BigMechineFactory implements TeaCompany {
public BigMechineFactory() {
}
@Override
public void hongCha() {
System.out.println("生产了 2.2t 红茶");
}
@Override
public void lvCha() {
System.out.println("生产了 3.5t 绿茶");
}
@Override
public void baiCha() {
System.out.println("生产了 2t 白茶");
}
@Override
public void longJing() {
System.out.println("生产了 1.5t 龙井茶");
}
}
这样的话,每一台机器就拥有了制作茶叶的功能了,因为工厂模式规定,不能暴露对象的创建,通过一个接口的方式来指定要创建的对象,所以这里不能在外部直接 new 每一台机器了,而是通过一个 Factory 类来创建不同的机器。
package com.xww.dp.factory;
/**
* 工厂类,提供对象创建的方法
*
* @author xww
*
*/
public class TeaFactory {
// 私有化
private TeaFactory() {
}
public static TeaCompany createTeaFractory(Mechine mechine) {
switch (mechine) {
case SmallMechine:
return new SmallMechineFactory();
case MiddleMechine:
return new MiddleMechineFactory();
case BigMechine:
return new BigMechineFactory();
}
return null;
}
}
然后呢,我们的客户端就可以这样调用:
package com.xww.dp.factory;
/**
* 工厂模式 —— 简单工厂
*
* 有什么问题呢? 如果工厂引进新的机器,那么需要在 enum 里面加一个机器,还有添加一个类,修改两个地方。
*
* @author xww
* @博客 ;https://blog.csdn.net/smile_running?t=1
*/
public class SimpleFactoryClient {
public static void main(String[] args) {
TeaCompany company = TeaFactory.createTeaFractory(Mechine.SmallMechine);
company.hongCha();
company.lvCha();
company.baiCha();
company.longJing();
}
}
通过传入不同的枚举 Mechine 制作茶叶的机器,就可以方便的切换对象。
好了,以上就是简单工厂的创建对象的方式。下面来看看一般的工厂方法如何进行修改,工厂方法呢,指的是每一种茶都代表一种产品,所谓一种产品,其就是一个类,一个类通常都是单一的职责,所以要对简单工厂的修改,也非常简单。
工厂方法写法
继续回到我们的茶厂,说到这个茶公司啊,由于营业了两三年,收益不错,老板对公司进行了规模升级,同时引入了新的一台超级机器,产量比之前的机器更大。
根据老板对公司的升级操作,我们的代码也要添加一台超级机器。不过这里不再使用简单工厂,而是运用工厂方法来写,代码与之间有差异,需要修改几处地方。
首先,TeaFactory 已经变成了接口的形式,这是一个巨大的变化,这里的返回值,就是我们的茶公司接口对象。
package com.xww.dp.factory;
/**
* 工厂类,提供对象创建的方法
*
* @author xww
*
*/
public interface TeaFactory {
TeaCompany createCompany();
}
接下来,为每一台机器创建一个实例对象:
package com.xww.dp.factory;
/**
* 小型机器创建
*
* @author xww
*
*/
public class SmallMechineFactoryCreater implements TeaFactory {
@Override
public TeaCompany createCompany() {
return new SmallMechineFactory();
}
}
package com.xww.dp.factory;
/**
* 中型机器创建
*
* @author xww
*
*/
public class MiddleMechineFactoryCreater implements TeaFactory {
@Override
public TeaCompany createCompany() {
return new MiddleMechineFactory();
}
}
package com.xww.dp.factory;
/**
* 大型机器创建
*
* @author xww
*
*/
public class BigMechineFactoryCreater implements TeaFactory {
@Override
public TeaCompany createCompany() {
return new BigMechineFactory();
}
}
这一步骤主要目的:为了与 enum 进行解耦,因为我们在新加入的超级机器时,必定要在枚举类中加入新的代码,而且还有在创建对象的方法里面也要做出相应的添加,不仅修改了两处地方,而且违背了我们尽量不要去修改原有的代码,所以能别修改就尽量少修改。
那么把 TeaFactory 类改为接口的形式,无异于多了很多的实现类出来,比如上面的三个机器,就要多出三个类去实现这个接口,从而创建对象,工厂方法势必会多出很多类来。而刚刚我们提到老板引进了新的超级机器,所以用工厂方法至少避免了修改原有的代码,我们只需要新建一个类,实现 TeaFactory 接口即可:
package com.xww.dp.factory;
/**
* 超级机器创建
*
* @author xww
*
*/
public class SuperMechineFactoryCreater implements TeaFactory {
@Override
public TeaCompany createCompany() {
return new SuperMechineFactory();
}
}
然后,客户端与要做出相应的变化。
package com.xww.dp.factory;
/**
* 简单工厂 - 每个工厂对应一个对象实例类
*
* @author xww
*
*/
public class GerneralFactoryClient {
public static void main(String[] args) {
TeaFactory teaFactory = new MiddleMechineFactoryCreater();
TeaCompany company = teaFactory.createCompany();
company.hongCha();
company.lvCha();
company.baiCha();
company.longJing();
}
}
这样的话,我们就从简单工厂转变到工厂方法的写法了,而简单工厂用到的一个枚举类型,就不需要再使用了。工厂方法就是每一个工厂对应一个产品,它们之间的创建方式,都是通过接口来实现的。不过工厂方法也暴露了一个缺点,就是类太多了,就如上面一样,每引进一台机器就要创建一个新的类,那我引进几十台呢,我调用的时候都不知道要 new 哪一个类,几十个类根本不清楚要实例化哪一个工厂类,这就有点懵了,而且类越来越多的话更不好办,那么下面来看看抽象工厂如何解决这个问题。
抽象工厂写法
既然工厂模式有三种方式,那它必定有其存在的道理,工厂方法可以解决简单工厂对类的修改问题,而抽象工厂的出现也就是为了解决工厂方法类繁重的问题,我们并不知道要实例化的类有哪几个,这让我们怎么用。所以,抽象工厂就解决了这个问题,把类的创建隐藏起来,通过 static 方法让我们可以选择需要的工厂对象。
所以呢,代码又需要做大改动了,我们通过工厂方法创建的 4 个机器 FactoryCreater 类就不需要了,因为抽象工厂正是为了解决这个问题,那它们就没有必要存在了。因为少了这 4 个类,我们的 TeaFactory 要大改版,不再是 interface 类型,而是 class 类型,并且提供 static 方法给外部调用:
package com.xww.dp.factory;
/**
* 工厂类,提供对象创建的方法
*
* @author xww
*
*/
public class TeaFactory {
private static TeaCompany createCompany(Class<? extends TeaCompany> company) {
try {
return (TeaCompany) company.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static TeaCompany createSmallMechineFactory() {
return createCompany(SmallMechineFactory.class);
}
public static TeaCompany createMiddleMechineFactory() {
return createCompany(MiddleMechineFactory.class);
}
public static TeaCompany createBigMechineFactory() {
return createCompany(BigMechineFactory.class);
}
public static TeaCompany createSuperMechineFactory() {
return createCompany(SuperMechineFactory.class);
}
}
这里通过类的反射拿到对象的实例,也没有用到枚举类型,在原来简单工厂的基础上稍微修改即可。然后是客户端的调用:
package com.xww.dp.factory;
/**
* 抽象工厂 —— 解决工厂方法类多的问题
*
* @author xww
* @博客 : https://blog.csdn.net/smile_running?t=1
*/
public class AbstractFactoryClient {
public static void main(String[] args) {
TeaCompany company = TeaFactory.createSuperMechineFactory();
company.hongCha();
company.lvCha();
company.baiCha();
company.longJing();
}
}
看上去,是不是有点像 Java 线程池的创建方式呢,几种线程池的实例化,也用了工厂设计模式来提供对象的创建的,从而屏蔽了内部的实现代码和复杂的操作,工厂模式是一种创建对象较好的方式。
那么至此,工厂模式的三种方式都介绍完了,也是经常使用的一种设计模式,在源码中也有很多都利用这种设计模式来创建对象的。