При разработке класса следует ли согласованность поведения предпочтительнее обычной практики программирования? Чтобы привести конкретный пример:
Общее соглашение таково: если классу принадлежит объект (например, он его создал), он отвечает за его очистку после завершения. Конкретным примером может быть .NET, если ваш класс владеет IDisposable
объектом, он должен утилизировать его в конце своей жизни. И если у вас нет этого, не трогайте его.
Теперь, если мы посмотрим на StreamWriter
класс в .NET, мы можем найти в документации, что он закрывает основной поток, когда он закрывается / удаляется. Это необходимо в тех случаях, когда StreamWriter
создается экземпляр путем передачи имени файла, поскольку модуль записи создает базовый файловый поток и, следовательно, должен его закрывать. Однако можно также передать во внешний поток, который автор также закрывает.
Это раздражало меня кучу раз (да, я знаю, что вы можете сделать не закрывающуюся оболочку, но это не главное), но, очевидно, Microsoft приняла решение, что более последовательно всегда закрывать поток независимо от того, откуда он пришел.
Когда я сталкиваюсь с таким шаблоном в одном из моих классов, я обычно создаю ownsFooBar
флаг, который устанавливается в false в тех случаях, когда FooBar
вводится через конструктор, и в true в противном случае. Таким образом, ответственность за его очистку передается вызывающей стороне, когда он передает экземпляр явно.
Теперь мне интересно, может быть, последовательность должна быть в пользу перед лучшей практикой (или, может быть, моя лучшая практика не настолько хороша)? Есть аргументы за / против?
Изменить для уточнения
Под «согласованностью» я подразумеваю: согласованное поведение класса, всегда берущее на себя ответственность (и закрывающее поток), против «наилучшей практики» - владеть объектом только в том случае, если вы его создали или явно передали право владения.
Как для примера, где это enoying:
Предположим, у вас есть два заданных класса (из какой-то сторонней библиотеки), которые принимают поток, чтобы что-то с ним делать, например, создавать и обрабатывать некоторые данные:
public class DataProcessor
{
public Result ProcessData(Stream input)
{
using (var reader = new StreamReader(input))
{
...
}
}
}
public class DataSource
{
public void GetData(Stream output)
{
using (var writer = new StreamWriter(output))
{
....
}
}
}
Теперь я хочу использовать это так:
Result ProcessSomething(DataSource source)
{
var processor = new DataProcessor();
...
var ms = new MemoryStream();
source.GetData(ms);
return processor.ProcessData(ms);
}
Это не удастся, за исключением Cannot access a closed stream
обработки данных. Это немного сконструировано, но должно проиллюстрировать это. Существуют различные способы исправить это, но, тем не менее, я чувствую, что работаю над тем, что мне не нужно.
Ответы:
Я тоже использую эту технику, я называю это «передачей», и я считаю, что она заслуживает статуса паттерна. Когда объект A принимает одноразовый объект B в качестве параметра времени строительства, он также принимает логическое значение с именем 'handoff' (по умолчанию false), и если оно истинно, то удаление A каскадируется для удаления B.
Я не поддерживаю распространение неудачных решений других людей, поэтому я бы никогда не принял плохую практику Microsoft как устоявшуюся конвенцию, и я бы не стал считать слепое следование другими лицами неудачных решений Microsoft «последовательным поведением» в любой форме, форме или форме. ,
источник
handOff
шаблона, если такое свойство установлено в конструкторе, но существует другой метод для его изменения, этот другой метод также должен принятьhandOff
флаг. ЕслиhandOff
был указан для текущего элемента, он должен быть утилизирован, тогда новый элемент должен быть принят, а внутреннийhandOff
флаг обновлен, чтобы отразить статус нового элемента. Метод не должен проверять на совпадение старые и новые ссылки, поскольку, если исходная ссылка была «передана», все ссылки, хранящиеся у исходного вызывающего, должны быть отброшены.Dispose
запросы к системным кистям игнорировались, метод "color.CreateBrush" может на досуге либо создать новую кисть (которая потребует удаления), либо вернуть системную кисть (которая будет игнорировать удаление). Самый простой способ предотвратить повторное использование объекта, если он заботится об удалении, но разрешить повторное использование, если это не так, значит избавиться от негоЯ думаю, что вы правы, если честно. Я думаю, что Microsoft перепутала класс StreamWriter, в частности, по причинам, которые вы описываете.
Однако с тех пор я видел много кода, где люди даже не пытаются избавиться от своего собственного потока, потому что фреймворк сделает это за них, поэтому исправление его сейчас принесет больше вреда, чем пользы.
источник
Я бы пошел с последовательностью. Как вы упомянули для большинства людей, имеет смысл, что если ваш объект создает дочерний объект, он будет владельцем и уничтожит его. Если ему дается ссылка извне, в какой-то момент времени «извне» также содержит тот же объект, и он не должен принадлежать. Если вы хотите передать владение извне в ваш объект, используйте методы Attach / Detach или конструктор, который принимает логическое значение, которое дает понять, что владение передано.
Напечатав все это для полного ответа, я думаю, что большинство общих шаблонов проектирования не обязательно попадут в ту же категорию, что и классы StreamWriter / StreamReader. Я всегда думал, что причина, по которой MS выбрала StreamWriter / Reader для автоматического захвата владения, заключается в том, что в этом конкретном случае совместное владение не имеет большого смысла. Вы не можете одновременно читать / записывать два разных объекта из одного потока. Я уверен, что вы могли бы написать какую-то детерминистическую долю времени, но это был бы адский паттерн. Поэтому, вероятно, поэтому они и сказали, так как нет смысла делиться потоком, давайте всегда будем его принимать. Но я бы не стал считать, что такое поведение когда-либо стало общим соглашением для всех классов.
Вы можете считать Streams последовательным шаблоном, в котором передаваемый объект не является разделяемым с точки зрения проектирования, и поэтому без каких-либо дополнительных флагов устройство чтения / записи просто переходит во владение.
источник
Быть осторожен. То, что вы считаете «последовательностью», может быть просто случайным набором привычек.
«Координация пользовательских интерфейсов для согласованности», SIGCHI Bulletin 20, (1989), 63-65. Извините, нет ссылки, это старая статья. «... двухдневный семинар из 15 экспертов не смог дать определение последовательности». Да, речь идет об «интерфейсе», но я считаю, что если вы некоторое время подумаете о «согласованности», то обнаружите, что тоже не можете ее определить.
источник