Java工厂模式:工厂模式就是专门负责将大量有共同接口的类实例化,而且不必事先知道每次是要实例化哪一个类的模式。它定义一个用于创建对象的接口,由子类决定实例化哪一个类。
先举个简单工厂的例子:
interface Fruit{
public void grow();//生长
public void pick();//采摘
}
class Apple implements Fruit{
public void grow(){
System.out.println("苹果在生长。。。");
}
public void pick(){
System.out.println("摘苹果。。。");
}
};
class Orange implements Fruit{
public void grow(){
System.out.println("橘子在生长。。。");
}
public void pick(){
System.out.println("摘橘子。。。");
}
};
class Factory{
public static Fruit getFruitInstance(){
return new Orange();
}
};
public class Factory01{
public static void main(String args[]){
Fruit f = Factory.getFruitInstance();
f.grow();
f.pick();
}
};
1、对于以上的工厂设计存在以下问题
如果现在客户(main)要求既可以使用Apple对象,也可以使用Orange对象则无法通过工厂完成。
以下进行改进:
interface Fruit{
public void grow();//生长
public void pick();//采摘
}
class Apple implements Fruit{
public void grow(){
System.out.println("苹果在生长。。。");
}
public void pick(){
System.out.println("摘苹果。。。");
}
};
class Orange implements Fruit{
public void grow(){
System.out.println("橘子在生长。。。");
}
public void pick(){
System.out.println("摘橘子。。。");
}
};
class Factory{
public static Fruit getFruitInstance(String type){
Fruit f = null;
if("Apple".equals(type)){
f = new Apple();
}
if("Orange".equals(type){
f = new Orange();
}
return f;
}
};
public class Factory02{
public static void main(String args[]){
if(args.length == 0){
System.out.println("必须输入一个要使用的名称");
System.exit(1);
}
Fruit f = Factory.getFruitInstance(args[0]);
f.grow();
f.pick();
}
};
2、对于以上实现还是存在以下问题:
如果输入的参数错误,则会出现空指针异常
需要对f的输入进行判断:
if(f != null){
f.grow();
f.pick();
}
else{
System.out.println("没有发现这个类型");
}
3、
如果现在扩充一个子类,则要修改工厂(工厂中如果没有子类的判断,则无法使用这个子类),这样的工厂很不方便,继续改进
interface Fruit{
public void grow();//生长
public void pick();//采摘
}
class Apple implements Fruit{
public void grow(){
System.out.println("苹果在生长。。。");
}
public void pick(){
System.out.println("摘苹果。。。");
}
};
class Orange implements Fruit{
public void grow(){
System.out.println("橘子在生长。。。");
}
public void pick(){
System.out.println("摘橘子。。。");
}
};
class Cherry implements Fruit{
public void grow(){
System.out.println("樱桃在生长。。。");
}
public void pick(){
System.out.println("摘樱桃。。。");
}
};
class Factory{
public static Fruit getFruitInstance(String type){
Fruit f = null;
//通过Class类完成
try{
f = (Fruit)Class.forName(type).newInstance();
}catch (Exception e){
System.out.println(e) ;
}
return f;
}
};
public class Factory02{
public static void main(String args[]){
if(args.length == 0){
System.out.println("必须输入一个要使用的名称");
System.exit(1);
}
Fruit f = Factory.getFruitInstance(args[0]);
if(f != null){
f.grow();
f.pick();
}
else{
System.out.println("没有发现这个类型");
}
}
};
4、解决了工厂由于增加子类需要的问题
一个新的问题:在项目中可能有几十个类同时实现一个接口,那么此用户如何知道已有的接口子类呢?
代号 -> 子类的包类名称(key -> value)
要有一个文件列表,给用户列出全部的代码 -> 子类的映射
import java.io.* ;
import java.util.* ;
interface Fruit{
public void grow();//生长
public void pick();//采摘
}
class Apple implements Fruit{
public void grow(){
System.out.println("苹果在生长。。。");
}
public void pick(){
System.out.println("摘苹果。。。");
}
};
class Orange implements Fruit{
public void grow(){
System.out.println("橘子在生长。。。");
}
public void pick(){
System.out.println("摘橘子。。。");
}
};
class Cherry implements Fruit{
public void grow(){
System.out.println("樱桃在生长。。。");
}
public void pick(){
System.out.println("摘樱桃。。。");
}
};
class Banana implements Fruit{
public void grow(){
System.out.println("香蕉在生长。。。 ") ;
}
public void pick(){
System.out.println("摘香蕉。。。") ;
}
};
// 定义一个新类,此类可以从键盘输入数据
class InputData{
BufferedReader buf = null ;
public InputData(){
buf = new BufferedReader(new InputStreamReader(System.in)) ;
}
public String getString(){
String str = null ;
try{
str = buf.readLine() ;
}catch (Exception e){
System.out.println(e) ;
}
return str ;
}
};
class Factory{
public static Fruit getFruitInstance(String type){
Fruit f = null;
//通过Class类完成
try{
f = (Fruit)Class.forName(type).newInstance();
}catch (Exception e){
System.out.println(e) ;
}
return f;
}
};
public class Factory02{
public static void main(String args[]){
Properties p = new Properties() ;
p.setProperty("a","Apple") ;
p.setProperty("o","Orange") ;
p.setProperty("b","Banana") ;
p.setProperty("c","Cherry") ;
System.out.println(p) ;
// 加入一个可以从键盘输入数据的类
System.out.print("输入要使用的子类代码:") ;
String code = new InputData().getString() ;
System.out.println(p.getProperty(code)) ;
Fruit f = Factory.getFruitInstance(p.getProperty(code)) ;
if(f != null){
f.grow();
f.pick();
}
else{
System.out.println("没有发现这个类型");
}
}
};
5、以上实现解决了用户 无法知道全部子类的问题,同时通过Properties保存了全部的子类信息,之后通过代码进行操作。
但,程序中依然存在问题:如果程序现在圹充一个子类,则需要修改设置的属性
解决的途径,通过文件保存Properties中的内容,以后修改文件即可,而不用去修改类
import java.io.* ;
import java.util.* ;
public class Demo{
public static void main(String args[]) throws Exception{
// 需要从文件中读取要Properties中的属性
Properties p = new Properties() ;
p.loadFromXML(new FileInputStream("Tsang.xml")) ;
System.out.println(p) ;
}
};
XML文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>MLDN --> LXH</comment>
<entry key="b">Banana</entry>
<entry key="a">Apple</entry>
<entry key="o">Orange</entry>
<entry key="c">Cherry</entry>
<entry key="d">DD</entry>
</properties>
小结工厂模式的适用范围
• 在编码时不能预见需要创建哪一种类的实例。
• 一个类使用它的子类来创建对象。
• 开发人员不希望创建了哪个类的实例以及如何创建实例的信息暴露给外部程序。
• 在编码时不能预见需要创建哪一种类的实例。
• 一个类使用它的子类来创建对象。
• 开发人员不希望创建了哪个类的实例以及如何创建实例的信息暴露给外部程序。