简单工厂模式

【问题引入】

初始情况: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类实现Output
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();
    }

}

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. 简单工厂模式的缺点有哪些?

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值