一、模式定义
简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式(同属于创建型模式的还有工厂方法模式,抽象工厂模式,单例模式,建造者模式)。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
一般由一个工厂类、一个产品基类、多个产品子类构成。由工厂对象决定创建哪种产品类的实例,应用层只需要和工厂类交互。
二、模式结构
从上图可以看出,简单工厂模式由三部分组成:具体工厂、具体产品和抽象产品。
-
工厂类(Creator)角色:担任这个角色的是简单工厂模式的核心,含有与应用紧密相关的商业逻辑。工厂类在客户端的直接调用下创建产品对象,它往往由一个具体Java类实现。
-
抽象产品(AbstractProduct)角色:担任这个角色的类是由简单工厂模式所创建的对象的父类,或它们共同拥有的接口。抽象产品角色可以用一个Java接口或者Java抽象类实现。
-
具体产品(ConcreteProduct)角色:简单工厂模式所创建的任何对象都是这个角色的实例,具体产品角色由一个具体Java类实现。
三、实例分析
例子参考
这个是Video抽象类,有一个produce()方法:
package com.viagra.design.creational.simpleFactory;
/**
* Created by viagra
*/
public abstract class Video {
public abstract void produce();
}
有一个JavaVideo,继承自Video.
package com.viagra.design.creational.simpleFactory;
/**
* Created by viagra
*/
public class JavaVideo extends Video {
@Override
public void produce() {
System.out.println("录制Java课程视频");
}
}
有一个PythonVideo,继承自Video.
package com.viagra.design.creational.simpleFactory;
/**
* Created by viagra
*/
public class PythonVideo extends Video {
@Override
public void produce() {
System.out.println("录制Python课程视频");
}
}
测试类:
package com.viagra.design.creational.simpleFactory;
/**
* Created by viagra
*/
public class Test {
public static void main(String[] args) {
Video video = new PythonVideo();
video.produce();
}
}
对应的类图如下:
移植到简单工厂里面:
新建一个工厂类VideoFactory:
ackage com.viagra.design.creational.simpleFactory;
/**
* @Author: HASEE
* @Description:
* @Date: Created in 11:04 2019/4/26
* @Modified By:
*/
public class VideoFactory {
public Video getVideo(String type){
if ("java".equalsIgnoreCase(type)){
return new JavaVideo();
}else if ("python".equalsIgnoreCase(type)){
return new PythonVideo();
}
return null;
}
}
对应的Test类如下:
package com.viagra.design.creational.simpleFactory;
/**
* Created by viagra
*/
public class Test {
public static void main(String[] args) {
VideoFactory videoFactory = new VideoFactory();
Video video = videoFactory.getVideo("java");
if (video == null){
return;
}
video.produce();
}
}
对应的类图如下:
如果在添加一个算法课程或者前端课程,我们需要修改VideoFactory
的代码,而修改这个类就会带来风险,所以就违反了**“开闭原则”。
可以通过反射来弥补简单工厂模式**的局限性:
package com.viagra.design.creational.simpleFactory;
/**
* @Author: HASEE
* @Description:
* @Date: Created in 11:04 2019/4/26
* @Modified By:
*/
public class VideoFactory {
public Video getVideo(Class c){
Video video = null;
try {
video = (Video) Class.forName(c.getName()).newInstance();
}catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return video;
}
}
对应Test类的修改:
package com.viagra.design.creational.simpleFactory;
/**
* Created by viagra
*/
public class Test {
public static void main(String[] args) {
// VideoFactory videoFactory = new VideoFactory();
// Video video = videoFactory.getVideo("java");
VideoFactory videoFactory = new VideoFactory();
Video video = videoFactory.getVideo(JavaVideo.class);
if (video == null){
return;
}
video.produce();
}
}
四、模式优缺点
1.优点
- 工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象。
- 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。
通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。 - 当需要引入新的产品是不需要修改客户端的代码,只需要添加相应的产品类并修改工厂类就可以了,所以说从产品的角度上简单工厂模式是符合“开-闭”原则的。
2.缺点 - 由于工厂类集中了所有产品创建逻辑,工厂类一般被我们称作“全能类”或者“上帝类”,因为所有的产品创建他都能完成,这看似是好事,但仔细想想是有问题的。比如全国上下所有的事情都有国家主义一个人干会不会有问题,当然有!一旦不能正常工作,整个系统都要受到影响。
- 使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
- 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。所以说从工厂的角度来说简单工厂模式是不符合“开-闭”原则的。
简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
五、在JDK源码中的使用
- 1.Java中的Calendar类,在创建Calendar实例时,就使用了简单工厂。
private static Calendar createCalendar(TimeZone zone, Locale aLocale) {
... ...
Calendar cal = null;
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
if (cal == null) {
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja" && aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
Calendar的UML图如下,
有三个子类BuddhistCalendar,JapaneseImperialCalendar和GregorianCalendar。所以使用者在创建Calendar时,并不需要特别创建某一具体的Calendar,工厂会根据其时区等属性,自动生产合适的Calendar对象。
- 2.jdk中的线程池:ThreadPoolExecutor,根据自己的需求传入corePoolSize、maximumPoolSize、keepAliveTimem、keepAliveTimem、unit、threadFactory、handler这几个参数,new一个指定的ThreadPoolExecutor出来。
jdk提供了Executors这个类,让开发者对线程池的使用与生产分离开,开发者只需要调用不同的方法就可以获取到不同的线程池,开发者不用关心线程池的实现细节,只需要调用api即可获取不同的线程池。
如:Executors.newSingleThreadExecutor() 获取单线程的线程池
Executors.newCachedThreadPool() 获取无界线程池 - 3.slf4j 中简单方法的使用
package org.slf4j;
public final class LoggerFactory {
//..
public static Logger getLogger(Class clazz) {
return getLogger(clazz.getName());
}
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
//..
}
查看IloggerFactory
类:
package org.slf4j;
// 工厂方法
public interface ILoggerFactory {
public Logger getLogger(String name);
}
打开第一个类看看:
package ch.qos.logback.classic;
//..
public class LoggerContext extends ContextBase implements ILoggerFactory,
LifeCycle {
public final Logger getLogger(final String name) {
if (name == null) {
throw new IllegalArgumentException("name argument cannot be null");
}
// if we are asking for the root logger, then let us return it without
// wasting time
if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {// 返回root节点的logger
return root;
}
int i = 0;
Logger logger = root;
// check if the desired logger exists, if it does, return it
// without further ado.
Logger childLogger = (Logger) loggerCache.get(name);
// if we have the child, then let us return it without wasting time
if (childLogger != null) {// 返回子节点的logger
return childLogger;
}
// if the desired logger does not exist, them create all the loggers
// in between as well (if they don't already exist)
String childName;
while (true) {
int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
if (h == -1) {
childName = name;
} else {
childName = name.substring(0, h);
}
// move i left of the last point
i = h + 1;
synchronized (logger) {
childLogger = logger.getChildByName(childName);
if (childLogger == null) {
childLogger = logger.createChildByName(childName);
loggerCache.put(childName, childLogger);
incSize();
}
}
logger = childLogger;
if (h == -1) {
return childLogger;
}
}
}
}
这里用了工厂方法模式,和简单工厂模式,这是一个组合模式。