Каков шаблон дизайна в этом коде? - PullRequest
1 голос
/ 12 февраля 2010

Скажем, у меня есть класс singleton-ish, factory-ish, mirror-ish, который получает некоторые входные данные и возвращает новый экземпляр конкретной реализации некоторого интерфейса. Что это за дизайн? Есть ли лучший способ сделать то, что я хочу?

Вот некоторый код для иллюстрации:

using System;
using System.Collections.Generic;

// static factory class
public static class ArticleFactory 
{
    // given an SKU, store the Type object for an IArticle object
    private static Dictionary<string, Type> articleRegistry = new Dictionary<string, Type>();

    // allow public registration of SKU-to-Type object relationships
    public static bool Register(string sku, Type typeInfo)
    {
        if(!articleRegistry.ContainsKey(sku)) 
        {
            articleRegistry.Add(sku, typeInfo);
            return true;
        }
        return false;
    }

    // given a SKU, give me an instance of the related IArticle object
    public static IArticle NewArticle(string sku)
    {
        if(articleRegistry.ContainsKey(sku))
        {
            // use reflection to invoke the default constructor
            return articleRegistry[sku].GetConstructor(Types.EmptyTypes).Invoke(null) as IArticle;
        }
        return null;
    }
}

// example concrete-implementation of an IArticle
public class Jeans : IArticle 
{
    public decimal GetPrice() {  return SomeDecimal(); }
}

// WHERE DO I CALL THIS LINE? 
ArticleFactory.Register("0929-291", typeof(Jeans)); 

// Later on, if another group needs to write the class for Snowboards, 
// how can they self-register their class, without changing any "Main()"
// or "Page_Init()" function?

Ответы [ 4 ]

2 голосов
/ 12 февраля 2010

Я не знаю, есть ли у него «имя» как таковое, но похоже, что это какой-то ручной сервисный решатель. Проблема, которую я вижу (из опыта, к сожалению), заключается в том, что она негибкая в реальном выражении, в том числе:

  • регистрация имеет только одну конфигурацию
  • трудно провести юнит-тест

Лично я бы посмотрел на контейнер IoC, если бы делал это в новой системе; IoC может справиться с этим отношением и предоставить гораздо больше возможностей бесплатно (время жизни, обогащение, дополнительная настройка и т. д.) и решить множество связанных с этим проблем.

Кстати, может быть проще:

return Activator.CreateInstance(articleRegistry[sku]);
2 голосов
/ 12 февраля 2010

Похоже, вы уже определили шаблон. Это шаблон фабричного метода . Или, скорее, несколько недоделанная реализация одного. Немного лучшим подходом было бы сначала сделать его интерфейсом:

public interface IArticleFactory
{
    IArticle CreateArticle(string sku);
}

Затем реализуем фабрику без какого-либо отражения:

public class MyArticleFactory
{
    private Dictionary<string, Func<IArticle>> instantiators =
        new Dictionary<string, Func<Iarticle>>();

    public MyArticleFactory()
    {
        Register("Jeans", () => new Jeans());
        Register("Shirt", () => new Shirt());
        // etc.
    }

    public IArticle CreateArticle(string sku)
    {
        Func<IArticle> instantiator;
        if (creators.TryGetValue(sku, out instantiator))
            return instantiator();
        throw new UnknownSkuException(sku);
    }

    protected void Register(string sku, Func<IArticle> instantiator)
    {
        creators.Add(sku, instantiator);
    }
}

Несколько важных отличий:

  • Регистрация не является публичной и не должна быть. Обычно регистрация либо находится в файле конфигурации, либо является частной.

  • Не требуется, чтобы конкретные типы IArticle имели конструктор по умолчанию без параметров. Это может легко зарегистрировать статьи с параметризованными конструкторами (при условии, что они знают, какие параметры использовать).

  • Выдает исключение при повторных регистрациях. Мне не нравится идея простого возвращения false; если вы попытаетесь зарегистрировать один и тот же фабричный метод дважды, это следует считать ошибкой.

  • Это не статично. Вы можете заменить этот завод другим заводом. Вы можете протестировать его.

Конечно, еще лучшим подходом было бы просто использовать любое из множества существующих .NET инъекций / инверсий зависимостей, таких как Ninject или AutoFac .

1 голос
/ 12 февраля 2010

Я думаю, что вы здесь делаете в основном инъекцию зависимости (или Inversion of Control, как ее называют крутые дети). Посмотрите эти ссылки:

Объяснение из Википедии: http://en.wikipedia.org/wiki/Dependency_Injection

Две структуры DI .Net:

StructureMap: http://structuremap.sourceforge.net/QuickStart.htm

Замок Виндзор: http://www.castleproject.org/container/index.html

0 голосов
/ 12 февраля 2010

Это просто фабричный шаблон, который использует отражение в своей реализации. Однако вместо использования отражения лучше было бы просто поместить экземпляры фабричных классов непосредственно в словарь, хотя для этого может потребоваться некоторый шаблонный код.

...