Ideatryout: типизированный провайдерзавод - PullRequest
2 голосов
/ 09 марта 2010

Я уже пару дней думаю о запуске нового проекта в C # .NET (4.0RC). Конечным результатом проекта будет библиотека классов, состоящая из того, что я пока называю «ProviderFactory», пары интерфейсов (или абстрактных классов) и «ProviderType» по умолчанию или двух.

Прежде всего мне может понадобиться / принять решение изменить наименование моих классов, чтобы они не конфликтовали с тем, что уже есть в .NET. Во-вторых, есть хороший шанс, что то, что я здесь описываю, уже существует (оно может быть даже внутри .NET без моего ведома), так что в этом случае будет полезна отдельная ссылка на этот проект или учебник.

Функциональность, о которой я думаю, это не просто фабрика, которая может предоставить мне простого провайдера, но она должна быть способна генерировать любого типа провайдера. Я хочу иметь возможность написать что-то вроде этого:

BlogProvider bp = ProviderFactory.GetProvider<BlogProvider>();

и должен возвращать текущий выбранный BlogProvider, или ноль, если его нет. Также я хочу иметь возможность предоставить список всех доступных провайдеров одного типа, например,

string[] blogproviders = ProviderFactory.GetRegisteredProviders<BlogProvider>();

и должен возвращать массив строк с блог-провайдерами (например, «ODBCBlogProvider», «XMLBlogProvider» и т. Д.). Мне также нужно иметь возможность установить текущий поставщик, например, так:

ProviderFactory.SetCurrentProvider<BlogProvider>("ODBCBlogProvider");

Это должно сделать текущий BlogProvider ODBCBlogProvider, так что в следующий раз, когда я вызову ProviderFactory.GetProvider (), он должен вернуть экземпляр ODBCBlogProvider (что по причине необходимости расширить базовый класс BlogProvider).

Кроме того, каждый ProviderType (или ProviderBase, если хотите) должен иметь ProviderTypeName (BlogProvider) и ProviderTypeDisplayName («Блог» или «Блог-провайдер»), который уникален для этого ProviderType и каждого провайдера, расширяющего потребности этого провайдера. иметь ProviderName (ODBCBlogProvider) и ProviderDisplayName («ODBC» или «блог ODBC» и т. д.). ProviderName и ProviderTypeName могут просто для простоты быть именами классов, полученными при отражении.

Это, вероятно, означает, что мне нужен интерфейс для ProviderBase, что-то вроде этого:

public interface ProviderBase
{
    string ProviderName { get; } // The display-name.
    string Description { get; } // A description of the provider type, like "This provides you with a blog."
}

А для BlogProvider мне нужно создать абстрактный класс, который реализует эти два члена и создает методы для переопределения реальных BlogProvider.

public abstract class BlogProvider : ProviderBase
{
    public string ProviderName
    {
        get { return "Blog"; }
    }

    public string Description
    {
        get { return "This provides you with a blog."; }
    }

    public abstract int GetPostCount();
}

Затем где-то в моем коде мне нужно было выполнить что-то вроде

ProviderFactory.RegisterProviderType(typeof(BlogProvider));

или, что еще лучше, можно использовать такие атрибуты:

[Provider("Blog", "This provides you with a blog.")]
public interface BlogProvider
{
    int GetPostCount();
}

Я не вижу необходимости использовать абстрактный класс с этим подходом, потому что ни один из членов не должен быть установлен в базовом классе. Также исчезла необходимость в ProviderBase-интерфейсе. Идеально было бы, если бы ProviderFactory мог просто найти все классы / интерфейсы, содержащие ProviderAttribute, а ODBCBlogProvider просто должен был бы реализовать BlogProvider, но я не уверен, как мне это сделать, особенно когда это требование Типы провайдеров и провайдеров могут приходить для внешних сборок.

Другое требование к ProviderFactory - это то, что он должен работать как в среде без состояния, так и в среде, где он поддерживается, поэтому он должен иметь возможность сохранять настройки в некоторый файл конфигурации / поток / базу данных (что-то подобное). , на самом деле не имеет значения) при изменении и загрузке из этого при инициализации, но это должно быть необязательным. Таким образом, он также будет работать в Интернете (наличие блога на собственной машине не имеет большого смысла).

Еще одно требование заключается в том, что поставщики могут требовать других поставщиков для работы. Например, AudiEngine может работать только с AudiCar, однако AudiCar может отлично работать с BmwEngine.

Кроме того (и я пока не уверен, куда это поместить, может быть, необязательный атрибут в самом классе провайдера) у каждого провайдера должна быть опция singleton или нет.

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

Ответы [ 2 ]

2 голосов
/ 09 марта 2010

Ну, это выглядит как сверхпрочное (скорее, чрезмерно причудливое название) IoC / DI контейнер.

Идея IoC почти такая же, за исключением того, что они (контейнеры) не требуют от ваших "соавторов" реализации каких-либо интерфейсов или наследования от базовых классов:

var builder = new ContainerBuilder();
builder.Register<Car>();
builder.Register<Engine>().As<IEngine>();

var container = builder.Build();

// somewhere in the code
var car = container.Resolve<Car>();

Это пример использования autofac , который является одним из множества контейнеров DI для C # /. NET.

0 голосов
/ 10 марта 2010

Как указывает @Anton, это близко к контейнеру IoC / DI. Опять же, используя Autofac, вы можете проделать длинный путь, сделав что-то вроде этого:

var builder = new ContainerBuilder();
builder.Register<Car>();
builder.Register<BmwEngine>();
builder.Register<AudiEngine>();
builder.Register<DefaultEngine>();
builder.Register<EngineSelector>().As<IEngineSelector>().SingletonScope();

builder.Register(
    c => {
            var selector = c.Resolve<IEngineSelector>();
            switch(selector.CurrentEngine)
            {
                case "bmw":
                    return c.Resolve<BmwEngine>();
                case "audi":
                    return c.Resolve<AudiEngine>();
                default:
                    return c.Resolve<DefaultEngine>();
            }
        }
    ).As<IEngine>().FactoryScoped();

var container = builder.Build();

// somewhere in the code
var selector = container.Resolve<IEngineSelector>();
selector.CurrentEngine = "bmw";

var car = container.Resolve<Car>();
...