Яка ідея іменування класів із суфіксом "Інформація", наприклад: "SomeClass" та "SomeClassInfo"?


20

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

Враховуючи, що фактичні пристрої (датчики та приймачі) - це одне, а їх представлення в програмному забезпеченні - інше, я думаю про те, щоб назвати деякі класи з шаблоном назви суфікса "Інформація".

Наприклад, хоча a Sensorбуде класом, який представляє фактичний датчик (коли він фактично підключений до якогось робочого пристрою), SensorInfoвикористовувався б для представлення лише характеристик такого датчика. Наприклад, при збереженні файлу я б серіалізував a SensorInfoдо заголовка файлу, замість серіалізації a Sensor, який би навіть не мав сенсу.

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

Крім того, занадто поширений приклад Employeeкласу, очевидно, є лише представленням реальної людини, але ніхто не запропонував би назвати клас EmployeeInfoзамість цього, наскільки я знаю.

Мовою, якою я працюю, є .NET, і ця модель іменування, як видається, є загальною у всіх рамках, наприклад для цих класів:

  • Directoryі DirectoryInfoзаняття;
  • Fileі FileInfoзаняття;
  • ConnectionInfoклас (без відповідного Connectionкласу);
  • DeviceInfoклас (без відповідного Deviceкласу);

Отже, моє запитання: чи існує загальне обгрунтування використання цього шаблону іменування? Чи є випадки, коли є сенс мати пари імен ( Thingі ThingInfo) та інші випадки, коли існує лише ThingInfoклас або Thingклас без його аналога?


5
Бен Аронсон зрозумів це правильно у своїй відповіді нижче; Infoсуфікс відрізняє staticклас , що містить допоміжні методи від свого аналога з урахуванням стану. Це не «найкраща практика» як така; це лише спосіб, який придумала команда .NET для вирішення певної проблеми. Вони могли б так само легко придумати FileUtilityі File, але File.DoSomething()і , FileInfo.FileNameздається, краще читати.
Роберт Харві

1
Варто зазначити, що це загальна закономірність, але з такою кількістю різних умов іменування, скільки є мов та API. Наприклад, у Java, якщо у вас є клас стану Foo, можливо, у вас є неіснуючий клас корисності Foos. Що стосується імен, важливим є послідовність в API та в ідеалі для API на платформі.
Кевін Крумвієде

1
Дійсно? "Ніхто не запропонував би назвати клас EmployeeInfo" ?? НІКОЛИ? Це здається трохи сильним для чогось не такого очевидного.
ThePopMachine

@ThePopMachine Я погоджуюся, що слово "ніхто" в цьому випадку може бути занадто важким, але Employeeдесятки прикладів знаходять десятки або в Інтернеті, або в класичних книгах, поки я цього ще не бачив EmployeeInfo(можливо, тому що працівник - жива істота, а не технічна конструкція, така як з'єднання або файл). Але, погоджено, якщо клас EmployeeInfoзапропонувати в рамках проекту, я вважаю, що це може мати користь.
heltonbiker

Відповіді:


21

Я думаю, що "інформація" - це неправильне слово. Об'єкти мають стан та дії: "інформація" - це лише інша назва "стан", яка вже виведена в ООП.

Що ви насправді намагаєтеся моделювати тут? Вам потрібен об'єкт, який представляє апаратне забезпечення в програмному забезпеченні, щоб інший код міг ним користуватися.

Це легко сказати, але, як ви з'ясували, тут є більше, ніж це. "Представлення обладнання" напрочуд широке. Об'єкт, що робить це, має декілька проблем:

  • Зв'язок пристроїв низького рівня, будь то розмова з інтерфейсом USB, послідовним портом, TCP / IP або власницьким з'єднанням.
  • Керуюча держава. Увімкнено пристрій? Готові поговорити з програмним забезпеченням? Зайнято?
  • Поводження з подіями. Пристрій видав дані: тепер нам потрібно генерувати події для передачі іншим зацікавленим класам.

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

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

Ось приклад того, як я б створив ієрархію класів для датчика температури:

  • ITemperatureSource: інтерфейс, який представляє все, що може виробляти дані про температуру: датчик, може навіть бути обгорткою файлів або жорстко кодованими даними (подумайте: макетне тестування).

  • Acme4680Sensor: Датчик моделі 4680 ACME (відмінно підходить для виявлення, коли Roadrunner знаходиться поруч). Це може реалізувати кілька інтерфейсів: можливо, цей датчик визначає і температуру, і вологість. Цей об'єкт містить стан програмного рівня, такий як "підключений датчик?" і "що було останнє читання?"

  • Acme4680SensorComm: використовується виключно для спілкування з фізичним пристроєм. Це не дуже підтримує стан. Він використовується для надсилання та отримання повідомлень. Він має метод C # для кожного з повідомлень, якими розбирається обладнання.

  • HardwareManager: використовується для отримання пристроїв. Це, по суті, фабрика, що кешує екземпляри: для кожного апаратного пристрою повинен бути лише один екземпляр об'єкта пристрою. Це повинно бути досить розумним, щоб знати, що якщо потік A вимагає датчика температури ACME, а нитка B запитує датчик вологості ACME, вони насправді є одним і тим же об'єктом і повинні бути повернені в обидва потоки.


На верхньому рівні у вас будуть інтерфейси для кожного типу обладнання. Вони описують дії, які ваш C # код буде виконувати на пристроях, використовуючи типи даних C # (не, наприклад, байтові масиви, якими може користуватися необроблений драйвер пристрою).

На тому ж рівні у вас є клас перерахунку з одним екземпляром для кожного типу обладнання. Датчик температури може бути одного типу, датчик вологості іншого.

Один рівень нижче цього - це фактичні класи, які реалізують ці інтерфейси: вони представляють один пристрій, аналогічний Acme4680Sensor, який я описав вище. Будь-який конкретний клас може реалізувати кілька інтерфейсів, якщо пристрій може виконувати кілька функцій.

Кожен клас пристроїв має свій приватний клас Comm (комунікації), який обробляє завдання низького рівня спілкування з обладнанням.

Поза межами апаратного модуля єдиний видимий шар - це інтерфейси / enum плюс HardwareManager. Клас HardwareManager - це заводська абстракція, яка обробляє екземпляри класів пристроїв, кешування екземплярів (ви дійсно не хочете, щоб два класи пристроїв розмовляли з одним і тим же апаратним пристроєм) і т. Д. Клас, який потребує певного типу датчика, просить HardwareManager отримати пристрій для конкретного перерахунку, який він потім з'ясовує, якщо він вже створений, якщо не як створити його та ініціалізувати тощо.

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


Приклад діаграми класів UML, що показує дизайн, описаний у цій відповіді

Примітка: між HardwareManager та кожним класом пристроїв існують асоціації, які я не малював, оскільки діаграма перетворилася б на суп зі стрілками.


Дуже цікава відповідь. Я б попросив швидкого редагування: залиште більш чітко, що таке спадщина / стримування / співпраця між чотирма згаданими вами класами. Велике спасибі!
heltonbiker

Я додав діаграму класів UML. Чи допомагає це?

2
Я б сказав, що це вбивча відповідь, і це не тільки допоможе мені багато, але я вважаю, що може допомогти набагато більше людей у ​​майбутньому. Крім того, приклад ієрархії класів ви надаєте надзвичайно добре картографування наших проблемних доменів та рішень. Ще раз дякую!
heltonbiker

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

11

Тут може виявитися трохи складно знайти єдину об'єднавчу конвенцію, оскільки ці класи розповсюджені на декілька просторів імен ( ConnectionInfoздається, в CrystalDecisionsі DeviceInfoв System.Reporting.WebForms).

Однак, дивлячись на ці приклади, здається, що суфікс є двома різними способами:

  • Розрізнення класу, що забезпечує статичні методи, та класу, що забезпечує методи екземпляра. Це стосується System.IOкласів, підкреслених їх описами:

    Каталог :

    Розкриває статичні методи створення, переміщення та перерахування через каталоги та підкаталоги. Цей клас не може бути успадкований.

    DirectoryInfo :

    Розкриває методи екземплярів для створення, переміщення та перерахування через каталоги та підкаталоги. Цей клас не може бути успадкований.

    Infoце здається дещо дивним вибором, але це робить різницю відносно зрозумілою: Directoryклас може розумно або представляти певний каталог, або надавати загальні допоміжні методи, пов'язані з каталогами, не тримаючи жодного стану, тоді як DirectoryInfoсправді може бути лише першим.

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

    Я думаю, що остання частина цього речення може бути частиною головоломки, яка відрізняє, скажімо, ConnectionInfoвід EmployeeInfo. Якби у мене був клас, який називається Connection, я б обґрунтовано очікував, що він фактично надасть мені функціонал, який має з'єднання - я б шукав такі методи, як void Open()і т. Д. Однак ніхто з розумом не сподівався б, що Employeeклас може насправді робити те, що реально Employeeробить, або шукати такі методи, як void DoPaperwork()або bool TryDiscreetlyBrowseFacebook().


1
Дуже дякую за вашу відповідь! Я вважаю, що перша куля у вашій відповіді безумовно описує, що відбувається в класах .NET, але друга має більше значення (і, до речі, відрізняється), тому що вона краще розкриває передбачувану абстракцію: річ, яку слід використовувати проти що потрібно описати. Важко було вибрати, яку відповідь прийняти.
heltonbiker

4

Взагалі Infoоб’єкт інкапсулює інформацію про стан об'єкта в якийсь момент часу . Якщо я попрошу систему переглянути файл і надати мені FileInfoоб'єкт, пов’язаний з його розміром, я би сподівався, що цей об'єкт повідомить розмір файлу в момент надання запиту (або, якщо точніше, розмір файл у якийсь момент між тим, коли було здійснено дзвінок, і коли він повернувся). Якщо розмір файлу змінюється між часом повернення запиту та часом, коли FileInfoоб'єкт вивчається, я б не очікував, що така зміна відобразиться в FileInfoоб’єкті.

Зауважте, що така поведінка буде сильно відрізнятися від поведінки Fileоб'єкта. Якщо запит на відкриття дискового файлу в неексклюзивному режимі дає Fileоб'єкт, який має Sizeвластивість, я б очікував, що значення, повернене тим самим, змінюється при зміні розміру файлу диска, оскільки Fileоб'єкт не просто представляє стан файл - він представляє сам файл .

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


1
Це не зовсім так, але він не з'являється , щоб пояснити закономірності присвоєння імен .NET дуже добре на всіх, так як Fileклас не може бути створений і FileInfoоб'єкти зробити оновлення з основною файловою системою.
Натан Туггі

@NathanTuggy Я погоджуюся, що це не дуже добре пов’язане з цією умовою іменування в .NET, але щодо моєї поточної проблеми я вважаю, що це дуже актуально і може викликати послідовне рішення. Я відповів на цю відповідь, але важко вибрати лише одного, який прийме ...
heltonbiker

@NathanTuggy: Я б не рекомендував використовувати .NET як модель будь-якої консистенції. Це хороший і корисний фреймворк, який включає в себе багато хороших ідей, але кілька поганих ідей також потрапляють у поєднання.
supercat

@supercat: Звичайно; просто здається дивним відповісти на питання про те, "чому .NET робить це так заново?" з "було б непогано робити речі% THIS_WAY%".
Натан Туггі

@NathanTuggy: Я не пам’ятав точних деталей розглянутих класів, але думав, що FileInfoпросто маю статично зафіксовану інформацію. Чи не так? Крім того, я ніколи не мав приводу відкривати файли в неексклюзивному режимі, але це повинно бути можливим, і я би очікував, що існує метод, який повідомить про поточний розмір файлу, навіть якщо об'єкт, який використовується для відкриття, не є називається File(зазвичай я просто використовувати ReadAllBytes, WriteAllBytes, ReadAllText, WriteAllTextі т.д.).
суперкат

2

Враховуючи, що фактичні пристрої (датчики та приймачі) - це одне, а їх представлення в програмному забезпеченні - інше, я думаю про те, щоб назвати деякі класи з шаблоном назви суфікса "Інформація".

Наприклад, хоча a Sensorбуде класом, який представляє фактичний датчик (коли він фактично підключений до якогось робочого пристрою), SensorInfoвикористовувався б для представлення лише характеристик такого датчика. Наприклад, при збереженні файлу я б серіалізував a SensorInfoдо заголовка файлу, замість серіалізації a Sensor, який би навіть не мав сенсу.

Мені не подобається ця відмінність. Усі об'єкти - це "представлення [и] в програмному забезпеченні". Ось що означає слово «об’єкт».

Тепер, можливо, має сенс відокремити інформацію про периферійну мережу від фактичного коду, який взаємодіє з периферійним пристроєм. Так, наприклад, у Sensor є SensorInfo, який містить більшість змінних екземплярів, а також деякі методи, які не потребують апаратного забезпечення, тоді як клас Sensor відповідає за фактичну взаємодію з фізичним датчиком. У вас немає -а, Sensorякщо ваш комп'ютер не має датчика, але ви, правдоподібно, можете мати-a SensorInfo.

Біда в тому, що подібний дизайн можна узагальнити до (майже) будь-якого класу. Тож треба бути обережним. Ви, очевидно, не мали б SensorInfoInfoкласу, наприклад. І якщо у вас є Sensorзмінна, ви можете виявити, що ви порушуєте закон Demeter , взаємодіючи з її SensorInfoчленом. Звичайно, нічого з цього не є фатальним, але дизайн API не лише для авторів бібліотеки. Якщо ви будете зберігати власний API чистим та простим, ваш код стане більш ретельним.

На мою думку, такі файлові системи, як каталоги, дуже близькі до цього краю. Є деякі ситуації, в яких потрібно описати каталог, який не є локально доступним, правда, але пересічний розробник, мабуть, не перебуває в одній із таких ситуацій. Ускладнювати структуру класу таким чином, на мій погляд, не допомагає. Контрастний підхід Python в pathlib: Є єдиний клас, який "найімовірніше, що вам потрібно", і різні допоміжні класи, які більшість розробників можуть сміливо ігнорувати. Якщо вони вам справді потрібні, вони надають в основному той самий інтерфейс, лише конкретними методами.


Дякую за вашу відповідь, і kudos за вашу конструкцію "have-a";) Ваша відповідь нагадує мені про дискомфорт, спричинений деяким концепцією "DTO" (об'єкт передачі даних) - або її побратимом об'єкта параметрів - на який нахмурився. більш ортодоксальними завзяттями ОО, оскільки вони зазвичай не містять методів (адже це об'єкт передачі даних ). У цьому сенсі, і підсумовуючи також те, що сказали інші, я думаю, що SensorInfoце буде свого роду DTO, та / або також як "знімок", або навіть "специфікація", що представляє лише частину даних / стану фактичного Sensorоб'єкта що "може бути навіть не там".
heltonbiker

Як і у більшості проблем із дизайном, тут немає жорсткого і швидкого правила. У наданому нами прикладі pathlib PureWindowsPathтрохи схожий на об'єкт Info, але він має методи для виконання речей, які не потребують системи Windows (наприклад, прийняття підкаталогу, розбиття розширення файлу тощо). Це корисніше, ніж просто надання прославленої структури.
Кевін

1
Знову ж таки, кудо за частину "прославленої структури" :). Це нагадує мені споріднену цитату з книги "Чистий код" дядька Боба, глава 6: "Зрілі програмісти знають, що думка про те, що все є об'єктом, є міфом. Іноді дійсно потрібно простої структури даних з процедурами, що діють на них".
heltonbiker

2

Я б сказав, що контекст / домен має значення, оскільки у нас є бізнес-логічний код високого рівня та моделі низького рівня, компоненти архітектури тощо.

"Інформація", "Дані", "Менеджер", "Об'єкт", "Клас", "Модель", "Контролер" і т.д. можуть бути смердючими суфіксами, особливо на нижчому рівні, оскільки кожен об'єкт має певну інформацію чи дані, так що інформація не потрібна.

Назви класів бізнес-домену повинні бути такими, як усі зацікавлені сторони говорять про це, незалежно від того, чи це звучить дивно чи не на 100% правильна мова.

Хорошими суфіксами для структур даних є, наприклад, "Список", "Карта" та для натякових шаблонів "Декоратор", "Адаптер", якщо ви вважаєте, що це необхідно.

За вашим сценарієм сенсорів, я б не сподівався SensorInfoзберегти те, що є вашим датчиком, але SensorSpec. Infoimho - це більше похідна інформація, наприклад, FileInfoце щось на зразок розміру, який ви не зберігаєте, або файловий шлях, побудований із шляху та назви файлу тощо.

Ще один момент:

У комп'ютерній науці є лише дві важкі речі: недійсність кешу та іменування речей.

- Філ Карлтон

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


1

ThingInfo може служити проксі-сервером для читання лише для читання.

див. http://www.dofactory.com/net/proxy-design-pattern

Проксі: "Надайте сурогат або заповнювач іншого об'єкта для контролю доступу до нього."

Зазвичай ThingInfo матиме загальнодоступні властивості без налаштування. Ці класи та методи класу безпечні для використання та не вносять жодних змін до даних резервного копіювання, об’єкта чи будь-яких інших об'єктів. Ніяких змін стану чи інших побічних ефектів не відбудеться. Вони можуть бути використані для звітування та веб-служб або будь-де, де вам потрібна інформація про об'єкт, але ви хочете обмежити доступ до власне об'єкта.

Використовуйте ThingInfo, коли це можливо, і обмежте використання фактичної речі на час, який вам потрібно змінити об'єкт Thing. Це робить читання та налагодження значно швидшими, коли ви звикаєте до використання цього шаблону.


Відмінно. Раніше сьогодні я аналізував свої архітектурні потреби щодо класів, які я використовував у питанні, і прийшов до цього самого висновку. У моєму випадку у мене є a, Receiverякий отримує потоки даних від багатьох Sensors. Ідея така: приймач повинен абстрагувати фактичні датчики. Але проблема: мені потрібен датчик інформації , так що я можу записати їх в якій - то заголовок файлу. Рішення: кожен IReceiverматиме список SensorInfo. Якщо я надішлю команди на приймач, які передбачають зміну стану датчика, ці зміни будуть відображені (через геттер) у відповідному SensorInfo.
heltonbiker

(продовження ...), тобто: я не розмовляю з самими датчиками, я розмовляю лише з приймачем за допомогою команд вищого рівня, а приймач по черзі розмовляє з кожним із датчиків. Але мені врешті потрібна інформація від датчиків, які я отримую від екземпляра Receiver через його List<SensorInfo>властивість readonly.
heltonbiker

1

Поки, здається, ніхто в цьому питанні не зрозумів реальної причини цієї конвенції про іменування.

DirectoryInfoЧи не каталог. Це DTO з даними про каталозі. Таких каталогів може бути безліч таких випадків. Це не сутність. Це об'єкт, що викидається. A не представляє фактичного каталогу. Ви також можете вважати це ручкою або контролером для каталогу.DirectoryInfo

Порівнюйте це з класом з іменем Employee. Це може бути об'єкт ORM, і це єдиний об'єкт, що описує цього працівника. Якщо це був об'єкт цінності без ідентичності, його слід називати EmployeeInfo. EmployeeДійсно представляє реальну працівника. Цільовий клас DTO, що викликається EmployeeInfo, явно не представляє працівника, а краще описує його або зберігає дані про нього.

Насправді є приклад у BCL, де обидва класи існують: A ServiceController- клас, який описує службу Windows. Для кожної служби може бути така кількість контролерів. ServiceBase(Або похідний клас) є фактичним обслуговуванням і він не концептуальний має сенсу мати кілька екземплярів за окрему службу.


Це схоже на Proxyзразок, згаданий @Shmoken, чи не так?
heltonbiker

@heltonbiker це не обов'язково має бути проксі. Це може бути об’єкт, який жодним чином не пов'язаний з реччю, яку вона описує. Наприклад, ви можете отримати EmployeeInfoвеб-службу. Це не проксі. Подальший приклад: у Anten AddressInfoнавіть немає речі, яку він може проксі-сервер, оскільки адреса не є сутністю. Це окрема цінність.
usr

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