您的位置:首页 > 编程语言 > Java开发

【JAVA设计模式】简单工厂模式(Simple Factory Pattern)

2016-03-27 11:03 686 查看

一、定义

提供一个创建对象实例的功能,而无须关心其具体实现。被创建实例的类型可以是接口、抽象类,也可以是具体的类。

PS:我们都知道JAVA思想一直推崇“面向接口编程”。而接口的思想就是“封装隔离”,即外部调用只能通过接口进行调用,外部调用并不知道内部具体实现,也就是说外部调用和内部实现是被接口隔离开的。那么只要接口不变,内部实现的变化就不会影响到外部应用,使得系统更加灵活,增加系统的扩展性和可维护性,这也就是所谓的“接口是系统可拔插的保证”。

二、案例

假设有一个接口叫Api,然后有一个实现类Impl实现了它,如何在客户端使用这个接口?

三、未使用模式情况

So Easy !我们可以很快画出这个案例的类图并写出代码。



/**
* @ClassName Api
* @Description: 定义一个接口
* @author Jerry
* @date 2016年3月26日 下午11:27:32
*/
public interface Api {
/**
* @Description: 简单实现,传入字符串打印
* @param @param str
*/
public void test(String str);
}
/**
* @ClassName Impl
* @Description: 实现Api接口
* @author Jerry
* @date 2016年3月27日 上午10:12:27
*/
public class Impl implements Api {

@Override
public void test(String str) {
System.out.println(str);
}
}
/**
* @ClassName Client
* @Description: 外部调用
* @author Jerry
* @date 2016年3月27日 上午10:13:35
*/
public class Client {

public static void main(String[] args) {
Api api = new Impl();
api.test("最爱你的人是我,你怎么舍得我难过");
}

}
如果我们仔细想想就会发现,这样的设计违背了接口的设计原则。因为客户端必须知道哪些具体实例实现了接口,并没有实现“封装隔离”。其实这里只是用到了接口的多态功能。

三、使用模式的情况

使用简单工厂后,类图变成了这个样子:



可以看到,有了工厂后,我们不需要知道接口的具体实现是什么,当我们要创建一个实例时,我们只要去找工厂,帮我创建一个实例就好,这样就可以做到外部调用与内部实现分离的目的。代码如下:

/**
* @ClassName Factory
* @Description: 其余代码和之前没有变化,只增加这样工厂类即可
* @author Jerry
* @date 2016年3月27日 上午10:23:30
*/
public class Factory {

/**
* @Description: 通常把Factory当成一个工具类,不需要创建类实例,所以直接使用静态方法
* @param 当然可以添加参数,使得客户端可以选择自己想要初始化的实例,但不推荐,后面会解释原因
* @return Api  返回一个具体实现
* @throws
*/
public static Api createApi() {
return new Impl();
}
}
/**
* @ClassName Client
* @Description: 外部调用
* @author Jerry
* @date 2016年3月27日 上午10:13:35
*/
public class Client {

public static void main(String[] args) {
//		Api api = new Impl();
Api api = Factory.createApi();	//无须在客户端直接new具体实现
api.test("最爱你的人是我,你怎么舍得我难过");
}

}
一个简单工厂理论上可以创造任何东西,所以又称为“万能工厂”。简单工厂的本质在于“选择合适的实现类”,既然要选择,那么肯定需要用户去指定参数,通常有以下3种:

来源于客户端,指定参数。例如我们可以在创建实例的方法中加入参数,这样客户端可以传入具体参数来获得自己想要的实例。但是这样会带来一个问题,客户端必须知道每个参数的含义功能,使得向用户暴露了一定的内部实现细节。通常我们使用配置文件的写法。
来源于配置文件,从配置文件获取用于判断的值,下文会具体介绍。
来源于程序运行期的某个值,比如内存中的某个变量值,此方法属于动态实现。

使用配置文件进行优化

我们使用properties文件,放在Factory同一个包下。
ImplClass=simpleFactory.Impl
然后修改Factory类,其余不变:
/**
* @ClassName Factory
* @Description: 其余代码和之前没有变化,只增加这样工厂类即可
* @author Jerry
* @date 2016年3月27日 上午10:23:30
*/
public class Factory {

public static Api createApi() {
Properties properties = new Properties();
InputStream in = null;
try {
in = Factory.class.getResourceAsStream("factory.properties");
properties.load(in);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Api api = null;
try {
//使用反射创建实例
api = (Api) Class.forName(properties.getProperty("ImplClass")).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return api;
}

/**
* @Description: 通常把Factory当成一个工具类,不需要创建类实例,所以直接使用静态方法
* @param 当然可以添加参数,使得客户端可以选择自己想要初始化的实例,但不推荐,后面会解释原因
* @return Api  返回一个具体实现
* @throws
*/
@Deprecated
public static Api createApi1() {
return new Impl();
}

}
使用配置文件的写法好处在于,可以更加便捷的更改具体实现类,无须修改代码,使得耦合性降低。

四、总结

何时选用简单工厂

如果想要完全封装隔离具体实现,让外部只能通过接口来操作封装体。
想要把对外创建对象的职责集中管理和控制,因为简单工厂也称万能工厂,可以创建很多的,不相关的对象。

------------------------------------------------------------------------------------------------------------------------------------------
Github: https://github.com/jerry-sc/designPattern

Reference:《研磨设计模式》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: