女娲補天的故事大家都聽說過吧,今天不說這個,說女娲創造人的故事,可不是“造人”的工作,這個詞被現代人濫用了。這個故事是說,女娲在補了天後,下到凡間一看,哇塞,風景太優美了,天空是湛藍的,水是清澈的,空氣是清新的,太美麗了,然後就待時間長了就有點寂寞了,沒有動物,這些看的到都是靜态的東西呀,怎麼辦?别忘了是神仙呀,沒有辦不到的事情,于是女娲就架起了八卦爐(技術術語:建立工廠)開始建立人,具體過程是這樣的:先是泥巴捏,然後放八卦爐裡烤,再扔到地上成長,但是意外總是會産生的:
第一次烤泥人,茲茲茲茲~~,感覺應該熟了,往地上一扔,biu~,一個白人誕生了,沒烤熟!
第二次烤泥人,茲茲茲茲茲茲茲茲~~,上次都沒烤熟,這次多烤會兒,往地上一扔,嘿,熟過頭了,黑人哪!
第三次烤泥人,茲~茲~茲~,一邊烤一邊看着,嘿,正正好,Perfect!優品,黃色人類!【備注:RB人不屬此列】
這個過程還是比較有意思的,先看看類圖:
那這個過程我們就用程式來表現,首先定義一個人類的總稱:
package factory.pattern;
/**
* 定義一個人類的統稱
*/
public interface Human {
//人是愉快的,會笑的,本來是想用smile表示,想了一下laugh更合适,好長時間沒有大笑了;
public void laugh();
//人類還會哭,代表痛苦
public void cry();
//人類會說話
public void talk();
}
然後定義具體的人類:
package factory.pattern;
/**
* 黃色人類,這個翻譯的不準确,将就點吧
*/
public class YellowHuman implements Human {
public void cry() {
System.out.println("黃色人類會哭");
}
public void laugh() {
System.out.println("黃色人類會大笑,幸福呀!");
}
public void talk() {
System.out.println("黃色人類會說話,一般說的都是雙位元組");
}
}
白色人類:
package factory.pattern;
/**
* 白色人類
*/
public class WhiteHuman implements Human {
public void cry() {
System.out.println("白色人類會哭");
}
public void laugh() {
System.out.println("白色人類會大笑,侵略的笑聲");
}
public void talk() {
System.out.println("白色人類會說話,一般都是但是單位元組!");
}
}
黑色人類:
package factory.pattern;
/**
* 黑色人類
*/
public class BlackHuman implements Human {
public void cry() {
System.out.println("黑人會哭");
}
public void laugh() {
System.out.println("黑人會笑");
}
public void talk() {
System.out.println("黑人可以說話,一般人聽不懂");
}
}
人類也定義完畢了,那我們把八卦爐定義出來:
package factory.pattern;
/**
* 今天講女娲造人的故事
*/
public class HumanFactory {
public static Human createHuman(Class c){
Human human = null; //定義一個類型的人類
try {
human = (Human)Class.forName(c.getName()).newInstance(); //産生一個人類
} catch (InstantiationException e) {//你要是不說個人類顔色的話,沒法烤,要白的黑,你說話了才好烤
System.out.println("必須指定人類的顔色");
} catch (IllegalAccessException e) { //定義的人類有問題,那就烤不出來了,這是...
System.out.println("人類定義錯誤!");
} catch (ClassNotFoundException e) { //你随便說個人類,我到哪裡給你制造去?!
System.out.println("混蛋,你指定的人類找不到!");
}
return human;
}
}
然後我們再把女娲聲明出來:
package factory.pattern;
/**
* 首先定義女娲,這真是額的神呀
*/
public class NvWa {
public static void main(String[] args) {
//女娲第一次造人,試驗性質,少造點,火候不足,缺陷産品
System.out.println("------------造出的第一批人是這樣的:白人-----------------");
Human whiteHuman = HumanFactory.createHuman(WhiteHuman.class);
whiteHuman.cry();
whiteHuman.laugh();
whiteHuman.talk();
//女娲第二次造人,火候加足點,然後又出了個次品,黑人
System.out.println("/n/n------------造出的第二批人是這樣的:黑人-----------------");
Human blackHuman = HumanFactory.createHuman(BlackHuman.class);
blackHuman.cry();
blackHuman.laugh();
blackHuman.talk();
//第三批人了,這次火候掌握的正好,黃色人類(不寫黃人,免得引起歧義),備注:RB人不屬于此列
System.out.println("/n/n------------造出的第三批人是這樣的:黃色人類-----------------");
Human yellowHuman = HumanFactory.createHuman(YellowHuman.class);
yellowHuman.cry();
yellowHuman.laugh();
yellowHuman.talk();
}
}
這樣這個世界就熱鬧起來了,人也有了,但是這樣建立太累了,神仙也會累的,那怎麼辦?神仙就想了:我塞進去一團泥巴,随機出來一群人,管他是黑人、白人、黃人,隻要是人就成(你看看,神仙都偷懶,何況是我們人),先修改類圖:
然後看我們的程式修改,先修改HumanFactory.java,增加了createHuman()方法:
//女娲生氣了,把一團泥巴塞到八卦爐,哎産生啥人類就啥人類
public static Human createHuman(){
Human human = null; //定義一個類型的人類
//首先是獲得有多少個實作類,多少個人類
List<Class> concreteHumanList = ClassUtils.getAllClassByInterface(Human.class); //定義了多少人類
//八卦爐自己開始想燒出什麼人就什麼人
Random random = new Random();
int rand = random.nextInt(concreteHumanList.size());
human = createHuman(concreteHumanList.get(rand));
return human;
}
然後看女娲是如何做的:
//女娲煩躁了,愛是啥人類就是啥人類,燒吧
for(int i=0;i<100;i++){
System.out.println("/n/n------------随機産生人類了-----------------" + i);
Human human = HumanFactory.createHuman();
human.cry();
human.laugh();
human.talk();
}
哇,這個世界熱鬧了!,不過還沒有完畢,這個程式你跑不起來,還要有這個類:
package factory.pattern;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
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;
}
}
告訴你了,這個ClassUtils可是個寶,用處可大了去了,可以由一個接口查找到所有的實作類,也可以由父類查找到所有的子類,這個要自己修改一下,動腦筋想下,簡單的很!完整的類圖如下:
我們來總結一下,特别是增加了createHuman()後,是不是這個工廠的擴充性更好了?你看你要再加一個人類,隻要你繼續內建Human接口成了,然後啥都不用修改就可以生産了,具體産多少,那要八卦爐說了算,簡單工廠模式就是這麼簡單,那我們再引入一個問題:人是有性别的呀,有男有女,你這怎麼沒差別,别急,這個且聽下回分解!
工廠方法模式還有一個非常重要的應用,就是延遲始化(Lazy initialization),什麼是延遲始化呢?一個對象初始化完畢後就不釋放,等到再次用到得就不用再次初始化了,直接從記憶體過中拿到就可以了,怎麼實作呢,很簡單,看例子:
private static HashMap<String,Human> humans = new HashMap<String,Human>();
//如果MAP中有,則直接從取出,不用初始化了
if(humans.containsKey(c.getSimpleName())){
human = humans.get(c.getSimpleName());
}else{
human = (Human)Class.forName(c.getName()).newInstance();
//放到MAP中
humans.put(c.getSimpleName(), human);
}
很簡單,就加上了黃色那部分的代碼,這個在類初始化很消耗資源的情況比較實用,比如你要連接配接硬體,或者是為了初始化一個類需要準備比較多條件(參數),通過這種方式可以很好的減少項目的複雜程度。