接口体现的是一种规范和实现分离的设计思想,充分利用接口可极好的降低程序各模块之间的耦合,提高系统的可扩展性和可维护性
## 简单工厂模式
import davieyang.Output;public class Computer{ private Output out; publicComputer(Output out) { this.out = out; } // 定义一个模拟获取字符串输入的方法 publicvoidkeyIn(String msg) { out.getData(msg); } // 定义一个模拟打印的方法 publicvoidprint() { out.out(); }}
Computer类已经完全与Printer类分离,只是与Output接口耦合,Computer不再负责创建Output对象,系统提供一个Output工厂来负责生成Output对象
import davieyang.Output;public class OutputFactory{ public Output getOutput() { // return new Printer(); return new BetterPrinter(); } public static void main(String[] args) { var of = new OutputFactory(); var c = new Computer(of.getOutput()); c.keyIn("轻量级Java EE企业应用实战"); c.keyIn("疯狂Java讲义"); c.print(); }}
OutputFactory类包含了一个getOutput()方法,该方法返回一个Output实现类的实例,该方法负责创建Output实例,具体创建哪一个实现类的对象由该方法决定(return new Printer();当然也可以有更复杂的逻辑),如果系统需要将Printer改为BetterPrinter实现类,只需要让BetterPrinter实现Output接口,并修改OutputFactory类中的getOutput()方法体即可(return new BetterPrinter();)
import davieyang.Output;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对象使用
把所有生成Output对象的逻辑集中在OutputFactory工厂类中管理,所有需要使用Output对象的类只需要与Output接口耦合,而不是与具体的实现类耦合,即使系统中有很多类使用了Printer对象,我们也只需要修改一处即可实现多处生效,而所有程序无须修改。
## 命令模式
场景:某个方法需要完成某一个行为,但这个行为的具体实现无法确定,必须等到执行该方法时才可以确定,例如假设有个方法需要遍历数组中的数组元素,但无法确定在遍历数组元素时如何处理这些元素,需要在调用该方法时再指定具体的处理行为
对于这样的需求,必须把“处理行为”坐位参数传入该方法
public interface Command{ // 接口里定义的process()方法用于封装“处理行为” voidprocess(int element);}
Command接口定义了一个process()方法,用于封装“处理行为”,但这个方法没有方法体,因为还无法确定这个处理行为
public class ProcessArray{ publicvoidprocess(int[] target, Command cmd) { for (var t : target) { cmd.process(t); } }}
处理数组的处理类,这个类中包含一个process()方法,这个方法无法确定处理数组的行为,所以定义该方法时使用了一个Command参数,这个Command参数负责对数组的处理行为
通过Command接口,实现了让ProcessArray类与具体的“处理行为”分离,程序使用了Command接口代表了对数组的处理行为
然而Command接口也没有提供真正的处理,只有等到需要调用ProcessArray对象的process()方法时,才真正传入一个Command对象,才确定对数组的处理行为
public class CommandTest{ publicstaticvoidmain(String[] args) { var pa = new ProcessArray(); int[] target = {3, -4, 6, 4}; // 第一次处理数组,具体处理行为取决于PrintCommand pa.process(target, new PrintCommand()); System.out.println("------------------"); // 第二次处理数组,具体处理行为取决于SquareCommand pa.process(target, new SquareCommand()); }}
public class PrintCommand implements Command{ publicvoidprocess(int element) { System.out.println("迭代输出目标数组的元素:" + element); }}
public class SquareCommand implements Command{ publicvoidprocess(int element) { System.out.println("数组元素的平方是:" + element * element); }}
执行CommandTest结果如下:
迭代输出目标数组的元素:3迭代输出目标数组的元素:-4迭代输出目标数组的元素:6迭代输出目标数组的元素:4------------------数组元素的平方是:9数组元素的平方是:16数组元素的平方是:36数组元素的平方是:16
对于PrintCommand和 SquareCommand两个实现类而言,实际有意义的部分就是process(int[]target)方法,该方法的方法体就是传入ProcessArray类里的process()方法的“处理行为”,通过这种方式就可以实现process()方法和“处理行为”的分离
## 设计模式
设计模式,就是对经常出现的软件设计问题的成熟解决方案,实际上设计模式仅仅是对特定问题的一种惯性思维,要理解设计模式必须以足够的代码积累量作为基础