简单工厂模式

来源:互联网 发布:淘宝实时交易量 编辑:程序博客网 时间:2024/06/11 09:54

【问题引入】

初始情况:A 类实例与 B 类实例通过硬编码耦合(即 A 类实例中的方法直接使用 new 关键字创建 B 类实例

系统重构要求:使用 C 类实例来代替系统中的 B 类实例

问题:如果系统中有100个或者10000个使用 B 类实例的地方,是否只能通过逐个的将 B 类实例修改为 C 类的实例来完成系统的重构呢?有没有更好的解决方案?

【解决方案】

考虑让 B 类实现一个 IB 接口,而 A 类只需要和 IB 接口耦合——A 类个并不直接使用 new 关键字来创建 B 类实例,而是重新定义一个工厂类:IBFactory, 由该工厂类负责创建 IB 实例;而 A 类通过调用 IBFactory 工厂的方法类得到 IB 的实例。

【应用场景 A —— Computer 与 Outpet 实现类的解耦】

1. 具体问题

假设程序中有个 Computer 对象需要依赖一个输出设备,现在有两个选择:直接让 Computer 对象依赖一个 Printer 对象(实现类),或者让 Computer 对象依赖一个 Output 属性(接口),选用哪种方式更好?为什么?如果系统需要重构,即替换系统中的 Printer 对象为其他对象,应该怎样设计重构方案?

2. 方案设计

为使系统具有更好的可维护性和可扩展性,一般选用工厂模式。根据简单工厂设计模式,程序应该让 Computer 依赖一个 Output 属性,将 Computer 类与 Printer 实现类分离开来。Computer 对象只需面向 Output 接口编程即可;而 Computer 具体依赖于 Output 的那个实现类则完全透明。

3. 代码实现

Computer.java

public class Computer {    private Output out;    // 通过构造器注入方式实现 Computer 类与 Output 接口的耦合    public Computer(Output out) {        this.out = out;    }    // 定义一个模拟获取字符串输入的方法    public void keyIn(String msg) {        out.getData(msg);    }    // 定义一个模拟打印的方法    public void print() {        out.out();    }    public static void main(String[] args) {        // 创建OutputFactory        OutputFactory of = new OutputFactory();        // 将Output对象传入,创建Computer对象        Computer c = new Computer(of.getOutput());        c.keyIn("轻量级Java EE企业应用实战");        c.keyIn("疯狂Java讲义");        c.print();    }}

Output.java

public interface Output {    // 接口里定义的属性只能是常量    public static final int MAX_CACHE_LINE = 50;    // 接口里定义的只能是public的抽象实例方法    public void out();    public void getData(String msg);}

Printer.java

// 让Printer类实现Outputpublic class Printer implements Output {    private String[] printData = new String[MAX_CACHE_LINE];    // 用以记录当前需打印的作业数    private int dataNum = 0;    public void out() {        // 只要还有作业,继续打印        while (dataNum > 0) {            System.out.println("打印机打印:" + printData[0]);            // 把作业队列整体前移一位,并将剩下的作业数减1            System.arraycopy(printData, 1, printData, 0, --dataNum);        }    }    public void getData(String msg) {        if (dataNum >= MAX_CACHE_LINE) {            System.out.println("输出队列已满,添加失败");        } else {            // 把打印数据添加到队列里,已保存数据的数量加1。            printData[dataNum++] = msg;        }    }}

OutputFactory.java

public class OutputFactory {    public Output getOutput() {        // 通过修改下面一行代码可以控制系统到底使用Output的哪个实现类。        return new Printer();    }}

4. 代码分析

Computer——场景类,模拟具体实现场景
Output——接口,抽象输出设备的共有属性和方法
OutputFactory——工厂类,完成 Output 实现类的创建过程,实现 Computer 类与 Output 实现类的解耦
Printer——Output 实现类,实现输出设备的具体属性和方法

5. 系统重构

要求:使用 BetterPrinter 类来代替 Printer 类
解决:只需让 BetterPrinter 类实现 Output 接口,并改写 OutputFactory 类的 getOutput() 方法即可

6. 系统重构源码

Computer.java

public class Computer {    private Output out;    // 通过构造器注入方式实现 Computer 类与 Output 接口的耦合    public Computer(Output out) {        this.out = out;    }    // 定义一个模拟获取字符串输入的方法    public void keyIn(String msg) {        out.getData(msg);    }    // 定义一个模拟打印的方法    public void print() {        out.out();    }    public static void main(String[] args) {        // 创建OutputFactory        OutputFactory of = new OutputFactory();        // 将Output对象传入,创建Computer对象        Computer c = new Computer(of.getOutput());        c.keyIn("轻量级Java EE企业应用实战");        c.keyIn("疯狂Java讲义");        c.print();    }}

Output.java

public interface Output {    // 接口里定义的属性只能是常量    public static final int MAX_CACHE_LINE = 50;    // 接口里定义的只能是public的抽象实例方法    public void out();    public void getData(String msg);}

BetterPrinter.java

public class BetterPrinter implements Output {    private String[] printData = new String[MAX_CACHE_LINE * 2];    // 用以记录当前需打印的作业数    private int dataNum = 0;    public void out() {        // 只要还有作业,继续打印        while (dataNum > 0) {            System.out.println("高速打印机正在打印:" + printData[0]);            // 把作业队列整体前移一位,并将剩下的作业数减1            System.arraycopy(printData, 1, printData, 0, --dataNum);        }    }    public void getData(String msg) {        if (dataNum >= MAX_CACHE_LINE * 2) {            System.out.println("输出队列已满,添加失败");        } else {            // 把打印数据添加到队列里,已保存数据的数量加1。            printData[dataNum++] = msg;        }    }}

OutputFactory.java

public class OutputFactory {    public Output getOutput() {        // 通过修改下面一行代码可以控制系统到底使用Output的哪个实现类。        return new Printer();    }}

7. 系统重构源码分析

Computer——场景类,模拟具体实现场景
Output——接口,抽象输出设备的共有属性和方法
OutputFactory——工厂类,完成 Output 实现类的创建过程,实现 Computer 类与 Output 实现类的解耦
BetterPrinter——Output 实现类,实现输出设备的具体属性和方法,用于替换系统中的 Printer 对象

8. 代码问题整理

①java.lang.System 类 public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 方法的作用是什么?

public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
作用:从源数组 src 中复制一个数组到目标数组 dest 中。原数组起始位置为 srcPos,目标数组起始位置为 destPos,复制长度为 length

②Computer.java类中问题 1 ?

public class Computer {/********************************************************************///1. 以下代码是否可以重写写入一个新的类中?    private Output out;    // 通过构造器注入方式实现 Computer 类与 Output 接口的耦合    public Computer(Output out) {        this.out = out;    }    // 定义一个模拟获取字符串输入的方法    public void keyIn(String msg) {        out.getData(msg);    }    // 定义一个模拟打印的方法    public void print() {        out.out();    }/********************************************************************/    public static void main(String[] args) {        // 创建OutputFactory        OutputFactory of = new OutputFactory();        // 将Output对象传入,创建Computer对象        Computer c = new Computer(of.getOutput());        c.keyIn("轻量级Java EE企业应用实战");        c.keyIn("疯狂Java讲义");        c.print();    }}

该问题的解决方案如下:

Client.java

/** * 场景类——用于测试简单工厂模式 */public class Client {    public static void main(String[] args) {        // 获取OutputFactory        OutputFactory of = new OutputFactory();        // 获取Output        Output output = of.getOutput();        System.out.println("打印机" + output.getClass().getName() + "正在工作");        // 将Output对象传入,创建Computer对象        Computer c = new PersonComputer(output);        c.keyIn("轻量级Java EE企业应用实战");        c.keyIn("疯狂Java讲义");        c.keyIn("设计模式之禅");        c.print();    }}

Computer.java

public interface Computer {    public void keyIn(String msg);    public void print();}

PersonComputer.java

/** * 调用者 * */public class PersonComputer implements Computer {    private Output out;    // 通过构造器注入方式实现 Computer 类与 Output 接口的耦合    public PersonComputer(Output out) {        this.out = out;    }    // 定义一个模拟获取字符串输入的方法    public void keyIn(String msg) {        out.getData(msg);    }    // 定义一个模拟打印的方法    public void print() {        out.out();    }}

Output.java

/** * 输出接口——定义输出设备的通用属性和方法 * */public interface Output {    // 接口里定义的属性只能是常量    public static final int MAX_CACHE_LINE = 50;    // 接口里定义的只能是public的抽象实例方法    public void out();    public void getData(String msg);}

Printer.java

/** * 被调用者 *  * 接口的实现类——接口中定义的方法和属性的具体实现 * */public class Printer implements Output {    private String[] printData = new String[MAX_CACHE_LINE];    // 用以记录当前需打印的作业数    private int dataNum = 0;    public void out() {        // 只要还有作业,继续打印        while (dataNum > 0) {            System.out.println("打印机打印:" + printData[0]);            // 把作业队列整体前移一位,并将剩下的作业数减1            System.arraycopy(printData, 1, printData, 0, --dataNum);        }    }    public void getData(String msg) {        if (dataNum >= MAX_CACHE_LINE) {            System.out.println("输出队列已满,添加失败");        } else {            // 把打印数据添加到队列里,已保存数据的数量加1。            printData[dataNum++] = msg;        }    }}

OutputFactory.java

/** * 工厂——实现解耦 * */public class OutputFactory {    public Output getOutput() {        // 通过修改下面一行代码可以控制系统到底使用Output的哪个实现类。        return new Printer();//      return new BetterPrinter();    }}

【相关问题】

1. 简单工厂模式使用的三个要求:

① 调用者面向被依赖对象的接口编程;
② 将被依赖对象的创建交给接口完成;
③ 调用者通过工厂来获得被依赖组件;

2. 简单工厂模式的优点有哪些?

① 让对象的调用者和对象的创建过程分离,当对象调用者需要对象时,直接从工厂获取;
② 避免了对象的调用者与对象的实现类以硬编码的方式耦合,以提高系统的可维护性、可扩展性;

3. 简单工厂模式的缺点有哪些?

当产品修改时,工厂类必须做出相应的修改

0 0
原创粉丝点击