Проблема мухи и Factory с IDisposable

Кажется, я мысленно застрял в дилемме "Веселый шаблон".

Во-первых, скажем, у меня есть одноразовый тип DisposableFiddle и factory FiddleFactory:

public interface DisposableFiddle : IDisposable
{
    // Implements IDisposable
}

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

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

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

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

Является ли проблема фактически тем, что я называю factory 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
}

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

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

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

Ответ 1

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

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

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

Ответ 2

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

Ответ 3

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

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

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