浅谈应用工厂模式和单例在Android中实现业务隔离

        Android中写应用,一样需要考虑到降低耦合性的问题,还有一些其他问题,比如App的增量式更新,业务变更的便捷实现等等,都会有工厂模式和单例的身影。

        

        工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑使用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。


        这里就用一个小例子来说明。


        假设我们现在要写一个登陆的业务功能,按照工厂模式的思路,我们不会直接去写一个登陆的类,然后将它new出来去调用login的登陆方法,而是会先写一个接口,接口里有一个方法名login,然后写一个接口的实现类,实现login方法,然后再去将其new出来,这样做的好处在于,如果实现类改变了,接口不需要动,不会影响上层界面。假设我们这里的类分别是:User用户类,IUserEngine登陆业务接口类和UserEngineImpl登陆业务接口实现类。


        到这里,貌似工厂模式差不多了?我们写一个TestCase,就可以直接实现登陆功能了?如下:

	public void testLogin() {
		User user = new User();
		user.setUserName("alex");
		user.setPasswd("123456");
		IUserEngine engine = new UserEngineImpl();
		engine.login(user);
	}

        看似没有问题,但是其中却隐含了大问题。。


        试想,如果你刚写完这个几百行的login方法,test也通过了,正在洋洋得意的时候,老大说,登陆业务要大改。。。怎么办?把刚写好的几百行代码删了重来?等你费了老大劲改完,通过,没准老大又说,恩 ,还是之前的好。。。


        所以最好的办法是不动原来的代码,很多人一定想的到,那不就直接重新写一个接口实现类叫做:UserEngineImpl2不就行了?确实,保留原来的那个UserEngineImpl不动,只是不用它。看上去是比之前好一些,但是还是不满意,假如之前UserEngineImpl在几百个地方被调用,那不得一个个去改?所以还是麻烦。。。


        所以这里最好以不变应万变,什么是不变的,就是那个接口IUserEngine!我们不妨将IUserEngine的名字与它对应的实现类的完整类名对应起来,存储在一个配置文件里面,每次我们都去配置文件中读取到底是要加载哪一个实现类,于是如果业务变更,需要修改实现类的时候,只需要更新一个实现类,并且改一下配置文件中的一行代码即可,实现如下:


BeanFactory:用来加载properties配置文件,并且实现获取对应的类的实例。而本身BeanFactory也只需要一个实例即可,所以采用单例模式,这里使用登记式单例:


package com.example.factorymode.util;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import com.example.factorymode.bean.User;
import com.example.factorymode.engine.IUserEngine;

public class BeanFactory {

	private static Properties properties;

	// 用于登记式单例模式存放BeanFactory唯一实例
	private static Map<String, BeanFactory> map = new HashMap<String, BeanFactory>();

	private static String beanFactoryName = BeanFactory.class.getName();

	static {
		// 单例模式初始化
		BeanFactory bFactory = new BeanFactory();
		map.put(BeanFactory.class.getName(), bFactory);

		// 初始化配置文件加载,只需要加载一次即可
		properties = new Properties();
		try {
			properties.load(BeanFactory.class.getClassLoader()
					.getResourceAsStream("config.properties"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 单例模式 ①提供一个私有的构造函数,使得外界不能去new ②提供一个getInstance方法,给外界提供本类自己的唯一一个实例
	 * 
	 * @return
	 */
	private BeanFactory() {

	}

	// 给外界提供实例,保证唯一性,这里采用登记式单例模式
	public static BeanFactory getInstance() {

		if (map.get(beanFactoryName) == null) {
			try {
				map.put(beanFactoryName,
						(BeanFactory) Class.forName(beanFactoryName)
								.newInstance());
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		return map.get(beanFactoryName);
	}

	/**
	 * 加载需要是实现类
	 * 
	 * @param clazz 接口的类
	 * @return
	 */
	public <T> T getImpl(Class<T> clazz) {
		String simpleName = clazz.getSimpleName();
		String implClassName = properties.getProperty(simpleName);
		try {
			return (T) Class.forName(implClassName).newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}


我们这里的properties文件放在src目录下,这里便于使用类加载器来加载。


config.properties内容:


IUserEngine=com.example.factorymode.engine.impl.UserEngineImpl



然后我们在TestCast里就可以这样来写:


	public void testLogin2(){
		
		User user = new User();
		user.setUserName("alex");
		user.setPasswd("123456");
		
		IUserEngine engine = BeanFactory.getInstance().getImpl(IUserEngine.class);
		
		engine.login(user);
	}


经过上述改造,遇到业务变更的时候,就可以更从容,而且也可以应用到App的增量式更新,比如只有登录业务改变的时候,更新APP的时候就只需要更新一个配置文件,和新增一个登录实现类即可让加载时加载到新增的实现类来产生对应的实例,从而节省用户的流量。




郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。