简单工厂模式
简单工厂模式是工厂方法的简化版,又称之为静态工厂方法
下面给出简单工厂方法的类结构
有了类图结构就可以根据类图写出相应的代码了.
首先是产品类,我们将同一类产品抽象为一个接口,表示这是一类产品,例如汽车类,定义一个汽车Car接口
Car汽车接口
/**
*@DESCRIPTION 这是一个汽车接口
*@AUTHOR SongHongWei
*@TIME 2018/8/11-23:40
*@PACKAGE_NAME test.factory.simple
**/
public interface Car
{
public void description();
}
对于Car汽车接口有很多实现类,例如宝马,奥迪,奔驰等等,那么下面就要定义具体的产品了,我们定义两个汽车产品,分别是BMW和Audi
产品实现类
BMW
/*
*@DESCRIPTION 宝马汽车
*@AUTHOR SongHongWei
*@TIME 2018/8/11-23:40
*@PACKAGE_NAME test.factory.simple
**/
public class BMW implements Car
{
@Override
public void description()
{
System.out.println("This is BMW");
}
}
Audi
/*
*@DESCRIPTION 奥迪汽车
*@AUTHOR SongHongWei
*@TIME 2018/8/11-23:40
*@PACKAGE_NAME test.factory.simple
**/
public class Audi implements Car
{
@Override
public void description()
{
System.out.println("This is Audi");
}
}
现在有了产品,我们就需要用来生产产品的工厂,所以需要建立一个用于生产汽车的汽车工厂
汽车工厂
/**
*@DESCRIPTION 根据不同的产品名称生产不同的产品,这里的返回值用Object接收,后面优化时用反射
*@AUTHOR SongHongWei
*@TIME 2018/8/11-23:31
*@CLASS_NAME SimpleFactory
**/
public Object getProduct(String productName)
{
if ("BMW".equals(productName))
{
return new BMW();
}
else if ("Audi".equals(productName))
{
return new Audi();
}
return null;
}
现在有了汽车工厂又有了产品,作为客户我们就可以命令工厂去生产指定的产品,假如我现在想要有一辆宝马,那我就跟工厂说,给我生产一辆宝马出来,代码实现方式就是
/*
*@DESCRIPTION 客户调用
*@AUTHOR SongHongWei
*@TIME 2018/8/11-23:33
*@PACKAGE_NAME test.factory.simple
**/
public class Client
{
public static void main(String[] args)
{
SimpleFactory simpleFactory = new SimpleFactory();
Car car = (Car)simpleFactory.getProductByReflect("BMW");
car.description();
}
}
输出结果为:
This is BMW
Process finished with exit code 0
其实作为上面的工厂方法可以利用java的反射去优化,这样就不用因为新增了汽车产品而一直else if下去了
修改后的工厂实现方法
/**
*@DESCRIPTION 这里引入了PropertiesUtil工具类
*@AUTHOR SongHongWei
*@TIME 2018/8/12-0:18
*@CLASS_NAME SimpleFactory
**/
public static Object getProductByReflect(String productName)
{
String className = PropertiesUtil.getPropertyValue(productName);
try
{
return Class.forName(className).newInstance();
}
catch (InstantiationException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
return null;
}
上述代码中引入了一个PropertiesUtil工具类,这个工具类是为了读取properties文件里面的内容的,为什么要建立.properties文件呢,因为java里面的反射机制是通过加载类的全路径是得到类的字节码的,作为调用者来说,根本就不知道类的全路径是什么,如果知道了,那根本就用不了工厂方法,直接去new出来就行了,对于用户来说,用户只知道他想要什么产品,而不去关心产品是怎么是实现或生成的,例如,我想要辆BMW,我不需要知道BMW的实现类是谁,我只要告诉工厂我要BMW就可以了,那么工厂怎么根据BMW去获取对应的类的全路径呢,这时就需要用到.properties文件了,我们可以事先在.properties文件里定义好BMW对应的类的全路径,就行Spring里bean的配置文件一样,定义一个id 后面跟着class的全路径,我们这里定义一个key后面跟着类的全路径,properties的内容如下所示:
bean.properties
BMW=test.factory.simple.BMW
Audi=test.factory.simple.Audi
PropertiesUtil 工具类的实现
public class PropertiesUtil
{
/**
*@DESCRIPTION 根据key值获取properties文件的value
*@AUTHOR SongHongWei
*@TIME 2018/8/12-0:09
*@CLASS_NAME PropertiesUtil
**/
public static String getPropertyValue(String key)
{
String value = null;
try
{
InputStream in = Properties.class.getResourceAsStream("/test/factory/simple/bean.properties");
Properties p = new Properties();
p.load(in);
value = p.getProperty(key);
}
catch (IOException e)
{
e.printStackTrace();
}
return value;
}
}
客户端的修改
/**
*@DESCRIPTION ${END}
*@AUTHOR SongHongWei
*@TIME 2018/8/11-23:33
*@PACKAGE_NAME test.factory.simple
**/
public class Client
{
public static void main(String[] args)
{
Car car = (Car)SimpleFactory.getProductByReflect("BMW");
car.description();
}
}
输出结果:
This is BMW
Process finished with exit code 0
总结
优点:简单工厂模式能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。明确区分了各自的职责和权力,有利于整个软件体系结构的优化。用户在使用时可以直接根据工厂类去创建所需的实例,而无需了解这些对象是如何创建以及如何组织的。有利于整个软件体系结构的优化。
缺点:由于工厂类集中了所有实例的创建逻辑,这就直接导致一旦这个工厂出了问题,所有的客户端都会受到牵连;而且由于简单工厂模式的产品室基于一个共同的抽象类或者接口,这样一来,一旦产品的种类增加的时候,即有不同的产品接口或者抽象类的时候,工厂类就需要判断何时创建何种种类的产品,这就和创建何种种类产品的产品相互混淆在了一起,违背了单一职责和“开放封闭原则”,因为当我们新增加一个产品的时候必须修改工厂类,相应的工厂类就需要重新编译一遍。
JDK中使用案例
在jdk1.7中,线程池Executors就使用了静态工厂方法,有兴趣的可以去看一下源码
Executors 的构造方法
构造方法被私有化了,也就是说不能new出来
/**
* Cannot instantiate.
*/
private Executors() {}
Executors 提供的一些静态工厂方法
/**
* Creates a single-threaded executor that can schedule commands
* to run after a given delay, or to execute periodically.
* (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* <tt>newScheduledThreadPool(1)</tt> the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
* @return the newly created scheduled executor
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
/**
* Creates a single-threaded executor that can schedule commands
* to run after a given delay, or to execute periodically.
* (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* <tt>newScheduledThreadPool(1)</tt> the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
* @return the newly created scheduled executor
*/
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}