1. 工厂模式的作用:解决接口选择的问题
2. 何时使用:当我们明确地知道不同条件下创建不同实例时。
3. 使用场景:
--java的可移植性(java--JVM(工厂)--操作系统)
--hibernate和mybatis切换数据库
4. 注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。
分析一下程序
package test;
public class test {
public static void main(String[] args) {
Apple apple = new Apple();
apple.eat();
//如果突然不想吃苹果,想吃香蕉就要重新实例化一个香蕉对象
//如此一来,每次切换都要更新客户端的代码
//能不能有一种方式,用户想吃什么就传入什么,不需要改动客户端的代码
}
}
class Apple{
public void eat() {
System.out.println("吃苹果。。。");
}
}
class Banana{
public void eat() {
System.out.println("吃香蕉。。。");
}
}
5. 改进,引入工厂类
package test;
public class test {
public static void main(String[] args) {
Fruit fruit = Factory.getInstance(args[0]);
fruit.eat();
}
}
abstract class Fruit{ //模板类
public void eat(){} ;
}
class Apple extends Fruit{
public void eat() {
System.out.println("吃苹果。。。");
}
}
class Banana extends Fruit{
public void eat() {
System.out.println("吃香蕉。。。");
}
}
interface Factory{ //工厂类
public static Fruit getInstance(String fruit) {
if("apple".equals(fruit)) {
return new Apple();
}else if("banana".equals(fruit)) {
return new Banana();
}else {
return null;
}
}
}
如此一来,只要是水果工厂里面有的水果,用户只要输入水果名称就可以吃到该水果,不需要自己再去生产该水果,生产的工作交由水果工厂负责。
但是这个代码还是有一个特别麻烦的地方,那就是每当接口产生一个新的子类的时候,接口本身也必须修改,可扩展性极低。
6. 利用反射改进工厂模式
package test;
public class TestFactory {
public static void main(String[] args) throws Exception {
Fruit instance = Factory.getInstance("test.Apple");
instance.eat();
}
}
interface Fruit{
public void eat();
}
class Apple implements Fruit{
@Override
public void eat() {
System.out.println("吃苹果...");
}
}
class Banana implements Fruit{
@Override
public void eat() {
System.out.println("吃香蕉....");
}
}
class Factory{
public static Fruit getInstance(String fruitClass) throws Exception {
return (Fruit)Class.forName(fruitClass).newInstance(); //一句代码搞定,以后有新的子类也不需要再修改工厂类了
}
}
但是这样还是不够完美,因为客户端需要明确知道子类的详细名称(包.类名),并且每次切换子类都要改动客户端,改进的方法是利用配置文件,将类名写进配置文件中,切换的时候,全部代码都不需要改动,只要改动配置文件就可以了。
7. 利用配置温文件管理工程类名
import java.util.ResourceBundle;
public class TestFactory {
public static final ResourceBundle rb = ResourceBundle.getBundle("fruit");//使用配置文件
public static void main(String[] args) throws Exception {
Fruit instance = Factory.getInstance(rb.getString("fruitClass"));
instance.eat();
}
}
interface Fruit{
public void eat();
}
class Apple implements Fruit{
@Override
public void eat() {
System.out.println("吃苹果...");
}
}
class Banana implements Fruit{
@Override
public void eat() {
System.out.println("吃香蕉....");
}
}
class Factory{
public static Fruit getInstance(String fruitClass) throws Exception {
return (Fruit)Class.forName(fruitClass).newInstance(); //一句代码搞定,以后有新的子类也不需要再修改工厂类了
}
}
上面的代码仍不完美,不完美之处在于需要用户去手工修改配置文件。所以必须想一种办法,把配置写回配置中,又可以跟程序进行有效的分离。参考servlet开发的经验,使用Annotation来实现配置。
8. 利用annotation改进工厂模式
package factory;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Factory(className = "factory.Apple")
public class TestFactory3{
public static void main(String[] args) {
Fruit fruit;
try {
fruit = getFruit();
fruit.eat();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static Fruit getFruit() throws Exception {
Class<?> cls = TestFactory3.class;
Factory an = cls.getAnnotation(Factory.class);//取得工厂annotation
return (Fruit)Class.forName(an.className()).newInstance(); //返回工厂实例
}
}
interface Fruit{
public void eat();
}
class Apple implements Fruit{
@Override
public void eat() {
System.out.println("吃苹果...");
}
}
class Banana implements Fruit{
@Override
public void eat() {
System.out.println("吃香蕉....");
}
}
//写自己的annotation
@Retention(RetentionPolicy.RUNTIME)
@interface Factory{
public String className() ;
}
9. 继续优化,简化客户端annotation
package factory;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Factory(className = "Banana")
public class TestFactory3{
public static void main(String[] args) {
Fruit fruit;
try {
fruit = getFruit();
fruit.eat();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static Fruit getFruit() throws Exception {
Class<?> cls = TestFactory3.class;
Factory an = cls.getAnnotation(Factory.class);//取得工厂annotation
return (Fruit)Class.forName(Fruit.class.getPackage().getName()+"."+an.className()).newInstance(); //返回工厂实例
}
}
interface Fruit{
public void eat();
}
class Apple implements Fruit{
@Override
public void eat() {
System.out.println("吃苹果...");
}
}
class Banana implements Fruit{
@Override
public void eat() {
System.out.println("吃香蕉....");
}
}
//写自己的annotation
@Retention(RetentionPolicy.RUNTIME)
@interface Factory{
public String className() ;
}