接口實現松耦合

來自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
總結:接口常用方法就是前面說到的策略設計模式,你編寫一個執行某些操作的方法,而該方法接受一個同樣是你指定的接口。你主要是聲明:“你可以用任何你想要的對象來調用我的方法,只要你的對象遵循我的接口。”這使得方法更加靈活、通用,並且更具有複用性。

前期設計我們不應該考慮適配器設計模式,而應該通過重構來統一接口,但是往往系統升級改造使得我們需要使用適配器模式爲無賴之舉。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章