企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## [完全解耦](https://lingcoder.gitee.io/onjava8/#/book/10-Interfaces?id=%e5%ae%8c%e5%85%a8%e8%a7%a3%e8%80%a6) 当方法操纵的是一个类而非接口时,它就只能作用于那个类或其子类。如果想把方法应用于那个继承层级结构之外的类,就会触霉头。接口在很大程度上放宽了这个限制,因而使用接口可以编写复用性更好的代码。 例如有一个类**Processor**有两个方法`name()`和`process()`。`process()`方法接受输入,修改并输出。把这个类作为基类用来创建各种不同类型的**Processor**。下例中,**Processor**的各个子类修改 String 对象(注意,返回类型可能是协变类型而非参数类型): ~~~ // interfaces/Applicator.java import java.util.*; class Processor { public String name() { return getClass().getSimpleName(); } public Object process(Object input) { return input; } } class Upcase extends Processor { // 返回协变类型 @Override public String process(Object input) { return ((String) input).toUpperCase(); } } class Downcase extends Processor { @Override public String process(Object input) { return ((String) input).toLowerCase(); } } class Splitter extends Processor { @Override public String process(Object input) { // split() divides a String into pieces: return Arrays.toString(((String) input).split(" ")); } } public class Applicator { public static void apply(Processor p, Object s) { System.out.println("Using Processor " + p.name()); System.out.println(p.process(s)); } public static void main(String[] args) { String s = "We are such stuff as dreams are made on"; apply(new Upcase(), s); apply(new Downcase(), s); apply(new Splitter(), s); } } ~~~ 输出: ~~~ Using Processor Upcase WE ARE SUCH STUFF AS DREAMS ARE MADE ON Using Processor Downcase we are such stuff as dreams are made on Using Processor Splitter [We, are, such, stuff, as, dreams, are, made, on] ~~~ **Applicator**的`apply()`方法可以接受任何类型的**Processor**,并将其应用到一个**Object**对象上输出结果。像本例中这样,创建一个能根据传入的参数类型从而具备不同行为的方法称为*策略*设计模式。方法包含算法中不变的部分,策略包含变化的部分。策略就是传入的对象,它包含要执行的代码。在这里,**Processor**对象是策略,`main()`方法展示了三种不同的应用于**String s**上的策略。 `split()`是**String**类中的方法,它接受**String**类型的对象并以传入的参数作为分割界限,返回一个数组**String\[\]\*\*。在这里用它是为了更快地创建 \*\*String**数组。 假设现在发现了一组电子滤波器,它们看起来好像能使用**Applicator**的`apply()`方法: ~~~ // interfaces/filters/Waveform.java package interfaces.filters; public class Waveform { private static long counter; private final long id = counter++; @Override public String toString() { return "Waveform " + id; } } // interfaces/filters/Filter.java package interfaces.filters; public class Filter { public String name() { return getClass().getSimpleName(); } public Waveform process(Waveform input) { return input; } } // interfaces/filters/LowPass.java package interfaces.filters; public class LowPass extends Filter { double cutoff; public LowPass(double cutoff) { this.cutoff = cutoff; } @Override public Waveform process(Waveform input) { return input; // Dummy processing 哑处理 } } // interfaces/filters/HighPass.java package interfaces.filters; public class HighPass extends Filter { double cutoff; public HighPass(double cutoff) { this.cutoff = cutoff; } @Override public Waveform process(Waveform input) { return input; } } // interfaces/filters/BandPass.java package interfaces.filters; public class BandPass extends Filter { double lowCutoff, highCutoff; public BandPass(double lowCut, double highCut) { lowCutoff = lowCut; highCutoff = highCut; } @Override public Waveform process(Waveform input) { return input; } } ~~~ **Filter**类与**Processor**类具有相同的接口元素,但是因为它不是继承自**Processor**—— 因为**Filter**类的创建者根本不知道你想将它当作**Processor**使用 —— 因此你不能将**Applicator**的`apply()`方法应用在**Filter**类上,即使这样做也能正常运行。主要是因为**Applicator**的`apply()`方法和**Processor**过于耦合,这阻止了**Applicator**的`apply()`方法被复用。另外要注意的一点是 Filter 类中`process()`方法的输入输出都是**Waveform**。 但如果**Processor**是一个接口,那么限制就会变得松动到足以复用**Applicator**的`apply()`方法,用来接受那个接口参数。下面是修改后的**Processor**和**Applicator**版本: ~~~ // interfaces/interfaceprocessor/Processor.java package interfaces.interfaceprocessor; public interface Processor { default String name() { return getClass().getSimpleName(); } Object process(Object input); } // interfaces/interfaceprocessor/Applicator.java package interfaces.interfaceprocessor; public class Applicator { public static void apply(Processor p, Object s) { System.out.println("Using Processor " + p.name()); System.out.println(p.process(s)); } } ~~~ 复用代码的第一种方式是客户端程序员遵循接口编写类,像这样: ~~~ // interfaces/interfaceprocessor/StringProcessor.java // {java interfaces.interfaceprocessor.StringProcessor} package interfaces.interfaceprocessor; import java.util.*; interface StringProcessor extends Processor { @Override String process(Object input); // [1] String S = "If she weighs the same as a duck, she's made of wood"; // [2] static void main(String[] args) { // [3] Applicator.apply(new Upcase(), S); Applicator.apply(new Downcase(), S); Applicator.apply(new Splitter(), S); } } class Upcase implements StringProcessor { // 返回协变类型 @Override public String process(Object input) { return ((String) input).toUpperCase(); } } class Downcase implements StringProcessor { @Override public String process(Object input) { return ((String) input).toLowerCase(); } } class Splitter implements StringProcessor { @Override public String process(Object input) { return Arrays.toString(((String) input).split(" ")); } } ~~~ 输出: ~~~ Using Processor Upcase IF SHE WEIGHS THE SAME AS A DUCK, SHE'S MADE OF WOOD Using Processor Downcase if she weighs the same as a duck, she's made of wood Using Processor Splitter [If, she, weighs, the, same, as, a, duck,, she's, made, of, wood] ~~~ > \[1\] 该声明不是必要的,即使移除它,编译器也不会报错。但是注意这里的协变返回类型从 Object 变成了 String。 > > \[2\] S 自动就是 final 和 static 的,因为它是在接口中定义的。 > > \[3\] 可以在接口中定义`main()`方法。 这种方式运作得很好,然而你经常遇到的情况是无法修改类。例如在电子滤波器的例子中,类库是被发现而不是创建的。在这些情况下,可以使用*适配器*设计模式。适配器允许代码接受已有的接口产生需要的接口,如下: ~~~ // interfaces/interfaceprocessor/FilterProcessor.java // {java interfaces.interfaceprocessor.FilterProcessor} package interfaces.interfaceprocessor; import interfaces.filters.*; class FilterAdapter implements Processor { Filter filter; FilterAdapter(Filter filter) { this.filter = filter; } @Override public String name() { return filter.name(); } @Override public Waveform process(Object input) { return filter.process((Waveform) input); } } public class FilterProcessor { public static void main(String[] args) { Waveform w = new Waveform(); Applicator.apply(new FilterAdapter(new LowPass(1.0)), w); Applicator.apply(new FilterAdapter(new HighPass(2.0)), w); Applicator.apply(new FilterAdapter(new BandPass(3.0, 4.0)), w); } } ~~~ 输出: ~~~ Using Processor LowPass Waveform 0 Using Processor HighPass Waveform 0 Using Processor BandPass Waveform 0 ~~~ 在这种使用适配器的方式中,**FilterAdapter**的构造器接受已有的接口**Filter**,继而产生需要的**Processor**接口的对象。你可能还注意到**FilterAdapter**中使用了委托。 协变允许我们从`process()`方法中产生一个**Waveform**而非**Object**对象。 将接口与实现解耦使得接口可以应用于多种不同的实现,因而代码更具可复用性。