Я, кажется, мысленно застреваю в дилемме шаблона В наилегчайшем весе.
Во-первых, скажем, у меня есть доступный тип 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
}
Тогда клиенту более ясно, что это не будет владеть возвращенной скрипкой, и это - обязанность пула расположить скрипки.
Или это может только быть с готовностью решено мудрое документацией?
Существует ли выход из дилеммы? Существует ли даже дилемма?:-)
Существует 3 типа указателей const:
//Data that p points to cannot be changed from p
const char* p = szBuffer;
//p cannot point to something different.
char* const p = szBuffer;
//Both of the above restrictions apply on p
const char* const p = szBuffer;
Метод # 2 выше наиболее похож на ссылку.
Существуют ключевые различия между ссылками и указателями const всех 3 типов:
Указатели const могут иметь значение NULL.
Ссылка не имеет собственного адреса, в то время как указатель имеет.
Адрес ссылки - это фактический адрес объекта.
Указатель имеет свой собственный адрес и сохраняет в качестве своего значения адрес значения, на которое он указывает.
Смотрите мой ответ здесь для гораздо больше различий между ссылками и указателями .
Я предполагаю, что вы имеете в виду указатель на конст (например, int * const ptr), а не указатель на конст (например, int const * ptr).
Я вижу два выхода из этой проблемы:
ThreadPool-style : перепроектировать классы, чтобы FidingPool
обеспечивал интерфейс для точного выполнения задач. Пул не выдает экземпляры Fiddle
, поскольку вместо них используется метод FidingPool.PlayFiddle
. Так как пул контролирует время жизни, он отвечает за их утилизацию.
SqlConnection-style : изменение метода открытого удаления Fiddle
так, что он действительно просто возвращает fiddle в пул fiddle (который инкапсулирует класс fiddle). Внутри пула fiddle заботится о действительно высвобождении одноразовых ресурсов.
Вы должны сделать что-то большее, чем просто документация и методы именования, чтобы сказать клиентам не вызывать dispose. На самом деле было бы лучше, чтобы клиенты вызывали dispose, чтобы создать шаблон использования. Возьмите некоторое направление от наших пулов подключения к базе данных.
База данных объединяет множество соединений, которые сами поддерживают пул. Вызывающий код создает соединение, открывает его и вызывает для него close (dispose). Вызывающий код даже не знает, объединен ли он в пул или нет, все это обрабатывается внутри класса соединения. При объединенном соединении вызов Open () игнорируется, если соединение уже открыто. Вызывается Close () / Dispose (), и в случае объединения в пул это фактически возвращает соединение обратно в пул, а не закрывает его.
Вы можете сделать то же самое, создав класс PooledFiddle, который переопределяет Dispose и возвращает объект в пул. В идеале клиенту даже не нужно было бы знать, что это объединенная скрипка.
Я согласен с вашим вторым мнением. Термины «пул» и «получить» делают вещи более понятными для потребителя. Тем не менее, это все еще недостаточно проясняет ситуацию, и всегда следует добавлять документацию, чтобы обеспечить полное и достоверное понимание.