给带有“ Info”后缀的类命名的想法是什么,例如:“ SomeClass”和“ SomeClassInfo”?


20

我正在一个处理物理设备的项目中工作,而在如何正确命名该项目中的某些类方面一直感到困惑。

考虑到实际的设备(传感器和接收器)是一回事,而它们在软件中的表示是另一回事,我正在考虑使用“ Info”后缀名称模式命名某些类。

例如,虽然a Sensor是代表实际传感器的类(当它实际上连接到某个工作设备时),但SensorInfo将仅用于代表此类传感器的特性。例如,保存文件后,我将序列化为SensorInfo文件头,而不是序列化Sensor,这甚至都没有道理。

但是现在我很困惑,因为在对象生命周期中存在一个中间立场,我无法决定是否应该使用一种或另一种,或者如何从另一种中获取一种,甚至不能将这两种变体实际上只折叠为一个类。

而且,太普遍的示例Employee类显然只是真实人物的表示,但EmployeeInfo据我所知,没有人会建议为该类命名。

我使用的语言是.NET,这种命名模式在整个框架中似乎很常见,例如这些类:

  • DirectoryDirectoryInfo班级;
  • FileFileInfo班级;
  • ConnectionInfo班级(无对应Connection班级);
  • DeviceInfo班级(无对应Device班级);

所以我的问题是:使用这种命名模式是否有共同的理由?在某些情况下,具有成对的名称(ThingThingInfo)是否有意义,而在其他情况下,仅应存在ThingInfo该类,或者Thing该类没有对应物?


5
Ben Aaronson在下面的回答中完全正确。所述Info后缀区分一个static含有来自其状态对应的实用方法类。因此,这不是“最佳实践”;这只是.NET团队提出来解决特定问题的一种方式。他们可以很容易拿出FileUtilityFile,但File.DoSomething()FileInfo.FileName似乎读更好。
罗伯特·哈维

1
值得注意的是,这是一种常见的模式,但是使用的命名约定与语言和API一样多。例如,在Java中,如果您有状态类Foo,则可能会有一个非实例化的实用程序类Foos。命名时,重要的是API内的一致性,最好是平台上的API之间的一致性。
凯文·克鲁维德

1
真?“没有人建议将这个类命名为EmployeeInfo”?没有人?对于似乎不太明显的事情,这似乎有点强大。
ThePopMachine

@ThePopMachine在这种情况下,我同意“没人”这个词可能太难了,但是Employee在网上或经典书籍中都有数十个示例,而我还没有见过EmployeeInfo(也许是因为员工是活人,而不是技术结构,例如连接或文件)。但是,我同意,如果EmployeeInfo要在一个项目中提议该课程,我相信它可以使用。
heltonbiker

Answers:


21

我认为“信息”是用词不当。对象具有状态和动作:“信息”只是“状态”的另一个名称,“状态”已经包含在OOP中。

真正想在这里建模吗?您需要一个代表软件中硬件的对象,以便其他代码可以使用它。

这很容易说出来,但是正如您所发现的那样,它还有更多。“代表硬件”令人惊讶地广泛。一个这样做的对象有几个问题:

  • 底层设备通信,无论是与USB接口,串行端口,TCP / IP还是专有连接进行通信。
  • 管理状态。设备打开了吗?准备好与软件对话了吗?忙?
  • 处理事件。设备生成了数据:现在我们需要生成事件以传递给其他感兴趣的类。

诸如打印机之类的某些设备比打印机/扫描仪/传真多功​​能设备所关心的问题要少。传感器可能只产生比特流,而复杂的设备可能具有复杂的协议和交互。

无论如何,回到您的特定问题,有几种方法可以执行此操作,具体取决于您的特定要求以及硬件交互的复杂性。

这是我如何设计温度传感器的类层次结构的示例:

  • ITemperatureSource:代表可以产生温度数据的任何东西的接口:传感器甚至可以是文件包装器或硬编码的数据(认为:模拟测试)。

  • Acme4680Sensor:ACME 4680型传感器(用于检测Roadrunner何时在附近的绝佳传感器)。这可以实现多个接口:也许此传感器同时检测温度和湿度。该对象包含程序级别的状态,例如“传感器是否已连接?”。和“最后一次阅读是什么?”

  • Acme4680SensorComm:用于与物理设备进行通信。它不会保持太多状态。它用于发送和接收消息。对于硬件可以理解的每条消息,它都有一个C#方法。

  • HardwareManager:用于获取设备。本质上,这是一个缓存实例的工厂:每个硬件设备只能有一个设备对象实例。必须足够聪明才能知道,如果线程A请求ACME温度传感器而线程B请求ACME湿度传感器,则它们实际上是同一对象,应该返回给两个线程。


在顶层,您将具有每种硬件类型的接口。它们使用C#数据类型(而不是原始设备驱动程序可能使用的字节数组)描述C#代码将对设备执行的操作。

在同一级别上,您有一个枚举类,每种硬件类型都有一个实例。温度传感器可能是一种,湿度传感器可能是另一种。

在此之下一级是实现这些接口的实际类:它们表示一种设备,类似于上面描述的Acme4680SensorI。如果设备可以执行多种功能,则任何特定类都可以实现多个接口。

每个设备类都有自己的专用Comm(通信)类,该类处理与硬件对话的底层任务。

在硬件模块之外,唯一可见的层是接口/枚举以及HardwareManager。HardwareManager类是工厂抽象,用于处理设备类的实例化,缓存实例(您确实不希望两个设备类与同一硬件设备通信)等。需要特定类型传感器的类要求HardwareManager获取特定枚举的设备,然后确定该设备是否已实例化,是否创建和初始化该设备等。

这里的目标是使业务逻辑与底层硬件逻辑脱钩。当您编写将传感器数据打印到屏幕上的代码时,该代码不应该关心您拥有的传感器类型,并且仅当这种去耦以这些硬件接口为中心。


UML类图示例,显示此答案中描述的设计

注意:在硬件管理器和我未绘制的每个设备类之间都有关联,因为该图会变成箭头汤。


非常有趣的答案。我要求快速编辑:更明确地说明您提到的四个类之间的继承/包含/协作是什么。非常感谢你!
heltonbiker

我添加了一个UML类图。这有帮助吗?

2
我会说这是一个致命的答案,这不仅会对我有很大帮助,而且我相信将来可能会帮助更多人。此外,您提供的类层次结构示例可以很好地映射我们的问题和解决方案领域。再一次非常感谢你!
heltonbiker

当我读到这个问题时,我意识到还有更多的事情要做,所以我有点过头了,并分享了整个设计。这不仅仅是一个简单的命名问题,因为名称表示设计。我曾经使用过这种方式构建的系统,而且它们运行良好。

11

在这里找到一个统一的约定可能有点困难,因为这些类分布在许多名称空间中(ConnectionInfo似乎在CrystalDecisionsDeviceInfoSystem.Reporting.WebForms)。

但是,看看这些示例,后缀似乎有两种不同的用法:

  • 区分提供静态方法的类和提供实例方法的类。System.IO类的情况就是如此,其描述强调了这一点:

    目录

    公开用于在目录和子目录中创建,移动和枚举的静态方法。这个类不能被继承。

    DirectoryInfo

    公开用于在目录和子目录中创建,移动和枚举的实例方法。这个类不能被继承。

    Info在这里似乎是一个有点奇怪的选择,但它确实使区别相对清楚:一个Directory类可以合理地表示一个特定目录,或者提供不涉及任何状态的,与目录相关的常规辅助方法,而DirectoryInfo实际上只能是前者。

  • 强调该类仅保存信息,而不提供未加前缀的名称可能合理预期的行为。

    我认为,这句话的最后一部分可能是一块拼图区分,说的,ConnectionInfoEmployeeInfo。如果我有一个叫做的类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用作任何形式的一致性模型。这是一个很好且有用的框架,其中包含了很多好主意,但是一些不好的主意也被混合在一起。
2015年

@supercat:当然;回答“为什么.NET为什么要这样做?”这个问题似乎很奇怪。与“做%THIS_WAY%的事情是一个好主意”。
内森·塔吉

@NathanTuggy:我不记得所讨论类的确切细节,但我认为FileInfo只是保存了静态捕获的信息。不是吗 另外,我从来没有机会以非独占模式打开文件,但是应该可以,而且我希望有一种方法可以报告文件的当前大小,即使用于打开的对象不是所谓的File(通常我只是用ReadAllBytesWriteAllBytesReadAllTextWriteAllText,等)。
2015年

2

考虑到实际的设备(传感器和接收器)是一回事,而它们在软件中的表示是另一回事,我正在考虑使用“ Info”后缀名称模式命名某些类。

例如,虽然a Sensor是代表实际传感器的类(当它实际上连接到某个工作设备时),但SensorInfo将仅用于代表此类传感器的特性。例如,保存文件后,我将序列化为SensorInfo文件头,而不是序列化Sensor,这甚至都没有道理。

我不喜欢这种区别。所有对象都是“软件中的表示形式”。这就是“对象”一词的意思。

现在,将有关外围设备的信息与与该外围设备接口的实际代码分开是有意义的。因此,例如,Sensor具有一个SensorInfo,其中包含大多数实例变量以及一些不需要硬件的方法,而Sensor类负责与物理传感器进行实际交互。您没有-a,Sensor除非您的计算机上有传感器,但是您可以合理地拥有-a SensorInfo

麻烦的是这种设计可以推广到(几乎)任何类别。因此,您必须小心。例如,您显然不会SensorInfoInfo上课。而且,如果您有一个Sensor变量,则可能通过与它的成员互动而发现自己违反了Demeter定律SensorInfo。当然,这些都不是致命的,但是API设计不仅针对库作者。如果您保持自己的API简洁明了,则代码将更易于维护。

我认为像目录这样的文件系统资源非常接近这一优势。在某些情况下,您想描述一个本地无法访问的目录,这是正确的,但是普通开发人员可能不在其中一种情况下。我认为,以这种方式使类结构复杂化是没有帮助的。在以下方面与Python的方法进行了对比pathlib:存在一个“最有可能需要的类”和大多数开发人员可以放心忽略的各种辅助类。但是,如果确实需要它们,则它们提供了大致相同的界面,只是去除了具体方法。


感谢您的回答,并为您的“ have-a”结构表示荣誉;)您的回答使我想起了“ DTO”(数据传输对象)概念(或其同级参数对象)引起的某些不适因为它通常不包含方法(毕竟它是数据传输对象),因此由更多的正统的OO狂热者使用。从这个意义上讲,我还总结了其他人所说的话,我认为这SensorInfo将是DTO,并且/或者也像“快照”,甚至是“ spec”,仅代表实际Sensor对象的数据/状态部分。“可能甚至不在那里”。
heltonbiker

与大多数设计问题一样,没有硬性规定。在我提供的pathlib示例中,其PureWindowsPath行为有点像Info对象,但是它确实具有不需要Windows系统的执行方法(例如,获取子目录,拆分文件扩展名等)。这比仅提供出色的结构更有用。
凯文(Kevin)

1
再次,对“荣耀的结构”部分表示敬意:)。这使我想起Bob叔叔的“干净代码”书第6章中的一则相关引文:“成熟的程序员知道,一切都是对象的想法是一个神话。有时,您确实确实想要简单的数据结构,并对其进行操作。”
heltonbiker

2

我会说上下文/域很重要,因为我们有高级业务逻辑代码和低级模型,体系结构组件等。

“信息”,“数据”,“管理器”,“对象”,“类”,“模型”,“控制器”等可能是有臭味的后缀,尤其是在较低级别,因为每个对象都有一些信息或数据,因此该信息不是必需的。

业务域的类名应该像所有利益相关者都在谈论的那样,无论它听起来很奇怪还是不是100%正确的语言。

如果您认为必要的话,数据结构的后缀是“ List”,“ Map”以及提示模式“ Decorator”,“ Adapter”。

对于您的传感器情况,我不希望SensorInfo保存您的传感器,但是SensorSpecInfoimho是更多的派生信息,例如FileInfo大小,您不保存或从路径和文件名构建的文件路径等。

还有一点:

在计算机科学中只有两件难事:缓存无效和命名。

-菲尔·卡尔顿

这总是让我想起一个名称仅几秒钟的想法,如果找不到任何名称,我会使用怪异的名称并在其上标记“ TODO”。由于我的IDE提供了重构支持,因此我以后总是可以更改它。这不是一个必须是好的公司口号,而只是一些我们可以随时更改的代码。记在脑子里。


1

ThingInfo可以作为Thing的出色只读代理。

参见http://www.dofactory.com/net/proxy-design-pattern

代理:“为另一个对象提供代理或占位符,以控制对其的访问。”

通常,ThingInfo将具有不带setter的公共属性。这些类和该类上的方法可以安全使用,并且不会对后备数据,对象或任何其他对象进行任何更改。不会发生状态更改或其他副作用。它们可以用于报表和Web服务,或者在您需要有关对象信息但想要限制对实际对象本身的访问的任何地方。

尽可能使用ThingInfo,并将实际Thing的使用限制为您实际需要更改Thing对象的时间。当您习惯使用此模式时,它将使读取和调试速度大大提高。


优秀。今天早些时候,我针对问题中使用的类分析了我的架构需求,并得出了同样的结论。就我而言,我有一个Receiver接收来自许多Sensor的数据流。这个想法是:接收器应该提取实际的传感器。但是问题是:我需要传感器信息,以便可以将它们写到某些文件头中。解决方法:每个IReceiver人都有一个清单SensorInfo。如果我将命令发送给接收器,这暗示着传感器状态发生变化,这些变化将(通过吸气剂)反映​​在相应的上SensorInfo
heltonbiker

(续...)即:我不与传感器本身对话,我仅通过更高级别的命令与Receiver对话,然后Receiver依次与每个Sensor对话。但是我最终需要来自传感器的信息,这些信息是通过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从Web服务获得一个。那不是代理。进一步的示例:由于地址不是实体,AddressInfo甚至没有东西可以代理。这是一个独立的价值。
usr 2015年

1
我知道,这很有道理!
heltonbiker
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.