【设计模式学习笔记】 之 简单工厂模式

 简介:工厂模式(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 }

输出:

打开配置文件第一行,关掉第二行

全家出游!
奔驰出行,请勿追尾
玩的愉快

打开配置文件第二行,关掉第一行

全家出游!
本田上路,请勿抛砖
玩的愉快

 

总结:

  简单工厂模式:

  1. 私有化工厂类的构造方法
  2. 根据配置文件的变化或者传参的变化,动态返回一个该参数代表的类的实例
  3. 拥有一个getInstance的方法
posted @ 2018-02-15 11:38  东北小狐狸  阅读(285)  评论(0编辑  收藏  举报