【设计模式】工厂模式

博主声明:

转载请在开头附加本文链接及作者信息,并标记为转载。本文由博主 威威喵 原创,请多支持与指教。

本文首发于此   博主威威喵  |  博客主页https://blog.csdn.net/smile_running

    工厂模式是比较常用的设计模式之一,通常用于对象的创建操作。联想我们实际生活中的工厂,在制造产品的时候,使用是一种严格的流程,用机器把复杂的工序简单化,而我们就不需要去了解这个东西如何被创建出来,只需要把创建的物品,通过简单的加工、包装一下就可以使用。

    工厂模式就是这个样子的,我们不需要知道那个物品究竟如何被创建出来的,更不需知道经过哪些复杂的操作步骤,只要我们知道如何使用即可。工厂模式的定义:属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

    工厂模式的写法就是:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。工厂模式分三种:简单工厂、工厂方法、抽象工厂。每一种都有细微的变化,需要根据实际场景决定如何选择。

简单工厂写法

    接下来,我们通过一个 茶厂 的案例来使用简单工厂、工厂方法、抽象工厂这三种方式写一下代码。步入正题,话说某地有一个制作茶叶的公司,它的旗下有一个规模不大的茶厂,这个茶厂中拥有三台机器,分别是小型、中型、大型机器,每台机器的产量都是不一样的,而且耗电也有差距,所以为了节省资金,公司都会根据当天待加工的茶叶量进行选择运作的机器。

机器是一个枚举类型,机器代码:

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 线程池的创建方式呢,几种线程池的实例化,也用了工厂设计模式来提供对象的创建的,从而屏蔽了内部的实现代码和复杂的操作,工厂模式是一种创建对象较好的方式。

    那么至此,工厂模式的三种方式都介绍完了,也是经常使用的一种设计模式,在源码中也有很多都利用这种设计模式来创建对象的。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值