2010-06-09 82 views
1

這裏簡要介紹一下我想要做的事情。我應該使用哪種設計模式?

用戶向我提供指向幾個照片共享網站(如Flickr,Zooomr等)之一的photo的鏈接。然後,我使用各自的API對照片進行一些處理。

現在,我只實施一項服務,但我很可能在不久的將來增加更多。

我不想有一堆if/else或switch語句來定義不同網站的邏輯(但也許這是必要的?)我寧願只調用GetImage(url)並讓它得到我來自url的域名來自任何服務的圖片。我很困惑如何設計GetImage函數和類。

也許我需要戰略模式?我仍然在閱讀並試圖理解各種設計模式,以及如何在這種情況下做出合適的設計。我在C#中這樣做,但這個問題是語言不可知的。

回答

3

你並不真正需要的模式呢。只需以乾淨的方式實施該服務,以便在某個地方使用Image FlikrApi.GetImage(Url)方法。

// client code 
Image image = flickApi.GetImage(url); 

當你來實現你的第二個服務,那麼你將對如何根據Url決定調用哪個函數有一些要求。然後,您可以決定如何執行此操作 - 對於兩種服務,它可能與切換頂級域名一樣簡單。所以你有一個開關在一個或另一個對象上調用一個或另一個方法。

readonly FlickrApi flikrApi = new FlickrApi(); 
readonly WhateverApi whateverApi = new WhateverApi(); // third party 

Image GetImage (Url uri) { 
    switch (url.TopLevelDomain()) { 
     case "flickr.com": 
      return flikrApi.GetImage(url); 
      break; 
     case "whatever.com": 
      return whateverApi.GetWhateverImage(url); 
      break; 
     default: 
      throw new UnhandledUriException(uri); 
} 

// client code 
Image image = GetImage(uri); 

學會數數 - 一,二,很多。當你點擊很多時,然後考慮重構一個模式。這可能是因爲你正在爲他們開發一個框架,而不是做一些有用的事情,所以你不能實現兩個以上的服務。

如果有更動態的服務需求,並可選擇僅基於頂級域名,然後我可能有一個地圖 - 填充TLD和服務代表一個Dictionary<string, Func<Url,Image>>

readonly Dictionary<string, Func<Url,Image>> apis = new ...; 

ImageApi() { 
    apis["flickr.com"] = new FlickrApi().GetImage; 
    apis["whatever.com"] = new WhateverApi().GetWhateverImage; 
    apis["zzze.com"] = (uri) => Zoobers.GetWhateverImage(new ZooberCreds(), uri.ToString()); 
} 

static Image GetImage (Url uri) { 
    string tld = urli.TopLevelDomain(); 
    if (!imageApis.ContainsKey(tld)) throw new UnhandledUriException(uri); 
    return imageApis[tld](uri); 
} 

// client code unchanged 

在沒有關閉/代表一種語言,你會定義一個接口,並使用這些,但是C#是比這更好的,並使用內置函數類型,可以使用任何適當的功能,而不是創建一個類只是爲了符合一個接口。這不完全是戰略模式,因爲在上下文和戰略之間沒有結構關係 - 在戰略模式中,有一個上下文有 - 一個戰略,一個戰略而且這個戰略可以改變。我們在這裏根據簡單的條件選擇策略。

如果您在決定使用哪種服務時有更復雜的要求,那麼您最終可能會遍歷接口列表IImageApi,其中接口包括一個bool HandlesUrl(Url)方法來詢問服務是否識別Url。在這種情況下,您不必使用代理與任何第三方代碼進行對話,而必須使用包裝。

interface IImageApi { 
    bool HandlesUri(Url); 
    Image GetImage(Url); 
} 

readonly List<IImageApi> apis = new ...; 

ImageApi() { 
    apis.Add(new FlickrApi()); // you wrote this, so it can implement the interface 
    apis.Add(new WhateverApiAdapter()); // third party requires adapter 
    apis.Add(new ZoobersApiAdapter()); // ditto 

    // or you can use something like MEF to populate the list 
} 

static Image GetImage (Url uri) { 
    foreach (var api in apis) 
     if (api.HandlesUri(uri)) 
      return api.GetImage(uri); 

    throw new UnhandledUriException(uri); 
} 

// client code unchanged 

做最簡單的事情,第二最簡單的事情第二,如果你必須最複雜的事情。

+0

+1同意。希望我有超過1個upvote ... – 2010-06-10 00:42:14

2

是的,Strategy聽起來像是一個很好的解決方案。替代方案可能是Template Method

與不同網站相關的策略可以由Abstract Factory/Factory Method生成,但是如果它們是無狀態的,它們也可以簡單地存儲在地圖中,其中鍵是網站URL的相關部分。

1

看起來你想要做的兩件事情:

  1. 爲每個服務單獨處理的實現。在這裏,您可以使用策略模式。
  2. 隱藏來自您調用者的每項服務都有不同處理實施細節的事實。在這裏,使用工廠模式。工廠模式將封裝您編寫的任何邏輯來區分服務,並構建適當的策略。

一般來說,我建議你延遲抽象和使用設計模式,以幫助你 - 不同的模式解決不同的問題。一堆讓你的代碼更難讀的設計模式並沒有太多的用處(除非你只是想了解設計模式)。

其他模式可能對您有用:

  • Adapter Pattern - 如果要定義自己的API,和「適應」每個特定服務的API,以適應自己有用的。
  • Template Method Pattern - 如果你的算法保持基本不變,但你需要特定部分取決於實施
1

是,策略模式是一個不錯的選擇,是「可插拔的」有用的。

您應該定義一個interface來定義您自己的API,然後在幾種策略中實現它。

採取維基百科的C#示例一看:Strategy pattern

interface IStrategy 
    { 
    void Execute(); 
    } 

    // Implements the algorithm using the strategy interface 
    class ConcreteStrategyA : IStrategy 
    { 
    public void Execute() 
    { 
     Console.WriteLine("Called ConcreteStrategyA.Execute()"); 
    } 
    } 

    class ConcreteStrategyB : IStrategy 
    { 
    public void Execute() 
    { 
     Console.WriteLine("Called ConcreteStrategyB.Execute()"); 
    } 
    } 
1

如果您只使用一種方法GetImage那麼策略是一個不錯的選擇,您可以使用Factory方法來設置正確的工廠。

class Context 
{ 
    IStrategy strategy; 
    String url; 

    void GetImage(url) 
    { 
      strategy = StrategyFactory.GetStrategyFor(url); 
      image = strategy.GetImage(url); 
    } 
} 
IStrategy 
{ 
    Image GetImage(string url); 
} 
FlickrStrategy:IStrategy 
{ 
    Image GetImage(string url) 
    { 
      FlickerService service = new FlickrService(); 
      service.authenticate(); 
      // 
      return service.get(url); 
    } 
} 

ImageShackUs:IStrategy 
{ 
    Image GetImage(string url) 
    { 
      HttpClient service = new HttpClient(url); 
      service.Connect(); 
      byte [] data = service.read() 
      ......   
    } 
} 

如果您使用更多的方法,那麼結構基本相同,但不稱爲策略,它是簡單的委託對象。

相關問題