工厂模式(FactoryModel)

简介

工厂模式是Java中最常用的设计模式之一,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建时不会对客户端暴露创建逻辑,并且是通过一种公共的接口来指向新的对象。

举例说明

假如你想要买一辆车,你只要知道具体你想要的是什么车就可以去工厂里面提货了,你不需要知道这个车是怎么做出来的,以及其他细节。

优点
  1. 一个调用者想创建一个对象,只要知道其名称就可以了。
  2. 扩展性高,如果想要增加一个产品,只要扩展一个工厂类就可以。
  3. 屏蔽产品的具体实现,调用者只关心接口。高度封装。
引用设计模式之禅中的讲解(包括代码)

女娲补天的故事大家都听说过吧,今天不说这个,说女娲创造人的故事,可不是“造人”的工作,这个词被现代人滥用了。这个故事是说,女娲在补了天后,下到凡间一看,哇塞,风景太优美了,天空是湛蓝的,水是清澈的,空气是清新的,太美丽了,然后就待时间长了就有点寂寞了,没有动物,这些看的到都是静态的东西呀,怎么办?
别忘了是神仙呀,没有办不到的事情,于是女娲就架起了八卦炉(技术术语:建立工厂)开始创建人,具体过程是这样的:先是泥巴捏,然后放八卦炉里烤,再扔到地上成长,但是意外总是会产生的:
第一次烤泥人,兹兹兹兹~~,感觉应该熟了,往地上一扔,biu~,一个白人诞生了,没烤熟!
第二次烤泥人,兹兹兹兹兹兹兹兹~~,上次都没烤熟,这次多烤会儿,往地上一扔,嘿,熟过头了,黑人哪!
第三次烤泥人,兹兹~,一边烤一边看着,嘿,正正好,Perfect!优品,黄色人类!【备注:RB 人不属此列】
这个过程还是比较有意思的,先看看类图:(之前在论坛上有兄弟建议加类图和源文件,以后的模式都会加上去,之前的会一个一个的补充,目的是让大家看着舒服,看着愉悦,看着就想要,就像是看色情小说一样,目标,目标而已,能不能实现就看大家给我的信心了)
在这里插入图片描述
那这个过程我们就用程序来表现,首先定义一个人类的总称:

package FactoryModel;

public interface Human {
	// 定义方法:笑,哭,说话
	public void laugh();
	public void cry();
	public void talk(); 
}

然后定义具体的人类:

  1. 白人
package FactoryModel;

/*白人的实现类*/
public class WhiteHuman implements Human {

	@Override
	public void laugh() {
		System.out.println("白色人类会笑!");
	}

	@Override
	public void cry() {
		System.out.println("白色人类会哭!");
	}

	@Override
	public void talk() {
		System.out.println("白色人类会说话!");
	}
}
  1. 黑人
package FactoryModel;

/*黑人的实现类*/
public class BlackHuman implements Human {

	@Override
	public void laugh() {
		System.out.println("黑人会笑!");
	}

	@Override
	public void cry() {
		System.out.println("黑人会哭!");
	}

	@Override
	public void talk() {
		System.out.println("黑人会说话!");
	}
}
  1. 黄种人
package FactoryModel;

/*黄种人的实现类*/
public class YellowHuman implements Human {

	@Override
	public void laugh() {
		System.out.println("黄种人会笑,开心的很!");
	}

	@Override
	public void cry() {
		System.out.println("黄种人会哭,一般不会哭!");
	}

	@Override
	public void talk() {
		System.out.println("黄种人会说话,别欺负我们,要不我们咬你!");
	}
}

人类也定义完毕了,那我们把八卦炉定义出来:

package FactoryModel;
/*八卦炉*/
public class HumanFactory {
	
	// 这个炉子只有一个功能就是:把泥巴扔进去,一个人就自动早出来了,但是得提前指定一下你要造的是黄种人还是黑人还是白人
	// 静态方法,到时候你直接可以用类名加点调用
	// 返回值:Human 类型的对象. 
	public static Human createHuman(Class c){
		Human human = null; // 先定义一个人类类型的,等会赋值给它
		
		try {
			human = (Human)Class.forName(c.getName()).newInstance();// 利用反射机制找到对应的类,new一个对象
		} catch (InstantiationException e) {
			// 实例化异常。当试图通过newInstance()方法创建某个类的实例,而该类是一个抽象类或者接口时,抛出的异常
			System.out.println("你必须指定人类的颜色");
		} catch (IllegalAccessException e) {
			// 安全权限异常,一般来说,是由于java在反射时调用了private方法导致的。把方法要改成public
			System.out.println("人类定义有错误!");
		} catch (ClassNotFoundException e) {
			System.out.println("混蛋,你指定的人类没有找到???");
		}
		
		return human;
	}
}

然后我们再把女娲声明出来:

package FactoryModel;

/*女娲类*/
public class NvWa {

	public static void main(String[] args) {
		// 第一次造人:白人
		System.out.println("-----------1------------");
		Human human1 = HumanFactory.createHuman(WhiteHuman.class); // 这里的参数一定要与后台的类名相同,要不然反射找不到这个类
		human1.laugh();
		human1.cry();
		human1.talk();
		
		// 第二次造人:黑人
		System.out.println("-----------2------------");
		Human human2 = HumanFactory.createHuman(BlackHuman.class);// 这里的参数一定要与后台的类名相同,要不然反射找不到这个类
		human2.laugh();
		human2.cry();
		human2.talk();
		
		// 第二次造人:黑人
		System.out.println("-----------3------------");
		Human human3 = HumanFactory.createHuman(YellowHuman.class);// 这里的参数一定要与后台的类名相同,要不然反射找不到这个类
		human3.laugh();
		human3.cry();
		human3.talk();
	}
}

运行结果:
在这里插入图片描述
这样这个世界就热闹起来了,人也有了,但是这样创建太累了,神仙也会累的,那怎么办?神仙就想了:我塞进去一团泥巴,随机出来一群人,管他是黑人、白人、黄人,只要是人就成(你看看,神仙都偷懒,何况是我们人),先修改类图:
在这里插入图片描述
从类图上我们可以看出来,HumanFactory中新增了一个方法:CreateHuman(),这个方法没有任何参数,只要调它就可以随机的创建一个人出来,什么肤色的都有可能。下面看看代码的修改:

package FactoryModel;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/*八卦炉*/
public class HumanFactory {
	
	// 这个炉子只有一个功能就是:把泥巴扔进去,一个人就自动早出来了,但是得提前指定一下你要造的是黄种人还是黑人还是白人
	// 静态方法,到时候你直接可以用类名加点调用
	// 返回值:Human 类型的对象. 
	public static Human createHuman(Class c){
		Human human = null; // 先定义一个人类类型的,等会赋值给它
		
		try {
			human = (Human)Class.forName(c.getName()).newInstance(); // 利用反射机制找到对应的类,new一个对象
		} catch (InstantiationException e) {
			// 实例化异常。当试图通过newInstance()方法创建某个类的实例,而该类是一个抽象类或者接口时,抛出的异常
			System.out.println("你必须指定人类的颜色");
		} catch (IllegalAccessException e) {
			// 安全权限异常,一般来说,是由于java在反射时调用了private方法导致的。把方法要改成public
			System.out.println("人类定义有错误!");
		} catch (ClassNotFoundException e) {
			System.out.println("混蛋,你指定的人类没有找到???");
		}
		
		return human;
	}
	
	// 女娲生气了,现在不管了,就直接把泥巴扔进去,爱产生啥就产生吧!
	public static Human createHuman(){
		Human human = null;
		// 首先获得所有的Human的实现类,其中用到的ClassUtils是一个工具类,等会展示代码
		List<Class> humanList = ClassUtils.getAllClassByInterface(Human.class);
		//随机少一个人出来
		Random random = new Random();
		int rand = random.nextInt(humanList.size()); // 产生一个随机数
		
		human = createHuman(humanList.get(rand)); // 调用上面的创造方法
		
		return human;
	}	
}

女娲的改变:

package FactoryModel;
/*女娲类*/
public class NvWa {
	public static void main(String[] args) {
		// 第一次造人:白人
		System.out.println("-----------1------------");
		Human human1 = HumanFactory.createHuman(WhiteHuman.class); // 这里的参数一定要与后台的类名相同,要不然反射找不到这个类
		human1.laugh();
		human1.cry();
		human1.talk();
		
		// 第二次造人:黑人
		System.out.println("-----------2------------");
		Human human2 = HumanFactory.createHuman(BlackHuman.class);// 这里的参数一定要与后台的类名相同,要不然反射找不到这个类
		human2.laugh();
		human2.cry();
		human2.talk();
		
		// 第二次造人:黑人
		System.out.println("-----------3------------");
		Human human3 = HumanFactory.createHuman(YellowHuman.class);// 这里的参数一定要与后台的类名相同,要不然反射找不到这个类
		human3.laugh();
		human3.cry();
		human3.talk();
		
		// 随机产生一百个人
		for(int i = 0; i < 100; i++){
			System.out.println("-----------" + (i+1) +"------------");
			Human human = HumanFactory.createHuman();// 这里的参数一定要与后台的类名相同,要不然反射找不到这个类
			human.laugh();
			human.cry();
			human.talk();
		}
	}
}

结果图:
在这里插入图片描述

工具类:给一个接口,返回这个接口的所有实现类
package FactoryModel;

import java.io.File; 
import java.io.IOException; 
import java.net.URL; 
import java.util.ArrayList; 
import java.util.Enumeration; 
import java.util.List; 
/**
* 工具类:给一个接口,返回这个接口的所有实现类
*/
@SuppressWarnings("all") 
public class ClassUtils { 

	public static List<Class> getAllClassByInterface(Class c){ 
		List<Class> returnClassList = new ArrayList<Class>(); //返回结果
		//如果不是一个接口,则不做处理
		if(c.isInterface()){ 
			String packageName = c.getPackage().getName(); //获得当前的包名
			try { 
				List<Class> allClass = getClasses(packageName); //获得当前包下以及子包下的所有类
				//判断是否是同一个接口
				for(int i=0;i<allClass.size();i++){ 
					if(c.isAssignableFrom(allClass.get(i))){ //判断是不是一个接口
						if(!c.equals(allClass.get(i))){ //本身不加进去
							returnClassList.add(allClass.get(i)); 
						} 
					} 
				} 
			} catch (ClassNotFoundException e) { 
				e.printStackTrace(); 
			} catch (IOException e) { 
				e.printStackTrace(); 
			}
		} 
		return returnClassList; 
	} 
	//从一个包中查找出所有的类,在jar包中不能查找
	private static List<Class> getClasses(String packageName) throws ClassNotFoundException, IOException { 
		ClassLoader classLoader = Thread.currentThread() .getContextClassLoader(); 
		String path = packageName.replace('.', '/'); 
		Enumeration<URL> resources = classLoader.getResources(path); 
		List<File> dirs = new ArrayList<File>(); 
		while (resources.hasMoreElements()) { 
			URL resource = resources.nextElement(); 
			dirs.add(new File(resource.getFile())); 
		} 
		ArrayList<Class> classes = new ArrayList<Class>(); 
		for (File directory : dirs) { 
			classes.addAll(findClasses(directory, packageName)); 
		} 
		return classes; 
	} 
	private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException { 
		List<Class> classes = new ArrayList<Class>(); 
		if (!directory.exists()) { 
			return classes; 
		} 
		File[] files = directory.listFiles(); 
		for (File file : files) { 
			if (file.isDirectory()) { 
				assert !file.getName().contains("."); 
				classes.addAll(findClasses(file, packageName + "." + file.getName())); 
			} else if (file.getName().endsWith(".class")) { 
				classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6))); 
			} 
		} 
		return classes; 
	}
}

工厂方法模式还有一个非常重要的应用,就是延迟始化(Lazy initialization),什么是延迟始化呢?
一个对象初始化完毕后就不释放,等到再次用到得就不用再次初始化了,直接从内存过中拿到就可以了,怎么实现呢,很简单,看例子:

package FactoryModel;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;

/*八卦炉*/
public class HumanFactory {
	
	// 加一个map来存放之前创建过的对象
	private static HashMap<String,Human> humans = new HashMap<>();
	
	// 这个炉子只有一个功能就是:把泥巴扔进去,一个人就自动早出来了,但是得提前指定一下你要造的是黄种人还是黑人还是白人
	// 静态方法,到时候你直接可以用类名加点调用
	// 返回值:Human 类型的对象. 
	public static Human createHuman(Class c){
		Human human = null; // 先定义一个人类类型的,等会赋值给它
		
		try {
			if(humans.containsKey(c.getSimpleName())){ // c.getSimpleName :返回源代码中给出的底层类的简称。
				human = humans.get(c.getSimpleName()); // 如果之前创建过,就直接拿出来用
			}else {
				human = (Human)Class.forName(c.getName()).newInstance(); // 利用反射机制找到对应的类,new一个对象
				humans.put(c.getSimpleName(), human); // 放入map中
			}
		} catch (InstantiationException e) {
			// 实例化异常。当试图通过newInstance()方法创建某个类的实例,而该类是一个抽象类或者接口时,抛出的异常
			System.out.println("你必须指定人类的颜色");
		} catch (IllegalAccessException e) {
			// 安全权限异常,一般来说,是由于java在反射时调用了private方法导致的。把方法要改成public
			System.out.println("人类定义有错误!");
		} catch (ClassNotFoundException e) {
			System.out.println("混蛋,你指定的人类没有找到???");
		}
		
		return human;
	}
	
	// 女娲生气了,现在不管了,就直接把泥巴扔进去,爱产生啥就产生吧!
	public static Human createHuman(){
		Human human = null;
		// 首先获得所有的Human的实现类,其中用到的ClassUtils是一个工具类,等会展示代码
		List<Class> humanList = ClassUtils.getAllClassByInterface(Human.class);
		//随机少一个人出来
		Random random = new Random();
		int rand = random.nextInt(humanList.size()); // 产生一个随机数
		
		human = createHuman(humanList.get(rand)); // 调用上面的创造方法

		return human;
	}
}

这个在类初始化很消耗资源的情况比较实用,比如你要连接硬件,或者是为了初始化一个类需要准备比较多条件(参数),通过这种方式可以很好的减少项目的复杂程度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值