为了避免上面的问题发生,工厂模式建议让Computer类组合一个Output类型的对象,将Computer类与Printer类完全分离。Computer对象实际组合的是Printer对象还是BetterPrinter对象,对Computer而言完全透明。当Printer对象切换到BetterPrinter时,系统完全不受影响。代码如下:
public class Computer { private Output out; public Computer(Output out) { this.out = out; } // 定义一个模拟获取字符串输入的方法 public void keyIn(String msg) { out.getData(msg); } // 定义一个模拟打印的方法 public void print() { out.out(); } } 上面的Computer类已经完全与Printer分离,只是与Output接口耦合。Computer不再负责创建Output对象,系统提供一个Output工厂来负责生成Output对象。这个OutputFactory工厂类代码如下: public class OutputFactory { public Output getOutput() { return new Printer(); } public static void main(String[] args) { OutputFactory of = new OutputFactory(); Computer c = new Computer(of.getOutput()); c.keyIn("轻量级Java EE企业应用实战"); c.keyIn("疯狂Java讲义"); c.print(); } } 在该OutputFactory类中包含了一个getOutput方法,该方法负责创建Output实例并返回,如果系统需要将Printer改为BetterPrinter实现类,只需要让BetterPrinter实现Output接口,并改变OutputFactory类中的getOutput方法即可。下面是BetterPrinter实现类的代码,BetterPrinter只是对原有的Printer进行简单修改,以模拟系统重构后的改进。
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; } } } 上面的BetterPrinter类也实现了Output接口,因此也可当成Output对象使用,于是只要把OutputFactory工厂类的getOutput方法修改即可。再次运行前面的OutputFactory程序,发现系统运行时已经改为BetterPrinter对象,而不再是原来的Printer对象。通过这种方式,即可把所有生成Output对象的逻辑集中在Output工厂类中管理,而所有需要使用Output对象的类只需与Output接口耦合,而不是与具体的实现类耦合。即是系统中有很多类使用了Printer对象,只要OutputFactory类的getOutput方法生的是BetterPrinter对象,则他们全部都会改为使用BetterPrinter对象,而所有程序无需修改每只需要修改OutputFactory工厂类的getOutput方法实现即可。某个方法需要完成某一个行为,但这个行为的具体实现无法确定,必须等到执行该方法时才可以确定。具体一点:假设有个方法需要遍历某个数组的数组元素,但无法确定在遍历时如何处理这些元素,需要在调用该方法时指定具体的处理行为。 这个要求看起来有点奇怪:这个方法不仅需要普通数据可以变化,甚至还有方法执行提也需要变化,难道需要把“处理行为”作为参数传入该方法? 对于这样一个需求,可以考虑使用一个Command接口来定义一个方法,用这个方法来封装“处理行为”。下面是Command接口的代码:
public interface Command { // 接口里定义的process方法用于封装“处理行为” void process(int[] target); }上面的process方法没有方法体,因为还无法确定这个处理行为。 下面是需要处理数组的处理类,在这个处理类中包含一个process方法,这个方法无法确定处理数组的处理行为,所以使用了一个Command参数,这个参数负责对数组的处理行为。
public class ProcessArray { public void process(int[] target, Command cmd) { cmd.process(target); } }通过一个Command接口,就实现了让ProcessArray和具体处理行为的分离,程序使用Command接口代表类对数组的处理行为。Command接口也没有提供真正的处理,只有等待需要调用ProcessArray对象的process方法是,才真正传入一个Command对象,才确定对数组的处理行为。 下面程序实现了对数组的两种处理方式:
public class CommandTest { public static void main(String[] args) { ProcessArray pa = new ProcessArray(); int[] target = { 3, -4, 6, 4 }; // 第一次处理数组,具体处理行为取决于PrintCommand pa.process(target, new PrintCommand()); System.out.println("------------------"); // 第二次处理数组,具体处理行为取决于AddCommand pa.process(target, new AddCommand()); } } public class PrintCommand implements Command { public void process(int[] target) { for (int tmp : target) { System.out.println("迭代输出目标数组的元素:" + tmp); } } } public class AddCommand implements Command { public void process(int[] target) { int sum = 0; for (int tmp : target) { sum += tmp; } System.out.println("数组元素的总和是:" + sum); } }