2010-06-09 16 views
1

Вот кратко, что я пытаюсь сделать.Какую модель дизайна я должен использовать?

Пользователь поставляет мне ссылку на photo с одного из нескольких сайтов обмена фотографиями (таких как Flickr, Zooomr и др.). Затем я делаю некоторую обработку на фотографии, используя их соответствующие 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); 

Научиться считать - один, два, много. Когда вы нажмете многих, подумайте о рефакторинге к шаблону. Возможно, вам не удалось реализовать более двух сервисов, поскольку вы работаете над каркасом для них, вместо того, чтобы делать что-то полезное.

Если вы сделать есть потребность в более динамичных услуг, а также может выбрать на основе только домен верхнего уровня, то я бы, наверное, есть карта - это 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 ... –

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() 
      ......   
    } 
} 

Если вы используете несколько методов, то структура в основном та же, но не называется стратегия, это просто объект делегата.