重温经典《Thinking in java》第四版之第九章 接口(四十七)

简介: 重温经典《Thinking in java》第四版之第九章 接口(四十七)

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类中用到了代理。将接口从具体实现中解耦使得接口可以应用于多种不同的具体实现,因此代码也就更具可复用性。

目录
相关文章
|
1月前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
129 3
|
5天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
35 6
|
22天前
|
Java API
Java中内置的函数式接口
Java中内置的函数式接口
23 2
|
27天前
|
Java
在Java中如何实现接口?
实现接口是 Java 编程中的一个重要环节,它有助于提高代码的规范性、可扩展性和复用性。通过正确地实现接口,可以使代码更加灵活、易于维护和扩展。
47 3
|
26天前
|
Java
在Java中,接口之间可以继承吗?
接口继承是一种重要的机制,它允许一个接口从另一个或多个接口继承方法和常量。
74 1
|
26天前
|
Java 开发者
在 Java 中,一个类可以实现多个接口吗?
这是 Java 面向对象编程的一个重要特性,它提供了极大的灵活性和扩展性。
57 1
|
26天前
|
Java
在Java中实现接口的具体代码示例
可以根据具体的需求,创建更多的类来实现这个接口,以满足不同形状的计算需求。希望这个示例对你理解在 Java 中如何实现接口有所帮助。
41 1
|
1月前
|
Java Android开发
Eclipse 创建 Java 接口
Eclipse 创建 Java 接口
27 1
|
1月前
|
Java
java线程接口
Thread的构造方法创建对象的时候传入了Runnable接口的对象 ,Runnable接口对象重写run方法相当于指定线程任务,创建线程的时候绑定了该线程对象要干的任务。 Runnable的对象称之为:线程任务对象 不是线程对象 必须要交给Thread线程对象。 通过Thread的构造方法, 就可以把任务对象Runnable,绑定到Thread对象中, 将来执行start方法,就会自动执行Runable实现类对象中的run里面的内容。
39 1
|
1月前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
46 4