Чи слід надавати перевагу конвенції щодо програмування?


14

При розробці класу слід підтримувати послідовність у поведінці щодо звичайної практики програмування? Навести конкретний приклад:

Поширена умова така: Якщо класу належить об'єкт (наприклад, він створив його), він відповідає за його очищення, як тільки це буде зроблено. Конкретним прикладом може бути .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у процесорі обробки даних. Це трохи побудовано, але має ілюструвати суть. Існують різні способи виправити це, але все-таки я відчуваю, що працюю над чимось, чого мені не слід.


Ви б не хотіли детальніше розібратися, Чому ілюстрована поведінка StreamWriter заподіює вам біль? Ви хочете споживати той же потік в іншому місці? Чому?
Робота

@Job: Я уточнив своє запитання
ChrisWue

Відповіді:


9

Я також використовую цю техніку, я називаю її «передачею рук», і я вважаю, що вона заслуговує статусу моделі. Коли об'єкт A приймає одноразовий об'єкт B як параметр часу побудови, він також приймає булеву назву 'handoff', (що за замовчуванням відповідає false,), і якщо це правда, то видалення A каскадів утилізацію B.

Я не прихильник розповсюдження невдалого вибору інших людей, тому я ніколи не сприймав би погану практику Майкрософт як усталену конвенцію, і не вважав би сліпих людей після нещасного вибору Microsoft "послідовною поведінкою" будь-яким способом, формою чи формою .


Примітка. Офіційне визначення цього шаблону я написав у своєму дописі на своєму блозі: michael.gr: Шаблон "Handoff" .
Майк Накіс

Як подальше розширення handOffшаблону, якщо таке властивість встановлено в конструкторі, але існує інший метод його модифікації, цей інший метод також повинен приймати handOffпрапор. Якщо handOffдля цього предмета, який зараз утримується, було зазначено, його слід утилізувати, тоді новий товар повинен бути прийнятий і внутрішній handOffпрапор оновлений, щоб відображати статус нового товару. Метод не повинен перевіряти старі та нові посилання на рівність, оскільки, якщо початкова посилання була "передана", всі посилання, що зберігаються оригінальним абонентом, слід відмовитися.
supercat

@supercat Я погоджуюся, але у мене є проблема з останнім реченням: якщо передача правдива для предмета, який зараз утримується, але нещодавно поставлений предмет дорівнює до предмета, який зараз утримується, то розпоряджається предметом, який зараз утримується фактично розміщує щойно поставлений товар. Я думаю, що повторна доставка того ж товару повинна бути незаконною.
Майк Накіс

Повторне постачання одного і того ж предмету, як правило, є незаконним, якщо об'єкт є незмінним, насправді не містить жодних ресурсів і не хвилює, чи він утилізується. Наприклад, якщо Disposeзапити були проігноровані для системних пензлів, метод "color.CreateBrush" може у вільний час або створити нову пензлик (що вимагатиме видалення), або повернути системну щітку (що ігнорує розпорядження). Найпростіший спосіб запобігти повторне використання об'єкта, якщо він піклується про утилізацію, але дозволити повторне використання, якщо цього не відбувається, - розпоряджатися ним.
supercat

3

Я думаю, ви праві, якщо чесно. Я думаю, що Microsoft переплутався з класом StreamWriter, зокрема, з описаних вами причин.

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


2

Я б пішов з послідовністю. Як ви згадали більшість людей, має сенс, що якщо ваш об’єкт створює дочірній об’єкт, він буде ним володіти і знищувати. Якщо йому надається посилання ззовні, в якийсь момент "зовнішній" також тримає один і той же об'єкт, і йому не слід володіти. Якщо ви хочете перенести право власності ззовні на ваш об'єкт, використовуйте методи Attach / Detach або конструктор, який приймає булеву форму, яка дає зрозуміти, що право власності передано.

Набравши все це для повного відповіді, я думаю, що більшість загальних моделей дизайну не обов'язково потраплятимуть до тієї ж категорії, що і класи StreamWriter / StreamReader. Я завжди думав, що причина, через яку MS вирішили автоматично перейняти право власності на StreamWriter / Reader, полягає в тому, що в даному конкретному випадку спільне володіння не має великого сенсу. Ви не можете одночасно читати / записувати в один і той же потік два різні об’єкти. Я впевнений, що ви можете написати якусь детерміновану частку часу, але це було б пекельним анти-зразком. Тож, мабуть, тому вони сказали, оскільки немає сенсу ділитися потоком, давайте просто завжди його перебирати. Але я б не вважав, що така поведінка колись стала загальною конвенцією для всіх класів.

Ви можете вважати Потоки послідовною схемою, коли об’єкт, що передається, не є спільним з точки зору дизайну, і тому без додаткових прапорів читач / автор просто бере на себе право власності.


Я уточнив своє запитання - з послідовністю мав на увазі поведінку завжди брати участь у власності.
ChrisWue

2

Будь обережний. Те, що ви вважаєте «послідовністю», може бути просто випадковим набором звичок.

"Координаційний інтерфейс користувача для узгодженості", Бюлетень SIGCHI 20, (1989), 63-65. Вибачте, немає посилання, це стара стаття. "... дводенний семінар з 15 експертів не зміг дати визначення послідовності". Так, мова йде про "інтерфейс", але я вважаю, що якщо ви думаєте про "послідовність" на деякий час, ви виявите, що і ви не можете його визначити.


Ви маєте рацію, якщо експерти збираються з різних кіл, але, розробляючи код, мені було легко дотримуватися певної моделі (або звички, якщо Ви хочете), з якою моя команда вже знайома. Наприклад, днями один з наших хлопців розпитував про деякі асинхронні операції, які ми мали, і коли я сказав йому, що вони поводяться так само, як Win32 Overlapped I / O, за 30 секунд він зрозумів весь інтерфейс ... в цьому випадку обидва Я і він прийшли з одного сервера заднього кінця фон Win32. Консистенція ftw! :)
DXM

@Bruce Я розумію, я вважав, що зрозуміло, що я маю на увазі з послідовністю, але, мабуть, це не так.
ChrisWue
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.