以前学C++时,搞课程设计用简单工厂模式做过《图形工厂》:
C++_图形工厂
我们说:主方法实际上相当于客户端,如果此时需要更换一个子类,则必须修改主方法,但这是我们要避免了!
怎么避免呢?
我们可以模仿JVM工作原理:所有程序只认JVM这个头,每个JVM会根据所在的操作系统不同自动匹配。也就是说:形成了“程序->JVM->操作系统”的结构。在接口与具体子类之间加入一个过渡端,通过此过渡端取得接口实例。我们把它亲切地叫着--工厂类!
在把反射应用到工厂模式之前,我们还是从简单工厂这个头说起。然后再比较把反射应用后的优点在哪里?有比较才有好坏!
工厂设计模式例子--“吃水果”
interface Fruit{ // 定义一个水果接口
public void eat() ; // 吃水果
}
class Apple implements Fruit{
public void eat(){
System.out.println("** 吃苹果。") ;
}
};
class Orange implements Fruit{
public void eat(){
System.out.println("** 吃橘子。") ;
}
};
class Factory{ // 定义工厂类
public static Fruit getInstance(String className){
Fruit f = null ;
if("apple".equals(className)){ // 判断是否要的是苹果的子类
f = new Apple() ;
}
if("orange".equals(className)){ // 注意顺序:字符串常量要写在前面
f = new Orange() ;
}
return f ;
}
};
public class InterfaceCaseDemo05{
public static void main(String args[]){
Fruit f = Factory.getInstance(args[0]) ; // 实例化接口
if(f!=null){ // 判断是否取得实例
f.eat() ;
}
}
};
程序运行结果:
java InterfaceCaseDemo05 apple
** 吃苹果。
java InterfaceCaseDemo05 orange
** 吃橘子。
进化:
通过以上代码和流程图我们知道了简单的工厂模式可以达到类的解耦合目的,但依然存在问题:那就是在增加一个子类时都 需要修改工厂类,这可麻烦了。
那就让我们应用反射机制上场来拯救之。
package zz.factorydemo;
interface Fruit{ // 水果接口
public void eat(); // 吃水果的方法
}
class Apple implements Fruit{ // 定义苹果
public void eat(){
System.out.println("**吃苹果!"); // 覆写抽象方法
}
}
class Orange implements Fruit{
public void eat(){
System.out.println("**吃橘子!");
}
}
class Factory {
public static Fruit getInstance(String className){
Fruit fruit = null; // 取得接口实例
try{ // 实例化对象
fruit = (Fruit)Class.forName(className).newInstance();
}catch (Exception e){
e.printStackTrace();
}
return fruit;
}
}
public class FactoryDemo01{
// 通过工厂类取得接口实例,传入完整的包.类名称
public static void main(String []args){
Fruit f = Factory.getInstance("zz.facotrydemo.Apple");
if (f != null){ // 判断是否取得接口实例
f.eat(); // 调用方法
}
}
}
程序运行结果
**吃苹果!
再进化:
但我们注意到操作时需要传入完整的包.类名称,而且用户无法知道一个接口有多少个可以使用的子类,所以我们可以找属性文件来帮忙,让他帮我们配置所要的子类信息。
属性文件fruit.properties内容:
apple = zz.factorydemo.Apple
orange = zz.factorydemo.Orange
package zz.factorydemo;
import java.util.Properties ;
import java.io.File ;
import java.io.FileOutputStream ;
import java.io.FileInputStream ;
interface Fruit{
public void eat() ; // 吃水果
}
class Apple implements Fruit{
public void eat(){ // 覆写eat()方法
System.out.println("** 吃苹果");
}
};
class Orange implements Fruit{
public void eat(){
System.out.println("** 吃橘子") ;
}
};
class Init{ // 定义初始化操作类
public static Properties getPro(){
Properties pro = new Properties() ; // 实例化属性类
File f = new File("d:\\fruit.properties") ; // 找到属性文件
try{
if(f.exists()){ // 文件存在
pro.load(new FileInputStream(f)) ; // 读取属性
}else{ // 建立一个新的属性类,同时设置好默认内容
pro.setProperty("apple","org.lxh.demo15.factorydemo02.Apple") ;
pro.setProperty("orange","org.lxh.demo15.factorydemo02.Orange") ;
pro.store(new FileOutputStream(f),"FRUIT CLASS") ;
}
}catch(Exception e){}
return pro ;
}
};
class Factory{
public static Fruit getInstance(String className){
Fruit fruit = null ;
try{
fruit = (Fruit)Class.forName(className).newInstance() ;
}catch(Exception e){
e.printStackTrace() ;
}
return fruit ;
}
};
public class FactoryDemo02{
public static void main(String args[]){
Properties pro = Init.getPro() ; // 初始化属性类
// 通过工厂类取得接口实例,通过属性的key传入完整的包.类名称
Fruit f = Factory.getInstance(pro.getProperty("apple")) ;
if(f!=null){
f.eat() ;
}
}
};
本程序很好地实现了代码与配置分离。通过配置文件配置要使用的类,之后通过程序读取属性文件,以完成具体功能。
当然最新的设计理念是:在程序中直接使用注释的方式进行配置。学习中……