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