來自java編程思想9.3 完全解耦
只要一個方法操作是類而非接口,那麼你就只能使用這個類及其子類。如果你想要將這個方法應用到不在此繼承結構中的某個類,那麼你就達不到目的。接口可以很大程度放寬這種限制,因此使用接口而非繼承使得我們可以編寫可複用性更好的代碼。PS:書中前面也說道了,慎用繼承,儘量使用組合!
例如:假設有一個Processor類,產生一個name()方法;另外還有一個process()方法,該方法接受輸入參數,修改它的值產生輸入。這個類作爲基類被擴展,用來創建不同的Processor。
package interfaces.classprocessor;
import static net.mindview.util.Print.*;
import java.util.Arrays;
class Processor{
public String name(){
return getClass().getSimpleName();
}
Object process(Object input){
return input;
}
}
class Upcase extends Processor{
String process(Object input){
return ((String)input).toLowerCase();
}
}
class Downcase extends Processor{
String process(Object input){
return ((String)input).toUpperCase();
}
}
class Splitter extends Processor{
String process (Object input){
return Arrays.toString(((String)input).split(" "));
}
}
public class Apply {
public static void process(Processor p,Object s){
print("Using processor "+p.name());
print(p.process(s));
}
public static String s =
"Disagreement with beliefs is by definition incorrct";
public static void main(String[] args) {
process(new Upcase(), s);
process(new Downcase(), s);
process(new Splitter(), s);
}
}
輸出:
Using processor Upcase
disagreement with beliefs is by definition incorrct
Using processor Downcase
DISAGREEMENT WITH BELIEFS IS BY DEFINITION INCORRCT
Using processor Splitter
[Disagreement, with, beliefs, is, by, definition, incorrct]
Apply.process()方法可以接受任何類型的Processor,並將其應用到一個Object對象上,然後打印結果。像本例剛開始看我們覺得設計是不錯的,創建一個能夠根據所傳遞的參數對象的不同而具有不同行爲的方法,稱爲策略模式。這類方法包含執行算法中固定不變的部分,而“策略”包含變化的部分,策略就是傳進去的參數。
現在有一個電子濾波器好像也是用Apply.process()方法。
在interfaces.filters包中創建相關的代碼
package interfaces.filters;
public class Waveform {
private static long counter;
private static long id = counter++;
public String toString(){
return "Waveform" + id;
}
}
基類Filter代碼實現
package interfaces.filters;
public class Filter {
public String name(){
return getClass().getSimpleName();
}
public Waveform process(Waveform input){
return input;
}
}
下面繼承Filter類產生的繼承類
package interfaces.filters;
public class LowPass extends Filter {
double cutoff;
public LowPass(double cutoff){
this.cutoff = cutoff;
}
public Waveform process(Waveform input){
return input;
}
}
package interfaces.filters;
public class HighPass extends Filter {
double cutoff;
public HighPass(double cutoff){
this.cutoff = cutoff;
}
public Waveform process(Waveform input){
return input;
}
}
package interfaces.filters;
public class BandPass extends Filter {
double lowCutoff,highCutoff;
public BandPass(double lowCutoff,double highCutoff){
this.lowCutoff = lowCutoff;
this.highCutoff = highCutoff;
}
public Waveform process(Waveform input){
return input;
}
}
通過上面代碼我們可以發現Filter和Processor具有相同接口元素,但是因爲它不是繼承Processor,因此你不能將Apply.process()方法應用到Filter上。
我們反過來找原因就會發現,主要是因爲Apply.process()方法和Processor之間耦合過緊,已經超出了所需要的程度了,使得應該複用Apply.process()時複用卻 用不了。
如果我們把Processor 定義爲一個接口,那麼耦合就會降低,就能複用Apply.process(),下面是修改的版本。
package interfaces.interfaceprocessor;
public interface Processor {
String name();
Object process(Object input);
}
package interfaces.interfaceprocessor;
import static net.mindview.util.Print.*;
public class Apply {
public static void process(Processor p,Object s){
print("Using Processor "+p.name());
print(p.process(s));
}
}
package interfaces.interfaceprocessor;
import java.util.Arrays;
public abstract class StringProcessor implements Processor{
public static void main(String[] args) {
Apply.process(new Upcase(), string);
Apply.process(new Downcase(), string);
Apply.process(new Splitter(), string);
}
public String name() {
return getClass().getSimpleName();
}
public abstract String process(Object input) ;
public static String string =
"disagreement with beliefs is by definition incorrct";
}
class Upcase extends StringProcessor{
public String process(Object input) {
return ((String)input).toUpperCase();
}
}
class Downcase extends StringProcessor{
public String process(Object input) {
return ((String)input).toLowerCase();
}
}
class Splitter extends StringProcessor{
@Override
public String process(Object input) {
return Arrays.toString(((String)input).split(" "));
}
}
結果輸入:
Using Processor Upcase
DISAGREEMENT WITH BELIEFS IS BY DEFINITION INCORRCT
Using Processor Downcase
disagreement with beliefs is by definition incorrct
Using Processor Splitter
[disagreement, with, beliefs, is, by, definition, incorrct]
這個時候我們還是無法Apply.process()方法在Filter上,但是我們可以引入“適配器”設計模式。適配器中代碼將接受你所擁有的接口,併產生你所需要的接口,就像下面一樣:
package interfaces.interfaceprocessor;
import interfaces.filters.Filter;
import interfaces.filters.Waveform;
public class FilterAdapter implements Processor {
@Override
public String name() {
return filter.name();
}
@Override
public Waveform process(Object input) {
return filter.process((Waveform)input);
}
Filter filter ;
public FilterAdapter(Filter filter){
this.filter = filter;
}
}
package interfaces.interfaceprocessor;
import interfaces.filters.BandPass;
import interfaces.filters.HighPass;
import interfaces.filters.LowPass;
import interfaces.filters.Waveform;
public class FilterProcessor {
public static void main(String[] args) {
Waveform w = new Waveform();
Apply.process(new FilterAdapter(new LowPass(1.0)), w);
Apply.process(new FilterAdapter(new HighPass(2.0)), w);
Apply.process(new FilterAdapter(new BandPass(3.0, 4.0)), w);
}
}
運行結果:
Using Processor LowPass
Waveform 0
Using Processor HighPass
Waveform 0
Using Processor BandPass
Waveform 0
總結:接口常用方法就是前面說到的策略設計模式,你編寫一個執行某些操作的方法,而該方法接受一個同樣是你指定的接口。你主要是聲明:“你可以用任何你想要的對象來調用我的方法,只要你的對象遵循我的接口。”這使得方法更加靈活、通用,並且更具有複用性。
前期設計我們不應該考慮適配器設計模式,而應該通過重構來統一接口,但是往往系統升級改造使得我們需要使用適配器模式爲無賴之舉。