9.3 完全解耦
只要一个方法操作的是类而非接口,那么你就只能使用这个类及其子类。如果你想要将这个方法应用于不在此继承结构中的某个类,那么你就会触霉头了。接口可以很大程度上放宽这种限制,因此,它使得我们可以编写复用性更好的代码。
例如,假设有一个Processor的类,它有一个name()方法,另外还有一个process()方法,该方法接受输入参数,修改它的值,然后产生输出。这个类作为基类而被扩展,用来创建各种不同类型的Processor。在本例中,Processor的子类将修改String对象(注意,返回类型可以是协变类型,而非参数类型):
packageinterfaces.classprocessor; importjava.util.*; importstaticnet.mindview.util.Print.*; classProcessor { publicStringname() { returngetClass().getSimpleName(); } Objectprocess(Objectinput) { returninput; } } classUpcaseextendsProcessor { Stringprocess(Objectinput) { // Covariant return return ((String)input).toUpperCase(); } } classDowncaseextendsProcessor { Stringprocess(Objectinput) { return ((String)input).toLowerCase(); } } classSplitterextendsProcessor { Stringprocess(Objectinput) { // The split() argument divides a String into pieces: returnArrays.toString(((String)input).split(" ")); } } publicclassApply { publicstaticvoidprocess(Processorp, Objects) { print("Using Processor "+p.name()); print(p.process(s)); } publicstaticStrings="Disagreement with beliefs is by definition incorrect"; publicstaticvoidmain(String[] args) { process(newUpcase(), s); process(newDowncase(), s); process(newSplitter(), s); } }
/* Output:
Using Processor Upcase
DISAGREEMENT WITH BELIEFS IS BY DEFINITION INCORRECT
Using Processor Downcase
disagreement with beliefs is by definition incorrect
Using Processor Splitter
[Disagreement, with, beliefs, is, by, definition, incorrect]
*///:~
Apply.process()方法可以接受任何类型的Processor,并将其应用到一个Object对象上,然后打印结果。像本例这样,创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,被称为策略设计模式。这类方法包含所要执行的算法中固定不变的部分,而“策略”包含变化的部分。策略就是传递进去的参数对象,它包含要执行的代码。这里,Processor对象就是一个策略,在main()中可以看到有三种不同的策略应用到了String类型的s对象上。
现在嘉禾我们发现了一组电子滤波器,它们看起来好像适用于Apply.process()方法:
//: interfaces/filters/Waveform.java packageinterfaces.filters; publicclassWaveform { privatestaticlongcounter; privatefinallongid=counter++; publicStringtoString() { return"Waveform "+id; } } ///:~ //: interfaces/filters/Filter.java packageinterfaces.filters; publicclassFilter { publicStringname() { returngetClass().getSimpleName(); } publicWaveformprocess(Waveforminput) { returninput; } } ///:~ //: interfaces/filters/LowPass.java packageinterfaces.filters; publicclassLowPassextendsFilter { doublecutoff; publicLowPass(doublecutoff) { this.cutoff=cutoff; } publicWaveformprocess(Waveforminput) { returninput; // Dummy processing } } ///:~ //: interfaces/filters/HighPass.java packageinterfaces.filters; publicclassHighPassextendsFilter { doublecutoff; publicHighPass(doublecutoff) { this.cutoff=cutoff; } publicWaveformprocess(Waveforminput) { returninput; } } ///:~ //: interfaces/filters/BandPass.java packageinterfaces.filters; publicclassBandPassextendsFilter { doublelowCutoff, highCutoff; publicBandPass(doublelowCut, doublehighCut) { lowCutoff=lowCut; highCutoff=highCut; } publicWaveformprocess(Waveforminput) { returninput; } }
///:~
Filter与Processor具有相同的接口元素,但是它并非继承自Processor——因为Filter类的创建者压根不清楚你想要将它用作Processor——因此你不能将Filter用于Apply.process()方法,即便这样做可以正常运行。这里主要是因为Apply.process()方法和Processor之间的耦合过紧,已经超出了所需要的程度,这就使得应该复用Apply.process()的代码时,复用却被禁止了。另外还需要注意的是它们的输入和输出都是Waveform。
但是,如果Processor是一个接口,那么这些限制就会变得松动,使得你可以复用该接口的Apply.process()。下面是Processor和Apply的修改版本:
//: interfaces/interfaceprocessor/Processor.java packageinterfaces.interfaceprocessor; publicinterfaceProcessor { Stringname(); Objectprocess(Objectinput); } ///:~ //: interfaces/interfaceprocessor/Apply.java packageinterfaces.interfaceprocessor; importstaticnet.mindview.util.Print.*; publicclassApply { publicstaticvoidprocess(Processorp, Objects) { print("Using Processor "+p.name()); print(p.process(s)); } }
///:~
复用代码的第一种方式是客户端程序员遵循该接口来编写他们自己的类,就像下面这样:
//: interfaces/interfaceprocessor/StringProcessor.java packageinterfaces.interfaceprocessor; importjava.util.*; publicabstractclassStringProcessorimplementsProcessor{ publicStringname() { returngetClass().getSimpleName(); } publicabstractStringprocess(Objectinput); publicstaticStrings="If she weighs the same as a duck, she’s made of wood"; publicstaticvoidmain(String[] args) { Apply.process(newUpcase(), s); Apply.process(newDowncase(), s); Apply.process(newSplitter(), s); } } classUpcaseextendsStringProcessor { publicStringprocess(Objectinput) { // Covariant return return ((String)input).toUpperCase(); } } classDowncaseextendsStringProcessor { publicStringprocess(Objectinput) { return ((String)input).toLowerCase(); } } classSplitterextendsStringProcessor { publicStringprocess(Objectinput) { returnArrays.toString(((String)input).split(" ")); } }
/* Output:
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]
*///:~
但是,你经常碰到的情况是你无法修改你想要使用的类。例如,在电子滤波器的例子中,类库是被发现而非被创建的。在这些情况下,可以使用适配器设计模式。适配器中的代码将接受你所拥有的接口,并产生你所需要的接口,就像下面这样:
//: interfaces/interfaceprocessor/FilterProcessor.java packageinterfaces.interfaceprocessor; importinterfaces.filters.*; classFilterAdapterimplementsProcessor { Filterfilter; publicFilterAdapter(Filterfilter) { this.filter=filter; } publicStringname() { returnfilter.name(); } publicWaveformprocess(Objectinput) { returnfilter.process((Waveform)input); } } publicclassFilterProcessor { publicstaticvoidmain(String[] args) { Waveformw=newWaveform(); Apply.process(newFilterAdapter(newLowPass(1.0)), w); Apply.process(newFilterAdapter(newHighPass(2.0)), w); Apply.process( newFilterAdapter(newBandPass(3.0, 4.0)), w); } }
/* Output:
Using Processor LowPass
Waveform 0
Using Processor HighPass
Waveform 0
Using Processor BandPass
Waveform 0
*///:~
在这种使用适配器的方式中,FilterAdapter的构造器接受你所拥有的接口Filter,然后生产具有你所需要的Processor接口的对象。你可能还注意到了,在FilterAdapter类中用到了代理。将接口从具体实现中解耦使得接口可以应用于多种不同的具体实现,因此代码也就更具可复用性。