Я работаю в проекте, который имеет дело с физическими устройствами, и меня смутило, как правильно назвать некоторые классы в этом проекте.
Учитывая фактические устройства (датчики и приемники) - это одно, а их представление в программном обеспечении - другое, я думаю о присвоении имен некоторым классам с помощью шаблона имени суффикса «Info».
Например, хотя a Sensor
будет классом для представления фактического датчика (когда он фактически подключен к какому-либо работающему устройству), SensorInfo
он будет использоваться для представления только характеристик такого датчика. Например, при сохранении файла я бы сериализовал a SensorInfo
в заголовок файла вместо сериализации a Sensor
, что даже не имело бы смысла.
Но теперь я в замешательстве, потому что в жизненном цикле объектов есть середина, где я не могу решить, следует ли мне использовать тот или иной, или как получить один из другого, или даже нужно ли сворачивать оба варианта только в один класс.
Кроме того, слишком распространенный пример Employee
класса, очевидно, является просто представлением реального человека, но EmployeeInfo
, насколько я знаю , никто бы не предложил назвать этот класс .
Язык, с которым я работаю, - .NET, и этот шаблон именования, кажется, распространен во всей среде, например, с этими классами:
Directory
иDirectoryInfo
классы;File
иFileInfo
классы;ConnectionInfo
класс (без соответствующегоConnection
класса);DeviceInfo
класс (без соответствующегоDevice
класса);
Итак, мой вопрос: есть ли общее обоснование использования этого шаблона именования? Существуют ли случаи, когда имеет смысл иметь пары имен ( Thing
и ThingInfo
), и другие случаи, когда должен существовать только ThingInfo
класс или Thing
класс без его аналога?
источник
Info
суффикс отличаетstatic
класс , содержащий вспомогательные методы от своего аналога с учетом состояния. Это не «лучшая практика» как таковая; Это просто способ, которым команда .NET придумала решение конкретной проблемы. Они могли бы так же легко придуматьFileUtility
иFile
, ноFile.DoSomething()
и ,FileInfo.FileName
кажется, лучше читать.Foo
, у вас может быть неинстанцируемый служебный классFoos
. Когда дело доходит до именования, важна согласованность в API, а в идеале - в API на платформе.Employee
примеры найдены десятками, в Интернете или в классических книгах, хотя я еще не виделEmployeeInfo
(возможно, потому что работник - живое существо, а не техническая конструкция, такая как соединение или файл). Но, согласился, если классEmployeeInfo
будет предложен в проекте, я думаю, что он может иметь свое применение.Ответы:
Я думаю, что "информация" является неправильным. У объектов есть состояние и действия: «информация» - это просто еще одно имя слова «состояние», которое уже встроено в ООП.
Что вы на самом деле пытаетесь смоделировать здесь? Вам нужен объект, который представляет аппаратное обеспечение в программном обеспечении, чтобы другой код мог его использовать.
Это легко сказать, но, как вы узнали, в этом есть нечто большее. «Представление оборудования» удивительно широк. У объекта, который делает это, есть несколько проблем:
Некоторые устройства, такие как датчики, будут иметь меньше проблем, чем, скажем, многофункциональное устройство принтер / сканер / факс. Датчик, скорее всего, просто создает поток битов, в то время как сложное устройство может иметь сложные протоколы и взаимодействия.
В любом случае, возвращаясь к вашему конкретному вопросу, есть несколько способов сделать это в зависимости от ваших конкретных требований, а также сложности взаимодействия с оборудованием.
Вот пример того, как я бы разработал иерархию классов для датчика температуры:
ITemperaSource: интерфейс, представляющий все, что может генерировать данные о температуре: датчик, который может быть даже оберткой файла или жестко закодированными данными (например, фиктивное тестирование).
Acme4680Sensor: датчик ACME модель 4680 (отлично подходит для обнаружения, когда Roadrunner находится поблизости). Это может реализовать несколько интерфейсов: возможно, этот датчик обнаруживает как температуру, так и влажность. Этот объект содержит состояние программного уровня, такое как «датчик подключен?» и "что было последним чтением?"
Acme4680SensorComm: используется исключительно для связи с физическим устройством. Это не поддерживает много государства. Используется для отправки и получения сообщений. У него есть метод C # для каждого сообщения, которое понимает аппаратное обеспечение.
HardwareManager: используется для получения устройств. По сути, это фабрика, которая кэширует экземпляры: для каждого аппаратного устройства должен быть только один экземпляр объекта устройства. Он должен быть достаточно умен, чтобы знать, что если поток A запрашивает датчик температуры ACME, а поток B запрашивает датчик влажности ACME, это фактически один и тот же объект, и его следует возвращать в оба потока.
На верхнем уровне у вас будут интерфейсы для каждого типа оборудования. Они описывают действия, которые ваш код C # будет выполнять на устройствах, используя типы данных C # (не например, байтовые массивы, которые может использовать необработанный драйвер устройства).
На том же уровне у вас есть класс перечисления с одним экземпляром для каждого типа оборудования. Датчик температуры может быть одного типа, датчик влажности - другим.
На один уровень ниже это фактические классы, которые реализуют эти интерфейсы: они представляют одно устройство, подобное Acme4680Sensor I, описанному выше. Любой конкретный класс может реализовывать несколько интерфейсов, если устройство может выполнять несколько функций.
Каждый класс устройств имеет свой собственный класс связи (связи), который обрабатывает низкоуровневую задачу общения с оборудованием.
Вне аппаратного модуля единственный видимый уровень - это интерфейсы / перечисление плюс HardwareManager. Класс HardwareManager - это фабричная абстракция, которая обрабатывает создание экземпляров классов устройств, кэширование экземпляров (вы на самом деле не хотите, чтобы два класса устройств общались с одним и тем же аппаратным устройством) и т. Д. Класс, которому требуется датчик определенного типа, просит HardwareManager получить устройство для конкретного перечисления, которое оно затем выясняет, если оно уже было создано, если нет, как его создать и инициализировать, и т. д.
Цель здесь - отделить бизнес-логику от аппаратной логики низкого уровня. Когда вы пишете код, который выводит данные датчика на экран, этот код не должен заботиться о том, какой тип датчика у вас есть, если и только если существует эта развязка, которая концентрируется на этих аппаратных интерфейсах.
Примечание: существуют ассоциации между HardwareManager и каждым классом устройств, которые я не рисовал, потому что диаграмма превратилась бы в суп со стрелками.
источник
Здесь может быть немного трудно найти единое объединяющее соглашение, потому что эти классы распределены по ряду пространств имен (
ConnectionInfo
кажется, внутриCrystalDecisions
иDeviceInfo
внутриSystem.Reporting.WebForms
).Однако, глядя на эти примеры, можно увидеть два разных использования суффикса:
Различение класса, предоставляющего статические методы, с классом, предоставляющим методы экземпляра. Это тот случай, когда
System.IO
классы подчеркнуты их описаниями:Каталог :
DirectoryInfo :
Info
Это выглядит немного странным выбором, но это делает разницу относительно ясной:Directory
класс может разумно либо представлять конкретный каталог, либо предоставлять общие вспомогательные методы, связанные с каталогом, без удержания какого-либо состояния, тогда как вDirectoryInfo
действительности может быть только первым.Подчеркивая, что класс содержит только информацию и не обеспечивает поведения, которое можно разумно ожидать от имени без суффикса .
Я думаю, что последняя часть этого предложения может быть частью головоломки, которая отличает, скажем,
ConnectionInfo
отEmployeeInfo
. Если бы у меня был класс с именемConnection
, я бы разумно ожидал, что он на самом деле предоставит мне функциональность, которая есть у соединения - я бы искал такие методы, какvoid Open()
и т. Д. Однако никто в здравом уме не ожидал бы, чтоEmployee
класс может на самом деле делать то, чтоEmployee
делает реальный , или искать методы, какvoid DoPaperwork()
илиbool TryDiscreetlyBrowseFacebook()
.источник
Как правило,
Info
объект инкапсулирует информацию о состоянии объекта в определенный момент времени . Если бы я попросил систему взглянуть на файл и дать мнеFileInfo
объект, связанный с его размером, я бы ожидал, что этот объект сообщит о размере файла во время запроса (или, точнее, о размере файла). файл в какой-то момент между тем, когда был сделан вызов и когда он вернулся). Если размер файла изменяется между временем возврата запроса и временемFileInfo
проверки объекта, я не ожидал бы, что такое изменение будет отражено вFileInfo
объекте.Обратите внимание, что это поведение будет сильно отличаться от поведения
File
объекта. Если запрос на открытие файла на диске в неисключительном режиме приводит кFile
объекту, у которого естьSize
свойство, я ожидаю, что возвращаемое им значение изменится при изменении размера файла на диске, посколькуFile
объект не просто представляет состояние файл - он представляет сам файл .Во многих случаях объекты, которые подключаются к ресурсу, должны быть очищены, когда их службы больше не нужны. Поскольку
*Info
объекты не прикрепляются к ресурсам, они не требуют очистки. Как следствие, в случаях, когдаInfo
объект будет удовлетворять требованиям клиента, может быть, лучше использовать код один, чем использовать объект, который будет представлять базовый ресурс, но чье соединение с этим ресурсом должно быть очищено.источник
File
класс не может быть создан иFileInfo
объекты сделать обновление с основной файловой системой.FileInfo
просто держал статически захваченную информацию. Не так ли? Кроме того, у меня никогда не было случая открывать файлы в неисключительном режиме, но это должно быть возможно, и я ожидаю, что существует метод, который сообщит о текущем размере файла, даже если объект, используемый для открытия, не является называетсяFile
(обычно я просто использоватьReadAllBytes
,WriteAllBytes
,ReadAllText
,WriteAllText
и т.д.).Мне не нравится это различие. Все объекты являются «представлением в программном обеспечении». Вот что означает слово «объект».
Теперь, возможно, имеет смысл отделить информацию о периферии от фактического кода, который взаимодействует с периферией. Так, например, у Sensor есть SensorInfo, который содержит большинство переменных экземпляра, а также некоторые методы, которые не требуют аппаратного обеспечения, в то время как класс Sensor отвечает за фактическое взаимодействие с физическим датчиком. У вас нет -a,
Sensor
если у вашего компьютера нет датчика, но вы, вероятно, можете иметь -aSensorInfo
.Беда в том, что этот вид дизайна можно обобщить на (почти) любой класс. Поэтому вы должны быть осторожны. У вас, очевидно, не будет
SensorInfoInfo
класса, например. И если у вас естьSensor
переменная, вы можете столкнуться с нарушением закона Деметры , взаимодействуя с ееSensorInfo
членом. Конечно, все это не фатально, но дизайн API не только для авторов библиотек. Если вы сохраните свой собственный API чистым и простым, ваш код будет более понятным.Ресурсы файловой системы, такие как каталоги, на мой взгляд, очень близки к этому краю. Есть некоторые ситуации, в которых вы хотите описать каталог, который не является локально доступным, правда, но обычный разработчик, вероятно, не находится в одной из этих ситуаций. Такое усложнение структуры классов, на мой взгляд, бесполезно. Противопоставьте подход Python
pathlib
: Существует один класс, который «скорее всего вам нужен», и различные вспомогательные классы, которые большинство разработчиков могут спокойно игнорировать. Однако, если они вам действительно нужны, они предоставляют в основном один и тот же интерфейс, просто с удалением конкретных методов.источник
SensorInfo
это будет своего рода DTO, и / или также как «снимок» или даже «спецификация», представляющая только часть данных / состояния реальногоSensor
объекта что "может даже не быть".PureWindowsPath
он немного похож на объект Info, но у него есть методы для выполнения задач, не требующих системы Windows (например, создание подкаталога, выделение расширения файла и т. Д.). Это более полезно, чем просто предоставление прославленной структуры.Я бы сказал, что контекст / домен имеет значение, поскольку у нас есть код бизнес-логики высокого уровня и низкоуровневые модели, компоненты архитектуры и так далее ...
«Информация», «Данные», «Менеджер», «Объект», «Класс», «Модель», «Контроллер» и т. Д. Могут быть вонючими суффиксами, особенно на более низком уровне, поскольку каждый объект имеет некоторую информацию или данные, поэтому эта информация не нужна.
Имена классов бизнес-домена должны быть похожи на разговоры всех заинтересованных сторон, независимо от того, звучит ли это странно или не является на 100% правильным языком.
Хорошими суффиксами для структур данных являются, например, «Список», «Карта», а также для шаблонов подсказок «Декоратор», «Адаптер», если вы считаете, что это необходимо.
По сценарию с вашим датчиком я не ожидал
SensorInfo
бы сохранить то, чем является ваш датчик, ноSensorSpec
.Info
imho - это скорее производная информация, например,FileInfo
размер, который вы не сохраняете, или путь к файлу, который создается из пути и имени файла и т. д.Еще один момент:
Это всегда напоминает мне о том, что я думаю о названии всего несколько секунд, и если я его не нашел, я использую странные имена и помечаю их «TODO». Я всегда могу изменить его позже, так как моя IDE обеспечивает поддержку рефакторинга. Это не лозунг компании, который должен быть хорошим, а просто какой-то код, который мы можем изменить каждый раз, когда захотим. Запомни.
источник
ThingInfo может служить отличным прокси-сервером только для чтения.
см. http://www.dofactory.com/net/proxy-design-pattern
Прокси: «Предоставьте суррогат или заполнитель для другого объекта для контроля доступа к нему».
Обычно ThingInfo будет иметь открытые свойства без установщиков. Эти классы и методы класса безопасны в использовании и не будут вносить каких-либо изменений в вспомогательные данные, объект или любые другие объекты. Никаких изменений состояния или других побочных эффектов не произойдет. Они могут использоваться для создания отчетов и веб-сервисов или в любом месте, где вам нужна информация об объекте, но вы хотите ограничить доступ к самому фактическому объекту.
Используйте ThingInfo всякий раз, когда это возможно, и ограничивайте использование фактического Thing теми временами, когда вам действительно нужно изменить объект Thing. Это делает чтение и отладку значительно быстрее, когда вы привыкнете использовать этот шаблон.
источник
Receiver
который получает потоки данных от многихSensor
s. Идея такова: приемник должен абстрагировать фактические датчики. Но проблема: мне нужен датчик информации , так что я могу записать их в какой - то заголовок файла. Решение: у каждогоIReceiver
будет списокSensorInfo
. Если я посылаю команды получателю, которые подразумевают изменение состояния датчика, эти изменения будут отражены (через геттер) на соответствующемSensorInfo
.List<SensorInfo>
свойство только для чтения.Пока что никто в этом вопросе, похоже, не понял реальной причины такого соглашения об именах.
DirectoryInfo
Не каталог. Это DTO с данными о каталоге. Может быть много таких примеров, описывающих один и тот же каталог. Это не сущность. Это объект однозначного значения. А не представляет фактический каталог. Вы также можете думать об этом как о ручке или контроллере для каталога.DirectoryInfo
Сравните это с классом по имени
Employee
. Это может быть объект сущности ORM, и это единственный объект, описывающий этого сотрудника. Если это был объект-значение без идентичности, он должен быть вызванEmployeeInfo
. AnEmployee
действительно представляет фактического работника. Ценностный класс DTO, подобный значениюEmployeeInfo
, явно не представляет сотрудника, а скорее описывает его или хранит данные о нем.На самом деле в BCL есть пример, где существуют оба класса: A
ServiceController
- это класс, который описывает службу Windows. Таких контроллеров может быть любое количество для каждой услуги. AServiceBase
(или производный класс) - это фактический сервис, и концептуально не имеет смысла иметь несколько его экземпляров для каждого отдельного сервиса.источник
Proxy
шаблон, упомянутый @Shmoken, не так ли?EmployeeInfo
от веб-службы. Это не прокси. Еще один пример: у AnAddressInfo
даже нет вещи, которую он может прокси, потому что адрес не является сущностью. Это отдельная ценность.