目录
工厂模式的主要解决的问题是,将原来分布在各个地方的对象创建过程单独抽离出来,交给工厂类负责创建。其他地方想要使用对象直接找工厂(即调用工厂的方法)获取对象。
工厂模式主要有三种类型
1、简单工厂
2、工厂方法
3、抽象工厂
一、简单工厂
拿泡茶为例,茶叶有好多种类,比如龙井、碧螺春、毛尖等。
首先一个ITea接口:
public interface ITea {
// 沏茶
public void makeTea();
}
再定义两种类型的茶,西湖龙井和碧螺春:
public class LongjingTea implements ITea {
@Override
public void makeTea() {
System.out.println("西湖龙井");
}
}
public class BiluochunTea implements ITea{
@Override
public void makeTea() {
System.out.println("碧螺春");
}
}
假如其它有一处代码要使用“茶”这个对象沏一壶茶,有如下代码:
/**
* 准备沏一杯茶,根据传入的参数决定不同的茶叶类型
*/
public ITea prepareTea(String type){
ITea tea = null;
if(type.equals("longjing")){
tea = new LongjingTea();
}else if(type.equals("biluochun")){
tea = new BiluochunTea();
}
if(tea != null){
tea.makeTea();
}
return tea;
}
接下来,我们可以分析下不使用工厂模式的情况:
如果我们的工程中不至一处用了类似这段代码逻辑,那增加一种茶叶的类型(比如毛尖)就需要修改多处代码,不利于维护。
因此,可以考虑,将创建茶叶对象的逻辑抽离出来,单独放到一个类中,这个类便是工厂类(专门生产茶叶的工厂)。这样维护起来便方便很多,客户端代码也无需知道对象创建的具体细节,只需要从工厂类中获取对象即可。
简单工厂类实现如下:
public class TeaFactory {
public ITea createTea(String type){
ITea tea = null;
if(type.equals("longjing")){
tea = new LongjingTea();
}else if(type.equals("biluochun")){
tea = new BiluochunTea();
}
if(tea != null){
tea.makeTea();
}
return tea;
}
}
客户端代码要使用茶对象,需要从工厂中获取:
public static void main(String[] args) {
TeaFactory teaFactory = new TeaFactory();
ITea tea = teaFactory.createTea("longjing");
}
应用:
Spring 中的 BeanFactory 就是简单工厂模式的体现,根据传入一个唯一的标识来获得 Bean 对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。在容器启动过程中,Spring读取 bean 的 xml 配置文件或者注解了Bean的组件,将 bean 元素分别转换成一个 BeanDefinition 对象。然后通过 BeanDefinitionRegistry 将这些 bean 注册到 beanFactory 中,保存在它的一个 ConcurrentHashMap 中。取的时候通过这个方法来取:
Object getBean(String name)
<T> T getBean(Class<T> requiredType)
<T> T getBean(String name, Class<T> requiredType)
二、工厂方法模式
在上面的简单工厂中,如果要创建的产品类型较多,且各个产品创建的过程不尽相同,则一个工厂类职责会变得越来越多,不符合单一职责原则。
另外简单工厂也不符合开闭原则。要新增一种产品需要修改原来的工厂类。因此,工厂方法模式中,将生产各种类型的产品的工厂也做了抽象分离。比如,上面例子中的,生产龙井的有专门的龙井工厂,生产碧螺春的有专门的碧螺春工厂。
看代码,接着上面的实例进行改造。首先创建统一的工厂接口:
/**
* 生产茶叶的统一接口
*/
public interface ITeaFactory {
// 生产茶叶
public ITea createTea();
}
然后创建两个生产不同类型产品的工厂实现类:
public class LongjingTeaFactory implements ITeaFactory{
@Override
public ITea createTea() {
return new LongjingTea();
}
}
public class BiluochunTeaFactory implements ITeaFactory{
@Override
public ITea createTea() {
return new BiluochunTea();
}
}
客户端代码:
public class FactoryMethodTest {
public static void main(String[] args) {
ITeaFactory factory = new LongjingTeaFactory();
factory.createTea();
factory = new BiluochunTeaFactory();
factory.createTea();
}
}
如果要新增一种茶叶,比如毛尖,只需要新建一个生产毛尖的工厂类实现ITeaFactory即可。可以看到符合开闭原则、单一职责原则。
工厂方法适用于以下场景:
1、创建对象需要大量重复的代码。
2、客户端(应用层)不依赖于产品类实例如何被创建、实现等细节。
3、一个类通过其子类来指定创建哪个对象。工厂方法也有缺点:
1、类的个数容易过多,增加复杂度。
2、增加了系统的抽象性和理解难度。
应用:
在Spring中用到的静态工厂模式的是FactoryBean接口,具体包含的接口如下所以:
T getObject() throws Exception; //返回此工厂管理的对象的实例
Class<?> getObjectType(); //返回此FactoryBean创建的对象的类型
//这个工厂返回的对象是否是单例,默认返回true;若是单例则由getObject返回的//是相同的对象,是可以缓存的引用
default boolean isSingleton() {
return true;
}
所以对照着来看的,Factorybean本身就是一个抽象工厂,每个开发自己实现Factorybean接口的实现类就是一个具体工厂,这个工厂通过 getObject() 方法可以拿到一个特定的对象。
在Spring工程中使用Factorybean也很简单,案例如下:
//FactoryBean接口的实现类
@Component
public class FactoryBeanLearn implements FactoryBean {
@Override
public Object getObject() throws Exception {
//这个Bean是我们自己new的,这里我们就可以控制Bean的创建过程了
return new FactoryBeanServiceImpl();
}
@Override
public Class<?> getObjectType() {
return FactoryBeanService.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
//接口
public interface FactoryBeanService {
/**
* 测试FactoryBean
*/
void testFactoryBean();
}
//实现类
public class FactoryBeanServiceImpl implements FactoryBeanService {
/**
* 测试FactoryBean
*/
@Override
public void testFactoryBean() {
System.out.println("我是FactoryBean的一个测试类。。。。");
}
}
//单测
@Test
public void test() {
ClassPathXmlApplicationContext cac = new ClassPathXmlApplicationContext("classpath:com/applicationContext.xml");
FactoryBeanService beanService = cac.getBean(FactoryBeanService.class);
beanService.testFactoryBean();
}
实现原理:实现了 FactoryBean 接口的 bean 是一类叫做 factory 的 bean。其特点是,spring 会在使用 getBean()调用获得该 bean 时,会自动调用该 bean 的 getObject()方法,所以返回的不是 factory 这个 bean,而是这个 bean.getOjbect()方法的返回值。
三、抽象工厂
举例说明下,有两个工厂,美的、格力。这两个工厂都生产两种产品:冰箱和洗衣机。下面使用抽象工厂模式来描述每个工厂的两种产品的创建过程。
1、首先创建两个产品的接口类:
/**
* 冰箱
*/
public interface IFridge {
// 冷藏
void coldStorage();
}
/**
* 洗衣机
*/
public interface IWasher {
void wash();
}
2、创建每个工厂的两种产品(总共四种产品):
美的的冰箱和洗衣机
public class MeideFridge implements IFridge{
@Override
public void coldStorage() {
System.out.println("美的冰箱");
}
}
public class MeideWasher implements IWasher {
@Override
public void wash() {
System.out.println("美的洗衣机");
}
}
格力的冰箱和洗衣机
public class GeliFridge implements IFridge {
@Override
public void coldStorage() {
System.out.println("格力冰箱");
}
}
public class GeliWasher implements IWasher{
@Override
public void wash() {
System.out.println("格力洗衣机");
}
}
3、创建抽象工厂接口
家用电器工厂,生产一组产品
/**
* 抽象工厂接口,家用电器工厂,生产冰箱和洗衣机
*/
public interface IHouseholdElectricFactory {
IFridge createFridge();
IWasher createWasher();
}
4、创建具体产品等级的工厂
这里是创建美的和格力的工厂实现类。
public class MeideHouseholdFactory implements IHouseholdElectricFactory{
@Override
public IFridge createFridge() {
return new MeideFridge();
}
@Override
public IWasher createWasher() {
return new MeideWasher();
}
}
public class GeliHouseholdFactory implements IHouseholdElectricFactory{
@Override
public IFridge createFridge() {
return new GeliFridge();
}
@Override
public IWasher createWasher() {
return new GeliWasher();
}
}
5、客户端代码使用
public class AbsFactoryTest {
public static void main(String[] args) {
IHouseholdElectricFactory factory = new MeideHouseholdFactory();
factory.createFridge().coldStorage();
factory.createWasher().wash();
}
}
四、三种工厂模式比较
1.简单工厂模式与工厂方法对比
简单工厂:定义一个抽象的算法接口,提供不同算法的公共接口方法。其他具体算法继承这个抽象类,并实现具体的算法
工厂方法:定义一个用于创建对象的接口,让子类决定实例化哪一个类。 遵循了开放—封闭原则
工厂模式PK简单工厂模式:
1、工厂方法的优点:
工厂方法是简单工厂模式的进一步抽象和推广。它遵循了“开放—封闭”原则。
2、简单工厂模式的优点:
简单工厂模式最大的优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。
3、工厂方法的缺点:
工厂方法把简单工厂的内部逻辑判断转移到了客户端代码来执行;每增加一产品就要增加一个产品工厂的类,增加了额外的开发量。
4、简单工厂的缺点:
是没有遵守开放—封闭原则。所谓的“开放-封闭”原则就是开放接口,封闭修改。如果将来需要添加一个开方的算法,那么,在简单工厂模式中,就必须在简单工厂类中添加相应的判断语句!另外,在简单工厂类中利用了Switch语句,这对程序的扩展本身就不不利。
2.工厂方法与抽象工厂对比
抽象工厂:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
工厂模式和抽象工厂的区别:
只有一个User类和User操作类的时候,只需要工厂方法模式; 但数据库中有很多的表,而Sql和access又是两大不同的分类,所以就延伸到了抽象工厂模式
抽象工厂的优点:
1、易于交换产品系列,由于具体工厂类,在一个应用程序中只需要在初始化的时候出现一次, 这就使得改变一个应用的具体工厂变得非常容易,它只需改变具体工厂即可使用不同的产品配置。
2、它让具体的创建实例过程与客户端分离,客户端是通过他们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中。
抽象模式的缺点:
1、抽象模式虽然便于两数据库之间的切换,但是不便于增加需求功能。
2、如果有100个调用数据库访问的类,就需要多次实例化100此具体工厂类。