Предоставляет ли MEF какое-либо значение шаблону Singleton? - PullRequest
5 голосов
/ 19 декабря 2010

Я работаю над проектом MEF, чтобы изучить методы использования и реализации. Моя первая фаза открытия заключается в реализации динамически конфигурируемого и централизованного контроллера данных. Один из способов настройки поведения - это наследование предоставленного мною класса, который применяет правило сингулярности. Несмотря на то, что шаблон Singleton очень злонамерен при его использовании, я, возможно, нашел реализацию, которая может до некоторой степени проверять существование этого шаблона.

Ситуация

Предположим, что модуль управления данными ( DataController ), импортированный хостом , предназначен для обеспечения общего канала для баз данных по запросу с помощью родственных модулей. Мне нужен только один DataController, и чтобы его можно было объединить в модуль, DataController должен реализовывать IDataController. Реализация DataProvider в качестве базового класса является чисто дополнительной; однако для получения данных из DataProvider потребуется дополнительная обработка.

Наблюдения

Сбор фактов :

  • Статический класс не может реализовать или расширить абстрактные классы или интерфейсы. Этот факт один исключает использование статического класса чтобы обеспечить исключительное существование DataController.

  • DataController, реализующий Шаблон синглтона обеспечит единственное существование в приложении домен. Там нет никаких ограничений на DataController; разрешено наследовать требуемый интерфейс для импорта и составлено в Host.

  • Учитывая происхождение DataController, стандартная реализация для Синглтон может оказаться сложные в тех же случаях. Предлагаемая библиотека данных обеспечивает как общедоступные классы: IDataController и реферат DataProvider. Для обеспечения единого экземпляр производного DataController, реализация потребует некоторых отклонение от нормы.

Решение

На данный момент решение кажется ясным. Реализация шаблона Singleton базовым классом DataHandler. Я не настолько наивен, чтобы думать, что есть другие способы сделать это. Но вот мои грубые ожидания о том, как реализовать шаблон:

// DataLibrary referenced by Host
public interface IDataController
{ 
    IDataController Start();
    DbConnection CreateConnection<TDbConnection>(params string[] args)
        where TDbConnection : DbConnection, IDbConnection;
}

public abstract class DataProvider
{

    // singleton implementation
    private static IDataController dcInstance;

    protected static IDataController Instance
    {
        get{ return dcInstance; }
    }
    // ========================

    abstract IDataController CreateController();

    protected IDataController instanceController<TDataController>()
        where TDataController : IDataController, new()
    {
        return new TDataController ();
    }
}

// references DataLibrary
[Export(typeof(IDataController))]
public class DataController : DataProvider, IDataController
{
    public IDataController Start()
    {
         return CreateController();
    }

    protected override IDataController CreateController()
    {
        return instanceController<DataController>();
    }

    public SqlConnection CreateConnection(params string[] args)
    {
        // instance and return new SqlConnection 
    }
}

Имейте в виду, что я работал над этим - прочитал, теоретизировал - и не завершил реализацию. Скорее всего, будут некоторые обновления, поскольку я отлаживаю любые проблемы.

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

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

Есть ли более практичная реализация, отвечающая моим требованиям? * Это правильная реализация шаблона Singleton в этом случае? * Придает ли эта реализация какое-либо значение существованию шаблона?

Ответы [ 2 ]

18 голосов
/ 19 декабря 2010

Если вы хотите применить тот факт, что в контейнере существует только один экземпляр класса, вы можете просто установить «общую» политику создания части:

[Export(typeof(IDataController))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class DataController : IDataController
{
    ...
}

Каждая часть импортирует IDataController затем получит тот же экземпляр.Обратите внимание, что это уже стандартное поведение в MEF, если вы не указали политику создания деталей на стороне импорта или экспорта.

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

builder.Register(c => new DataController())
    .As<IDataController>().SingleInstance();
0 голосов
/ 19 декабря 2010

Если у вас нет большего кода реализации, которым могли бы делиться все производные классы из DataProvider, вы можете просто отказаться от своего абстрактного класса.Эта реализация гарантирует многопоточность и использует ленивую конструкцию без использования блокировок.Однако требуется .NET 4.

public interface IDataController
{
    DbConnection CreateConnection<TDbConnection>(params string[] args)
        where TDbConnection : DbConnection, IDbConnection;
}

[Export(typeof(IDataController))]
public class DataController : IDataController
{
    // singleton implementation
    private static volatile Lazy<IDataController> _ControllerInstance = new Lazy<IDataController>(() => new DataController());

    public static IDataController ControllerInstance
    {
        get { return _ControllerInstance.Value; }
    }

    public DbConnection CreateConnection<TDbConnection>(params string[] args) 
        where TDbConnection : DbConnection, IDbConnection
    {
        throw new NotImplementedException();
    }
}
...