設計模式:結構型-介面卡模式

2021-03-10 12:00:04


第一章 介面卡模式介紹

介面卡模式的介紹:

介面卡模式(Adapter Pattern)是作為兩個不相容介面之間的橋樑,這種型別的設計模式屬於結構型模式。一些書籍也稱介面卡模式為預設介面卡模式(Default Adapter Pattern)。介面卡模式主要分為三類:類介面卡模式、物件介面卡模式、介面介面卡模式。以生活中手機充電為例來講解介面卡模式,手機本身並不能直接用220V交流電,需要將220V的交流電轉換為5V的直流電,在這個過程中,充電器本身相當於Adapter(介面卡),220V交流電相當於Adaptee (適配者),5V直流電則是我們的Target(目標)。

介面卡模式的優點:

  • 使用者端通過介面卡可以透明地呼叫目標介面。
  • 複用了現存的類,程式設計師不需要修改原有程式碼而重用現有的適配者類。
  • 將目標類和適配者類解耦,解決了目標類和適配者類介面不一致的問題。
  • 在很多業務場景中符合開閉原則。

介面卡模式的缺點:

  • 介面卡編寫過程需要結合業務場景全面考慮,可能會增加系統的複雜性。
  • 增加程式碼閱讀難度,降低程式碼可讀性,過多使用介面卡會使系統程式碼變得凌亂。

介面卡模式的應用:

  • 以前開發的系統存在滿足新系統功能需求的類,但其介面同新系統的介面不一致。
  • 使用第三方提供的元件,但元件介面定義和自己要求的介面定義不同。

介面卡模式的角色:

  • 目標(Target):當前系統業務所期待的介面,它可以是抽象類或介面。就好比上述例子中的5V直流電。
  • 適配者(Adaptee)類:它是被存取和適配的現存元件庫中的元件介面。就好比上述例子中的220V交流電。
  • 介面卡(Adapter)類:它是一個轉換器,通過繼承或參照適配者的物件,把適配者介面轉換成目標介面,讓客戶按目標介面/抽象類的格式存取適配者。

第二章 介面卡模式實現(類介面卡)

2.1、關係依賴圖

2.2、建立交流電

Voltage220V

public class Voltage220V {
    public int output220V() {
        System.out.println("Voltage220V output220V ...");
        return 220;
    }
}

2.3、定義直流電

Voltage5V

public interface Voltage5V {
    //定義一個標準,充電器來實現
    public int output5V();
}

2.4、建立充電器

VoltageAdapter

public class VoltageAdapter extends Voltage220V implements Voltage5V {
    @Override
    public int output5V() {
        //獲取220V交流電
        int output220V = output220V();
        //轉換為5V直流電
        int output5V = output220V / 44;

        System.out.println("VoltageAdapter output5V ...");
        return output5V;
    }
}

2.5、建立手機類

Phone

public class Phone {
    //充電
    public void charging(Voltage5V voltage5V) {
        if (voltage5V.output5V() == 5) {
            System.out.println("電壓剛好5V, 可以充電");
        } else if (voltage5V.output5V() > 5) {
            System.out.println("電壓大於5V, 不能充電");
        }
    }
}

2.6、測試充電器

PhoneTest

public class PhoneTest {
    public static void main(String[] args) {
        //建立一部手機
        Phone phone = new Phone();
        //用充電器充電
        phone.charging(new VoltageAdapter());
    }
}
Voltage220V output220V ...
VoltageAdapter output5V ...
電壓剛好5V, 可以充電

2.7、點評該模式

  • Java是單繼承機制,所以類介面卡需要繼承適配者(Adaptee,指Voltage220V)類,這點算是一個缺點,除此之外還必須要求目標(Target,指Voltage5V)必須是介面,有一定侷限性;
  • 適配者Voltage220V類的方法在介面卡VoltageAdapter類中都會暴露出來,也增加了使用的成本。但是由於其繼承了適配者Voltage220V類,所以它可以根據需求重寫該類的方法,使得介面卡VoltageAdapter類的靈活性增強了。

第三章 介面卡模式實現(物件介面卡)

3.1、關係依賴圖

基本思路和類介面卡模式相同,只是將VoltageAdapter類作修改,不是繼承Voltage220V類,而是持有Voltage220V類的範例,以解決相容性的問題。

根據「合成複用原則」,在系統中儘量使用關聯關係(聚合)來替代繼承關係。

3.2、建立交流電

Voltage220V

public class Voltage220V {
    public int output220V() {
        System.out.println("Voltage220V output220V ...");
        return 220;
    }
}

3.3、定義直流電

Voltage5V

public interface Voltage5V {
    //定義一個標準,充電器來實現
    public int output5V();
}

3.4、建立充電器

VoltageAdapter

public class VoltageAdapter implements Voltage5V {
    private Voltage220V voltage220V;

    public VoltageAdapter(Voltage220V voltage220V) {
        this.voltage220V = voltage220V;
    }

    @Override
    public int output5V() {
        //獲取220V交流電
        int output220V = voltage220V.output220V();
        //轉換為5V直流電
        int output5V = output220V / 44;

        System.out.println("VoltageAdapter output5V ...");
        return output5V;
    }
}

3.5、建立手機類

Phone

public class Phone {
    //充電
    public void charging(Voltage5V voltage5V) {
        if (voltage5V.output5V() == 5) {
            System.out.println("電壓剛好5V, 可以充電");
        } else if (voltage5V.output5V() > 5) {
            System.out.println("電壓大於5V, 不能充電");
        }
    }
}

3.6、測試充電器

PhoneTest

public class PhoneTest {
    public static void main(String[] args) {
        //建立一部手機
        Phone phone = new Phone();
        //用充電器充電(傳入220V交流電,傳出5V直流電)
        phone.charging(new VoltageAdapter(new Voltage220V()));
    }
}
Voltage220V output220V ...
VoltageAdapter output5V ...
電壓剛好5V, 可以充電

3.7、點評該模式

  • 物件介面卡和類介面卡其實算是同一種思想,只不過實現方式不同。
  • 根據合成複用原則,使用組合替代繼承, 所以它解決了類介面卡中VoltageAdapter必須繼承Voltage220V的侷限性問題,也不再強制要求Voltage5V必須是介面。使用成本更低,更靈活。因此,物件介面卡模式是介面卡模式常用的一種。

第四章 介面卡模式實現(介面介面卡)

4.1、關係依賴圖

介面介面卡介紹:當不需要全部實現介面提供的方法時,可先設計一個抽象類實現介面,併為該介面中每個方法提供 一個預設實現(空方法),那麼該抽象類的子類可有選擇地覆蓋父類別的某些方法來實現需求。

4.2、建立動畫介面

Animation

public interface Animation {
    public void fadeIn();
    public void fadeInUp();
    public void fadeInDown();
    public void fadeInLeft();
    public void fadeInRight();
    public void fadeOut();
    public void fadeOutUp();
    public void fadeOutDown();
    public void fadeOutLeft();
    public void fadeOutRight();
}

4.3、介面的介面卡

AnimationAdapter

public class AnimationAdapter implements Animation {
    @Override
    public void fadeIn() {}
    @Override
    public void fadeInUp() {}
    @Override
    public void fadeInDown() {}
    @Override
    public void fadeInLeft() {}
    @Override
    public void fadeInRight() {}
    @Override
    public void fadeOut() {}
    @Override
    public void fadeOutUp() {}
    @Override
    public void fadeOutDown() {}
    @Override
    public void fadeOutLeft() {}
    @Override
    public void fadeOutRight() {}
}

4.4、介面卡的子類

JFrameAnimation

public class JFrameAnimation extends AnimationAdapter {
    @Override
    public void fadeIn() {
        System.out.println("JFrameAnimation fadeIn ...");
    }

    @Override
    public void fadeOut() {
        System.out.println("JFrameAnimation fadeOut ...");
    }
}

4.5、使用者端測試類

Client

public class Client {
    public static void main(String[] args) {
        Animation animation = new JFrameAnimation();
        animation.fadeIn();
        animation.fadeOut();
    }
}
JFrameAnimation fadeIn ...
JFrameAnimation fadeOut ...

4.6、該模式的實踐

javax.servlet.http.HttpServlet繼承javax.servlet.GenericServlet空實現於javax.servlet.Servlet

第五章 介面卡模式應用

最佳實踐:介面卡模式在 SpringMVC 框架應用

由於Controller的型別不同,有多重實現方式,那麼呼叫方式就不是確定的,如果需要直接呼叫Controller方法,需要呼叫的時候就得不斷是使用if else來進行判斷是哪一種子類然後執行。那麼如果後面要擴充套件Controller,就得修改原來的程式碼,這樣違背了 OCP 原則。

因此SpringMVC定義了一個適配介面HandlerAdapter,使得每一種Controller有一種對應的介面卡實現類, 讓介面卡代替Controller執行相應的方法。這樣在擴充套件Controller時,只需要增加一個介面卡類就完成了SpringMVC的擴充套件了。

org.springframework.web.servlet.HandlerAdapter,相當於介面卡模式中的目標(Target)

public interface HandlerAdapter {
	boolean supports(Object handler);

	@Nullable
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

	long getLastModified(HttpServletRequest request, Object handler);
}

org.springframework.web.servlet.DispatcherServlet

public class DispatcherServlet extends FrameworkServlet {
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //通過request可以得到請求對應的控制器handler
        mappedHandler = getHandler(processedRequest);
        //根據handler得到對應的介面卡 
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
        //通過介面卡去呼叫對應controller的方法,並返回ModelAndView
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    }
}

getHandlerAdapter:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        for (HandlerAdapter adapter : this.handlerAdapters) {
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
    }
    throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

我們隨便找一個HandlerAdapter的實現類,相當於介面卡模式中的介面卡(Adapter),其中Object handler為適配者(Adaptee)

public class HttpRequestHandlerAdapter implements HandlerAdapter {
	@Override
	public boolean supports(Object handler) {
		return (handler instanceof HttpRequestHandler);
	}

	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		((HttpRequestHandler) handler).handleRequest(request, response);
		return null;
	}

	@Override
	public long getLastModified(HttpServletRequest request, Object handler) {
		if (handler instanceof LastModified) {
			return ((LastModified) handler).getLastModified(request);
		}
		return -1L;
	}
}