1. 程式人生 > 實用技巧 >Codeforces Round #689 (Div. 2) A,B,C,D題解

Codeforces Round #689 (Div. 2) A,B,C,D題解

現實生活中的介面卡例子 泰國插座用的是兩孔的(歐標),可以買個多功能轉換插頭 (介面卡) ,這樣就可以使用了 基本介紹 1) 介面卡模式(Adapter Pattern)將某個類的介面轉換成客戶端期望的另一個介面表示,主的目的是相容性,讓原本因介面不匹配不能一起工作的兩個類可以協同工作。其別名為包裝器(Wrapper) 2) 介面卡模式屬於結構型模式 3) 主要分為三類:類介面卡模式、物件介面卡模式、介面介面卡模式 介面卡模式工作原理 1) 介面卡模式:將一個類的介面轉換成另一種介面.讓原本介面不相容的類可以相容 2) 從使用者的角度看不到被適配者,是解耦的 3) 使用者呼叫介面卡轉化出來的目標介面方法,介面卡再呼叫被適配者的相關介面方法 4) 使用者收到反饋結果,感覺只是和目標介面互動,

類介面卡模式介紹 基本介紹:Adapter類,通過繼承 src類,實現 dst 類介面,完成src->dst的適配。 類介面卡模式應用例項 1) 應用例項說明 以生活中充電器的例子來講解介面卡,充電器本身相當於Adapter,220V交流電相當於src (即被適配者),我們的目dst(即 目標)是5V直流電 2) 思路分析(類圖) 3) 程式碼實現 被適配的類
//被適配的類
public class Voltage220V {
    //輸出220V的電壓
    public int output220V() {
        int src = 220;
        System.out.println(
"電壓=" + src + "伏"); return src; } }

適配介面

//適配介面
public interface IVoltage5V {
    public int output5V();
}

介面卡類

//介面卡類
public class VoltageAdapter extends Voltage220V implements IVoltage5V {

    @Override
    public int output5V() {
        // TODO Auto-generated method stub
        //獲取到220V電壓
int srcV = output220V(); int dstV = srcV / 44 ; //轉成 5v return dstV; } }

手機

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

測試:

public class Client {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println(" === 類介面卡模式 ====");
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter());
    }

}
類介面卡模式注意事項和細節 1) Java是單繼承機制,所以類介面卡需要繼承src類這一點算是一個缺點, 因為這要求dst必須是介面,有一定侷限性; 2) src類的方法在Adapter中都會暴露出來,也增加了使用的成本。 3) 由於其繼承了src類,所以它可以根據需求重寫src類的方法,使得Adapter的靈活性增強了。 物件介面卡模式 物件介面卡模式介紹 1) 基本思路和類的介面卡模式相同,只是將Adapter類作修改,不是繼承src類,而是持有src類的例項,以解決相容性的問題。 即:持有 src類,實現 dst 類介面,完成src->dst的適配 2) 根據“合成複用原則”,在系統中儘量使用關聯關係來替代繼承關係。 3) 物件介面卡模式是介面卡模式常用的一種 物件介面卡模式應用例項 1) 應用例項說明 以生活中充電器的例子來講解介面卡,充電器本身相當於Adapter,220V交流電相當於src (即被適配者),我們的目dst(即目標)是5V直流電,使用物件介面卡模式完成。 2) 思路分析(類圖):只需修改介面卡即可, 3) 程式碼實現 被適配的類
//被適配的類
public class Voltage220V {
    //輸出220V的電壓,不變
    public int output220V() {
        int src = 220;
        System.out.println("電壓=" + src + "伏");
        return src;
    }
}

適配介面

//適配介面
public interface IVoltage5V {
    public int output5V();
}

介面卡類

//介面卡類
public class VoltageAdapter  implements IVoltage5V {

    private Voltage220V voltage220V; // 關聯關係-聚合
    
    //通過構造器,傳入一個 Voltage220V 例項
    public VoltageAdapter(Voltage220V voltage220v) {
        
        this.voltage220V = voltage220v;
    }

    @Override
    public int output5V() {
        
        int dst = 0;
        if(null != voltage220V) {
            int src = voltage220V.output220V();//獲取220V 電壓
            System.out.println("使用物件介面卡,進行適配~~");
            dst = src / 44;
            System.out.println("適配完成,輸出的電壓為=" + dst);
        }
        return dst;
        
    }
}

手機

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

測試:

public class Client {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println(" === 物件介面卡模式 ====");
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter(new Voltage220V()));
    }
}
物件介面卡模式注意事項和細節 1) 物件介面卡和類介面卡其實算是同一種思想,只不過實現方式不同。根據合成複用原則,使用組合替代繼承, 所以它解決了類介面卡必須繼承src的侷限性問題,也不再要求dst必須是介面。 2) 使用成本更低,更靈活。 介面介面卡模式 介面介面卡模式介紹 1) 一些書籍稱為:介面卡模式(Default Adapter Pattern)或預設介面卡模式。 2) 當不需要全部實現介面提供的方法時,可先設計一個抽象類實現介面,併為該介面中每個方法提供一個預設實現(空方法),那麼該抽象類的子類可有選擇地覆蓋父類的某些方法來實現需求 3) 適用於一個介面不想使用其所有的方法的情況。 介面介面卡模式應用例項 1) Android中的屬性動畫ValueAnimator類可以通過addListener(AnimatorListener listener)方法新增監聽器, 那麼常規寫法如: 2) 有時候我們不想實現Animator.AnimatorListener介面的全部方法,我們只想監聽onAnimationStart,我們會如下寫 3) AnimatorListenerAdapter類,就是一個介面介面卡,程式碼如下:它空實現了Animator.AnimatorListener類(src)的所有方法. 4) AnimatorListener是一個介面. 5) 程式裡的匿名內部類就是Listener 具體實現類   再次理解介面介面卡 類圖

示例程式碼

介面

public interface Interface4 {
    public void m1();
    public void m2();
    public void m3();
    public void m4();
}

抽象類

//在AbsAdapter 我們將 Interface4 的方法進行預設實現
public abstract class AbsAdapter implements Interface4 {

    //預設實現
    public void m1() {

    }

    public void m2() {

    }

    public void m3() {

    }

    public void m4() {

    }
}

測試:

public class Client {
    public static void main(String[] args) {
        
        AbsAdapter absAdapter = new AbsAdapter() {
            //只需要去覆蓋我們 需要使用 介面方法
            @Override
            public void m1() {
                // TODO Auto-generated method stub
                System.out.println("使用了m1的方法");
            }
        };
        
        absAdapter.m1();
    }
}

介面卡模式在SpringMVC框架應用的原始碼剖析 1) SpringMvc中的HandlerAdapter, 就使用了介面卡模式 2) SpringMVC處理請求的流程自己回顧一下 3) 使用HandlerAdapter 的原因分析: 可以看到處理器的型別不同,有多重實現方式,那麼呼叫方式就不是確定的,如果需要直接呼叫Controller方法,需要呼叫的時候就得不斷是使用if else來進行判斷是哪一種子類然後執行。那麼如果後面要擴充套件Controller,就得修改原來的程式碼,這樣違背了OCP原則。 大體流程就是
public class DispatcherServlet extends FrameworkServlet {
// 通過HandlerMapping來對映Controller
mappedHandler = getHandler(processedRequest);
//獲取介面卡
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//..
// 通過介面卡呼叫controller的方法並返回ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}

具體:

  找到DispatcherServlet 類的原始碼:     當一個請求來的時候,DispatcherServlet 會執行doDispatcher 的一個方法,doDispatcher 會接收到request     

    這裡有一個非常核心的方法,根據請求得到一個mappedHandler,這個mappedHandler就是我們的controller,也就是我們的控制器,

   

    然後通過mappedHandler得到一個HandlerAdapter,也就是介面卡,因為不同的handler 要呼叫的不同的介面卡去處理

    

     這個getHandlerAdapter方法,返回的一個HandlerAdapter,是一個介面,

      

  通過這個HandlerAdapter的原始碼可以看到,它有好幾個實現的介面卡類:

    

    那麼這個getHandlerAdapter方法是怎麼拿到對應的controller的,看原始碼:getHandlerAdapter點進去

    

    可以看到它用的一個迴圈,如果為真,就返回這個handler,主要是這個supports方法來進行判斷的

  總結原理:

    

自己手寫一個加深理解

  首先寫一個controller介面:裡面有幾個實現類
//多種Controller實現  
public interface Controller {

}

class HttpController implements Controller {
    public void doHttpHandler() {
        System.out.println("http...");
    }
}

class SimpleController implements Controller {
    public void doSimplerHandler() {
        System.out.println("simple...");
    }
}

class AnnotationController implements Controller {
    public void doAnnotationHandler() {
        System.out.println("annotation...");
    }
}

HandlerAdapter類

///定義一個Adapter介面 
public interface HandlerAdapter {
    public boolean supports(Object handler);

    public void handle(Object handler);
}

// 多種介面卡類

class SimpleHandlerAdapter implements HandlerAdapter {

    public void handle(Object handler) {
        ((SimpleController) handler).doSimplerHandler();
    }

    public boolean supports(Object handler) {
        return (handler instanceof SimpleController);
    }

}

class HttpHandlerAdapter implements HandlerAdapter {

    public void handle(Object handler) {
        ((HttpController) handler).doHttpHandler();
    }

    public boolean supports(Object handler) {
        return (handler instanceof HttpController);
    }

}

class AnnotationHandlerAdapter implements HandlerAdapter {

    public void handle(Object handler) {
        ((AnnotationController) handler).doAnnotationHandler();
    }

    public boolean supports(Object handler) {

        return (handler instanceof AnnotationController);
    }

}

DispatchServlet
import java.util.ArrayList;
import java.util.List;

public class DispatchServlet {

    public static List<HandlerAdapter> handlerAdapters = new ArrayList<HandlerAdapter>();

    public DispatchServlet() {
        handlerAdapters.add(new AnnotationHandlerAdapter());
        handlerAdapters.add(new HttpHandlerAdapter());
        handlerAdapters.add(new SimpleHandlerAdapter());
    }

    public void doDispatch() {

        // 此處模擬SpringMVC從request取handler的物件,
        // 介面卡可以獲取到希望的Controller
         HttpController controller = new HttpController();
        // AnnotationController controller = new AnnotationController();
        //SimpleController controller = new SimpleController();
        // 得到對應介面卡
        HandlerAdapter adapter = getHandler(controller);
        // 通過介面卡執行對應的controller對應方法
        adapter.handle(controller);

    }

    public HandlerAdapter getHandler(Controller controller) {
        //遍歷:根據得到的controller(handler), 返回對應介面卡
        for (HandlerAdapter adapter : this.handlerAdapters) {
            if (adapter.supports(controller)) {
                return adapter;
            }
        }
        return null;
    }

    public static void main(String[] args) {
        new DispatchServlet().doDispatch(); // http...
    }

}

介面卡模式的注意事項和細節 1) 三種命名方式,是根據 src是以怎樣的形式給到Adapter(在Adapter裡的形式)來命名的。 2) 類介面卡:以類給到,在Adapter裡,就是將src當做類,繼承物件介面卡:以物件給到,在Adapter裡,將src作為一個物件,持有介面介面卡:以介面給到,在Adapter裡,將src作為一個介面,實現 3) Adapter模式最大的作用還是將原本不相容的介面融合在一起工作。 4) 實際開發中,實現起來不拘泥於我們講解的三種經典形式