简介:工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,拒绝客服端程序员通过new创建需要的实例,并且是通过使用一个共同的接口来指向新创建的对象,即接口引用指向实现类对象,是多态的灵活运用。
举例1【未使用工厂模式】:
一个家庭中有多辆汽车,这个家庭想去出游,需要先传一个Car到Family中持有,才能出游。
首先考虑多辆汽车,都有同样的run方法,抽取公共接口如下:
1 package com.mi.simplefactory.simplefactory1; 2 3 /** 4 * 汽车接口 5 */ 6 public interface Car { 7 8 public void run(); 9 }
现在假设有两辆汽车(多辆同理),HondaCar (本田)和 BenzCar(奔驰),都实现了Car接口
1 package com.mi.simplefactory.simplefactory1; 2 3 public class HondaCar implements Car{ 4 5 @Override 6 public void run() { 7 System.out.println("本田上路,请勿抛砖"); 8 } 9 10 11 }
1 package com.mi.simplefactory.simplefactory1; 2 3 public class BenzCar implements Car { 4 5 @Override 6 public void run() { 7 System.out.println("奔驰出行,请勿追尾"); 8 } 9 10 }
这里需要一个Family类,她应持有一个私有Car的引用,使用Family的构造方法进行初始化Car引用,Family中有一个travel方法
1 package com.mi.simplefactory.simplefactory1; 2 3 public class Family { 4 5 private Car car; 6 7 public Family(Car car) { 8 this.car = car; 9 } 10 public Family() {} 11 public void travel() { 12 System.out.println("全家出游!"); 13 car.run(); 14 System.out.println("玩的愉快"); 15 } 16 }
编写测试类,发现需要通过new 不同的Car实现类实例就可以改变汽车实际类型
1 package com.mi.simplefactory.simplefactory1; 2 3 public class Test { 4 5 public static void main(String[] args) { 6 /** 7 * 问题来了:family换个车必须要new不同的车,必须要修改代码,重新编译才能 8 * 正确更换汽车,还有,可不可以不用new的方式获取汽车? 9 */ 10 // Family family = new Family(new HondaCar()); 11 Family family = new Family(new BenzCar()); 12 family.travel(); 13 } 14 }
输出:
先注掉11行,解开10行,运行
全家出游!
本田上路,请勿抛砖
玩的愉快
注掉10,解开11,运行
全家出游!
奔驰出行,请勿追尾
玩的愉快
举例2【分支判断实现工厂】:
这里Car接口和Car接口的实现类不变,重写一个Family类,为car引用设置get set方法,去掉有参构造方法
1 package com.mi.simplefactory.simplefactory2; 2 3 import com.mi.simplefactory.simplefactory1.Car; 4 5 public class Family { 6 7 private Car car; 8 9 public Family() {} 10 public void travel() { 11 System.out.println("全家出游!"); 12 car.run(); 13 System.out.println("玩的愉快"); 14 } 15 public Car getCar() { 16 return car; 17 } 18 public void setCar(Car car) { 19 this.car = car; 20 } 21 22 23 }
创建一个工厂,通过获取汽车的类名为参数,内部if分支判断,实例化内部持有的car引用,还有有一个静态方法getInstance(),获取该 Car的实例
1 package com.mi.simplefactory.simplefactory2; 2 3 import com.mi.simplefactory.simplefactory1.BenzCar; 4 import com.mi.simplefactory.simplefactory1.Car; 5 import com.mi.simplefactory.simplefactory1.HondaCar; 6 7 public class Factory { 8 9 private Car car; 10 public Factory(String carName) { 11 if(carName.equals("BenzCar")) { 12 this.car = new BenzCar(); 13 } 14 if(carName.equals("HondaCar")) { 15 this.car = new HondaCar(); 16 } 17 } 18 public Car getInstance() { 19 return car; 20 } 21 22 }
测试类
1 package com.mi.simplefactory.simplefactory2; 2 3 import com.mi.simplefactory.simplefactory1.Car; 4 5 public class Test { 6 7 public static void main(String[] args) { 8 /** 9 * 此包中的factory可以不使用new的方式获取汽车 10 */ 11 Family family = new Family(); 12 // Factory factory = new Factory("HondaCar"); 13 Factory factory = new Factory("BenzCar"); 14 Car car = factory.getInstance(); 15 family.setCar(car); 16 family.travel(); 17 } 18 19 }
输出就不贴出来了,和之前的输出是一样的。
这个例子,我们的确通过一个工厂去得到我们需要的实例,但是,如果汽车特别的多,我们是不是需要写无数个分支判断啊?代码很冗长,效率不高,于是我们想到了通过反射来实现,于是有了第三个例子
举例3【通过反射方式】:
学过反射的同学都应该明白,我们可以通过一个properties配置文件去动态改变代码中产生的代码,这是种可以不改变代码的情况下使用的很方便的方式
创建一个properties配置文件:config.properties,(这里直接写好了两个,方便切换)
#Car=com.mi.simplefactory.simplefactory1.BenzCar
Car=com.mi.simplefactory.simplefactory1.HondaCar
创建Factory类
1 package com.mi.simplefactory.simplefactory3; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.util.Properties; 6 7 import com.mi.simplefactory.simplefactory1.Car; 8 9 public class Factory { 10 11 private Factory() {} //拒绝客服端程序员new工厂对象,仅提供静态方式访问 12 public static Properties configs = new Properties(); 13 static { 14 //获取配置文件流 15 InputStream in = Factory.class.getResourceAsStream("config.properties"); 16 try { 17 //properties读取文件输入流 18 configs.load(in); 19 } catch (IOException e) { 20 e.printStackTrace(); 21 //静态方法/块中的异常是捕获不到的 22 throw new RuntimeException(e); 23 } 24 } 25 public static Car getInstance() throws ClassNotFoundException, InstantiationException, IllegalAccessException { 26 //从配置文件中取出Car键的值:汽车类的全路径 27 String carName = configs.getProperty("Car"); 28 //反射获取Class对象 29 Class<?> clazz = Class.forName(carName); 30 //反射新建实例 31 Car car = (Car)clazz.newInstance(); 32 return car; 33 } 34 35 }
以上代码是在这个Factory类被ClassLoader load的时候自动读取配置文件,并在配置文件中get(key)的方式获得key对应的value,查看Java docs文档可以看出Properties类是一种Map的实现,通过forName反射出我们需要的类的Class对象(在文件中查找我们需要的 类.class 文件),通过newInstance()方法获取该类的实例,相当于new,这样我们就能动态的通过修改配置文件,而无需修改代码,无需重新编译即可改变代码中的对象。
下边是测试类
1 package com.mi.simplefactory.simplefactory3; 2 3 import com.mi.simplefactory.simplefactory1.Car; 4 import com.mi.simplefactory.simplefactory2.Family; 5 6 public class Test { 7 8 public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { 9 Family family = new Family(); 10 Car car = Factory.getInstance(); 11 family.setCar(car); 12 family.travel(); 13 } 14 }
输出:
打开配置文件第一行,关掉第二行
全家出游!
奔驰出行,请勿追尾
玩的愉快
打开配置文件第二行,关掉第一行
全家出游!
本田上路,请勿抛砖
玩的愉快
总结:
简单工厂模式:
- 私有化工厂类的构造方法
- 根据配置文件的变化或者传参的变化,动态返回一个该参数代表的类的实例
- 拥有一个getInstance的方法