Мухи и заводская проблема с IDisposable - PullRequest
11 голосов
/ 25 февраля 2010

Я, кажется, мысленно застрял в дилемме модели «Вес».

Сначала, скажем, у меня есть одноразовый тип DisposableFiddle и фабрика FiddleFactory:

public interface DisposableFiddle : IDisposable
{
    // Implements IDisposable
}

public class FiddleFactory
{
    public DisposableFiddle CreateFiddle(SomethingThatDifferentiatesFiddles s)
    {
        // returns a newly created fiddle.
    }
}

Тогда, на мой взгляд, клиенту FiddleFactory совершенно ясно, что фабрика не претендует на право собственности на созданную скрипку и что клиент обязан утилизировать скрипку, когда с ней покончили.

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

public class FiddleFactory
{
    private Dictionary<SomethingThatDifferentiatesFiddles, DisposableFiddle> fiddles = new ...;        

    public DisposableFiddle CreateFiddle(SomethingThatDifferentiatesFiddles s)
    {
        // returns an existing fiddle if a corresponding s is found,
        // or a newly created fiddle, after adding it to the dictionary,
        // if no corresponding s is found.
    }
}

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

Действительно ли проблема в том, что я называю фабрику FiddleFactory вместо, скажем, FiddlePool, а метод "создания" CreateFiddle вместо GetFiddle? Как это:

public class FiddlePool : IDisposable
{
    private Dictionary<SomethingThatDifferentiatesFiddles, DisposableFiddle> fiddles = new ...;        

    public DisposableFiddle GetFiddle(SomethingThatDifferentiatesFiddles s)
    {
        // returns an existing fiddle if a corresponding s is found,
        // or a newly created fiddle, after adding it to the dictionary,
        // if no corresponding s is found.
    }

    // Implements IDisposable
}

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

Или это может быть легко решено только по документации?

Есть ли выход из этой дилеммы? Есть ли даже дилемма? : -)

Ответы [ 3 ]

7 голосов
/ 25 февраля 2010

Я вижу два выхода из этой проблемы:

  • ThreadPool-style : перепроектируйте классы, чтобы FiddlePool предоставлял интерфейс для выполнения сложных задач,Пул не раздает Fiddle экземпляров, потому что вместо него есть метод FiddlePool.PlayFiddle.Так как пул управляет временем жизни скрипта, он отвечает за их удаление.

  • SqlConnection-style : измените открытый метод утилизации Fiddle, чтобы он действительновозвращает скрипты в пул скрипки (который инкапсулирует класс скрипки).Внутри пула скрипок заботится о действительно высвобождении одноразовых ресурсов.

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

Я согласен с вашим вторым мнением. Термины «Пул» и «Получить» действительно делают вещи более понятными для потребителя. Тем не менее, это все еще не дает ясности, и всегда следует добавлять документацию, чтобы обеспечить полное и правильное понимание.

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

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

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

Вы можете сделать то же самое, создав класс PooledFiddle, который переопределяетУтилизировать и вернуть объект в пул.В идеале клиенту даже не нужно было бы знать, что это объединенная скрипка.

...