[Adapter] 包裝資料與邏輯在一起的物件

[Adapter] 包裝資料與邏輯在一起的物件

 

原始碼:

   1:  public class DS {
   2:      public string Input { get; set; }
   3:      public string Output { get; set;}
   4:      public void Process(){
   5:           // ... input >> process >> output
   6:      }
   7:  }
   8:   
   9:   
  10:  public class MainClass {
  11:      public void Main(){
  12:          DS ds = new DS();
  13:          ds.Input = "A";
  14:          ds.Process();        
  15:          Console.WriteLine(ds.Output);
  16:      }
  17:  }

 

從上方的原始碼中我們可以看到DS具有2種功能:

1. 資料儲存體 ( Input , Output )

2. 邏輯處理器 ( Process )

 

限制:

    DS物件為第三方元件不可變更其內部邏輯.

 

挑戰:

     DS物件為第三方元件且可能會換版.

 

目標:

    將DS物件的功能做切割及再包裝

 

一、界面定義

   1:  public interface IInput {
   2:   
   3:  }
   4:   
   5:  public interface IOutput {
   6:   
   7:  }
   8:   
   9:  public interface IProcess  {
  10:      IOutput Process(IInput input);
  11:  }

 

二、再包裝

   1:  public class InputImpl : IInput {
   2:      public string Input { get; set;}
   3:  }
   1:  public class OutputImpl : IOutput {
   2:      public string Output { get; set;}
   3:  }
   1:  public class ProcessImpl : IProcess {
   2:      private DS ds = new DS(); // has without is
   3:      public IOutput Process(IInput input) {
   4:          InputImpl inputImpl = (InputImpl) input;
   5:          ds.Input = input.Input;
   6:          ds.Process();
   7:          return new OutputImpl() { Output : ds.Output };
   8:      }
   9:  }

 

三、測試

現在主程式已經跟第三方的"DS"類別相依性脫離了.

   1:  InputImpl input = new InputImpl(){ Input: "A" };
   2:  ProcessImpl process = new ProcessImpl();
   3:  OutputImpl output = (IOutput) process.Process(input);

 

四、效益在那裡?

未來如果第三方的"DS"類別有了差異只要改寫"ProcessImpl"類別即可,

不需再到各處改Reference到DS類別的程式了 :D

 

五、再加強

雖然結構上已經有了脫離, 但因為IInput及IOutput並沒有實質內容,

導致主程式及Process都用了轉型 (PS.累贅),

若IInput 及IOutput 實作了實質內容,

又會讓資料儲存體的彈性降低 (PS.也許不是每一種Input物件都有一個Input:string的屬性),

思考的重點是如何建立物件來增加彈性, 且使用泛型來解決轉型的問題,

後者比較容易可以先實作.

 

六、概念

某個Process的實作就是處理固定的Input & Output ,

透過泛型來固定Input及Output, 現在主程式不需要轉型了.

   1:  public interface IProcess<TInput ,TOutput> 
   2:      where  TInput : IInput
   3:      where  TOutput : IOuput {
   4:      TOutput Process(TInput input);
   5:  }
   6:   
   7:   
   8:  public class ProcessImpl : IProcess<InputImpl, OutputImpl> {
   9:      public OutputImpl Process(InputImpl input){
  10:          // ... logic
  11:      }
  12:  }

 

七、再加強

Process 有沒有 實作 Factory 的必要呢? 

考慮點是未來會不會有可能雖然是某個固定的Input & Output但有不同的流程且經常性變動

   1:  public interface IProcessFactory {
   2:      public ProcessImpl Create();
   3:  }
   4:   
   5:  public class ProcessFactory : IProcessFactory {    
   6:      public static ProcessFactory GetInstance(Type type){
   7:          switch(type){
   8:              case MAIN_TYPE:
   9:                    return new SimpleProcessFactory();
  10:          }
  11:          throw new NotImplementedException(type.ToString());            
  12:      }
  13:  }
  14:   
  15:  internal class SimpleProcessFactory : IProcessFactory {
  16:      public Process Create(){
  17:          return new ProcessImpl();
  18:      }
  19:  }

 

八、效益在那裡

現在Main 類別的內容如下, Main 與 Input 及 Output 仍然相依著, 但 ProcessImpl 也可以抽換了.

   1:  ProcessImpl process = ProcessFactory.GetInstance(this.GetType());
   2:  OutputImpl output = process.Process(new InputImpl(){  Input : "A" });
   3:  Console.WriteLine(output.Output);
 
 

結論:

1. Adaptee(DS in 3party.dll) 可抽換了, Main程式完全不認識他.

2. Adaptor(ProcessImpl) 也可抽換了, Main 程式可透過不同的工廠來取得.

3. 有沒有必要都實作? 要看需求, 如果沒有彈性的必要性, 就不需提早過度設計了.

 

討論題:

IInput , IOutput 沒有實質內容的實作, 還有意義嗎? 該重構它並移除嗎?還是很重要一定要留下來呢?

大家來討論吧 :D

 

加強練習題:

如果連Input & Output 都會換來換去的, 那該怎麼彈性它呢?