2010-02-25 5 views
11

Кажется, что я мысленно застрял в дилемме «Веселый шаблон».Проблема с мухой и фабрикой с IDisposable

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

public interface DisposableFiddle : IDisposable 
{ 
    // Implements IDisposable 
} 

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

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

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

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 
} 

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

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

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

ответ

7

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

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

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

+0

Спасибо, первый лучше следует за Деметрыем, а второй лучше подходит в моем общем дизайне. Хммм ... –

+0

Я поставлю свои два цента за первый подход - я видел, как слишком много людей пытаются написать свой собственный пул SqlConnnection! (Не совсем, но я * должен был объяснить, почему это не нужно.) –

2

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

+3

Мои мысли точно. Кроме того, я бы, вероятно, не назвал ваш класс DisposableFiddle, если вы не хотите, чтобы пользователь Dispose() это ... –

+0

@Reed: Согласовано об одноразовом значении. Правильное именование имеет решающее значение для передачи намерений и является первой линией защиты от неправильного использования. – jrista

+0

Хе-хе, да, я просто добавил одноразовые к имени примера, чтобы было легче следовать по моим (путаным) шагам ... –

1

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

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

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

+0

«В идеале клиент даже не должен был знать, что это объединенный скрипт». - Да, я согласен с этим. Я думаю, что это деталь реализации, которая лучше скрыта. –