1 简单工厂模式的定义
1.1 简单工厂模式的通用类图
1.2 简单工厂模式的实用通用源码
(1) 抽象产品类
public abstract class Product {
public void method1(){
// 业务逻辑处理
}
// 抽象方法
public abstract void method2();
}
(2) 具体产品类1、2
/**
* 具体的产品类可以有多个,都继承于抽象产品类
*/
public class ConcreteProduct1 extends Product {
public void method2() {
//业务逻辑处理
}
}
public class ConcreteProduct2 extends Product {
public void method2() {
//业务逻辑处理
}
}
(3)具体工厂类
public class Creator {
/**
* 简单工厂模式或者静态工厂模式
* @param clz 产品对象类类型
* @return 具体的产品对象
*/
public static <T extends Product> T createProduct(Class<T> clz) {
Product product = null;
try {
product = (Product) Class.forName(clz.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T)product;
}
}
(4) 场景类
public class Client {
public static void main(String[] args) {
Product product1 = Creator.createProduct(ConcreteProduct1.class);
Product product2 = Creator.createProduct(ConcreteProduct2.class);
}
}
1.3 总结
一个抽象产品类,可以派生出多个具体产品类。一个具体工厂类,通过往此工厂的static方法中传入不同参数,产出不同的具体产品类实例。
2 工厂方法模式的定义
2.1 工厂方法模式的通用类图
2.2 工厂方法模式的实用通用源码
(1) 抽象产品类
public abstract class Product {
public void method1(){
//业务逻辑处理
}
//抽象方法
public abstract void method2();
}
(2) 具体产品类1、2
/**
* 具体的产品类可以有多个,都继承于抽象产品类
*/
public class ConcreteProduct1 extends Product {
public void method2() {
//业务逻辑处理
}
}
public class ConcreteProduct2 extends Product {
public void method2() {
//业务逻辑处理
}
}
(3) 抽象工厂类
/**
* 抽象工厂类负责定义产品对象的产生
*/
public abstract class Creator {
// 创建一个产品对象,其输入参数类型可以自行设置通常为String、Enum、Class等,当然也可以为空
public abstract <T extends Product> T createProduct(Class<T> c);
}
(4)具体工厂类A
/**
* 具体如何产生一个产品的对象,是由具体的工厂类实现的
*/
public class ConcreteCreatorA extends Creator {
public <T extends Product> T createProduct(Class<T> c) {
Product product=null;
try {
product =(Product)Class.forName(c.getName()).newInstance();
} catch (Exception e) {
}
return (T)product;
}
}
(5)具体工厂类B
/**
* 具体如何产生一个产品的对象,是由具体的工厂类实现的
*/
public class ConcreteCreatorB extends Creator {
public <T extends Product> T createProduct(Class<T> c) {
Product product=null;
try {
product =(Product)Class.forName(c.getName()).newInstance();
} catch (Exception e) {
}
return (T)product;
}
}
(5) 场景类
public class Client {
public static void main(String[] args) {
Creator creatorA = new ConcreteCreatorA();
Product productA1 =creatorA.createProduct(ConcreteProduct1.class);
Product productA2 =creatorA.createProduct(ConcreteProduct2.class);
Creator creatorB = new ConcreteCreatorB();
Product productB1 =creatorB.createProduct(ConcreteProduct1.class);
Product productB2 =creatorB.createProduct(ConcreteProduct2.class);
}
}
2.3 总结
一个抽象产品类,可以派生出多个具体产品类。一个抽象工厂类,可以派生出多个具体工厂类。每个具体工厂类只能创建一个具体产品类的实例。
3 工厂方法模式实战
(1) 抽象产品类,IoHandler.java
public abstract class IoHandler {
public abstract void add(String id,String name);
public abstract void remove(String id,String name);
public abstract void update(String id,String name);
public abstract String query(String id);
}
(2) 具体产品类,DBHandler.java,FileHandler.java,XmlHandler.java
public class DBHandler extends IoHandler{
@Override
public void add(String id, String name) {
}
@Override
public void remove(String id, String name) {
}
@Override
public void update(String id, String name) {
}
@Override
public String query(String id) {
return "DBHandler";
}
}
public class FileHandler extends IoHandler {
@Override
public void add(String id, String name) {
}
@Override
public void remove(String id, String name) {
}
@Override
public void update(String id, String name) {
}
@Override
public String query(String id) {
return "FileHandler";
}
}
public class XmlHandler extends IoHandler {
@Override
public void add(String id, String name) {
}
@Override
public void remove(String id, String name) {
}
@Override
public void update(String id, String name) {
}
@Override
public String query(String id) {
return "SMBrother";
}
}
(3) 抽象工厂类,Factory.java
public abstract class Factory {
public abstract <T extends IoHandler> T getIOHandler(Class<T> clz);
}
(4)具体工厂类,IOFactory.java
public class IOFactory extends Factory {
public <T extends IoHandler> T getIOHandler(Class<T> clz) {
IoHandler handler = null;
try {
handler = (IoHandler)Class.forName(clz.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return (T) handler;
}
}
(6) 场景类
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView tvContent = (TextView) findViewById(R.id.tvContent);
IOFactory factory = new IOFactory();
// 获取查询普通文件数据
Button btnFile = (Button) findViewById(R.id.btnFile);
btnFile.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
IoHandler handler = factory.getIOHandler(FileHandler.class);
tvContent.setText(handler.query("333333333"));
}
});
// 获取查询Xml文件数据
Button btnXml = (Button) findViewById(R.id.btnXml);
btnXml.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
IoHandler handler = factory.getIOHandler(XmlHandler.class);
tvContent.setText(handler.query("333333333"));
}
});
// 获取查询数据库文件数据
Button btnDB = (Button) findViewById(R.id.btnDB);
btnDB.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
IoHandler handler = factory.getIOHandler(DBHandler.class);
tvContent.setText(handler.query("333333333"));
}
});
}
}
(6) 结果演示
4 工厂方法模式的应用
4.1 工厂方法模式的优点
(1)良好的封装性,代码结构清晰。一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的过程,降低模块间的耦合。
(2)工厂方法模式的扩展性非常优秀。在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以完成“拥抱变化”。例如在我们的例子中,需要增加一个棕色人种,则只需要增加一个ConcreteProduct3类,工厂类不用任何修改就可完成系统扩展。
(3)屏蔽产品类。这一特点非常重要,调用者不需要关心产品类的实现是如何变化的,它只需要关心产品的接口,只要接口保持不变系统中的上层模块就不要发生变化。因为产品类的实例化工作是由工厂类负责的,一个产品对象具体由哪一个产品生成是由工厂类决定的。
(4)工厂方法模式是典型的解耦框架。高层模块值需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特法则,我不需要的就不要去交流;也符合依赖倒置原则,只依赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类!
4.2 工厂方法模式的使用场景
(1)工厂方法模式是new一个对象的替代品。所以在所有需要生成对象的地方都可以使用,但是需要慎重地考虑是否要增加一个工厂类进行管理,增加代码的复杂度。
(2)需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式。例如需要设计一个连接邮件服务器的框架,有三种网络协议可供选择:POP3、IMAP、HTTP,我们就可以把这三种连接方法作为产品类,定义一个接口如:IConnectMail,然后定义对邮件的操作方法,用不同的方法实现三个具体的产品类再定义一个工厂方法,按照不同的传入条件,选择不同的连接方式。如此设计可以做到完美的扩展,如某些邮件服务器提供了WebService接口,我们只要增加一个产品类就可以了。
(3)工厂方法模式可以用在异构项目中。例如通过WebService与一个非Java的项目交互,虽然WebService号称是可以做到异构系统的同构化,但是在实际的开发中,还是会碰到很多问题,如类型问题、WSDL文件的支持问题,等等。从WSDL中产生的对象都认为是一个产品,然后由一个具体的工厂类进行管理,减少与外围系统的耦合。
(4)使用在测试驱动开发的框架下。例如测试一个类A,就需要把与类A有关联关系的类B也同时产生出来,我们可以使用工厂方法模式把类B虚拟出来,避免类A与类B的耦合。目前由于JMock和EasyMock的诞生,该使用场景已经弱化了,读者可以在遇到此种情况时直接考虑使用JMock或EasyMock。
5 扩展
5.1 替代单例模式
单例模式的核心要求就是在内存中只有一个对象,通过工厂方法模式也可以只在内存中生产一个对象。
非常简单的类图,Singleton定义了一个private的无参构造函数,目的是不允许通过new的方式创建一个对象。
(1) 单例类
public class Singleton {
// 不允许通过new产生一个对象
private Singleton(){
}
public void doSomething(){
// 业务处理
}
}
(2)负责生成单例的工厂类
/**
* Singleton保证不能通过正常的渠道建立一个对象,那SingletonFactory通过反射方式创建建立一个单例对象
*/
public class SingletonFactory {
private static Singleton singleton;
static{
try {
Class cl= Class.forName(Singleton.class.getName()); // 获得无参构造
Constructor constructor=cl.getDeclaredConstructor(); // 设置无参构造是可访问的
constructor.setAccessible(true); // 产生一个实例对象
singleton = (Singleton)constructor.newInstance();
} catch (Exception e) {
// 异常处理
}
}
public static Singleton getSingleton(){
return singleton;
}
}
通过获得类构造器,然后设置访问权限生成一个对象,然后提供外部访问,保证内存中的对象唯一。以上通过工厂方法模式创建了一个单例对象,该框架可以继续扩展,在一个项目中可以产生一个单例构造器,所有需要产生单例的类都遵循一定的规则(构造方法是private),然后通过扩展该框架,只要输入一个类型就可以获得唯一的一个实例。
5.2 延迟初始化
一个对象被消费完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被使用。
ProductFactory负责产品类对象的创建工作,并且通过prMap变量产生一个缓存,对需要再次被重用的对象保留,Product和ConcreteProduct是一个示例代码,请参考前面的代码。
(1) 延迟加载的工厂类
public class ProductFactory {
private static final Map<String,Product> prMap = new HashMap();
public static synchronized Product createProduct(String type) throws Excep;
// 如果Map中已经有这个对象
Product product =null;
if(prMap.containsKey(type)){
product = prMap.get(type);
} else {
if(type.equals("Product1")) {
product = new ConcreteProduct1();
} else {
product = new ConcreteProduct2();
}
// 同时把对象放到缓存容器中
prMap.put(type,product);
}
return product;
}
代码还比较简单,通过定义一个Map容器,容纳所有产生的对象,如果在Map容器中已经有的对象,则直接取出返回;如果没有,则根据需要的类型产生一个对象并放入到Map容器中,以方便下次调用。
延迟加载框架是可以扩展的,例如限制某一个产品类的最大实例化数量,可以通过判断Map中已有的对象数量来实现。例如JDBC连接数据库,都会要求设置一个MaxConnections最大连接数量,该数量就是内存中最大实例化的数量。
6 参考文章
《Android源码设计模式解析与实战》
《设计模式之禅》