I'm working in a project which deals with physical devices, and I've been confused as how to properly name some classes in this project.

Considering the actual devices (sensors and receivers) are one thing, and their representation in software is another, I am thinking about naming some classes with the "Info" suffix name pattern.

For example, while a Sensor would be a class to represent the actual sensor (when it is actually connected to some working device), SensorInfo would be used to represent only the characteristics of such sensor. For example, upon file save, I would serialize a SensorInfo to the file header, instead of serializing a Sensor, which sort of wouldn't even make sense.

But now I am confused, because there is a middleground on objects' lifecycle where I cannot decide if I should use one or another, or how to get one from another, or even whether both variants should actually be collapsed to only one class.

Also, the all too common example Employee class obviously is just a representation of the real person, but nobody would suggest to name the class EmployeeInfo instead, as far as I know.

The language I am working with is .NET, and this naming pattern seems to be common throughout the framework, for exemple with these classes:

  • Directory and DirectoryInfo classes;
  • File and FileInfo classes;
  • ConnectionInfoclass (with no correspondent Connection class);
  • DeviceInfo class (with no correspondent Device class);

So my question is: is there a common rationale about using this naming pattern? Are there cases where it makes sense to have pairs of names (Thing and ThingInfo) and other cases where there should only exist the ThingInfo class, or the Thing class, without its counterpart?

  • 5
    Ben Aaronson got it right in his answer below; the Info suffix distinguishes a static class containing utility methods from its stateful counterpart. It's not a "best practice" as such; it's just a way that the .NET team came up with to solve a particular problem. They could have just as easily come up with FileUtility and File, but File.DoSomething() and FileInfo.FileName seem to read better. – Robert Harvey May 8 '15 at 15:36
  • 1
    It's worth noting that this is a common pattern, but with as many different naming conventions as there are languages and APIs. In Java, for example, if you have a stateful class Foo, you might have a non-instantiable utility class Foos. When it comes to naming, the important thing is consistency within the API, and ideally across APIs on the platform. – Kevin Krumwiede May 9 '15 at 2:35
  • 1
    Really? "Nobody would suggest to name the class EmployeeInfo"?? NOBODY? That seems a little strong for something not so obvious. – ThePopMachine May 14 '15 at 16:49
  • @ThePopMachine I agree the word "nobody" might be too hard in this case, but Employee examples are found by the dozens, either online or in classic books, while I haven't seen EmployeeInfo yet (perhaps because an employee is a living being, not a technical construct like a connection or a file). But, agreed, if the class EmployeeInfo were to be proposed in a project, I believe it could have its use. – heltonbiker May 14 '15 at 18:24
  • 1
up vote 21 down vote accepted

I think "info" is a misnomer. Objects have state and actions: "info" is just another name for "state" which is already baked into OOP.

What are you really trying to model here? You need an object that represents the hardware in software so other code can use it.

That is easy to say but as you found out, there is more to it than that. "Representing hardware" is surprisingly broad. An object that does that has several concerns:

  • Low-level device communication, whether it be talking to the USB interface, a serial port, TCP/IP, or proprietary connection.
  • Managing state. Is the device turned on? Ready to talk to software? Busy?
  • Handling events. The device produced data: now we need to generate events to pass to other classes that are interested.

Certain devices such as sensors will have fewer concerns than say a printer/scanner/fax multifunction device. A sensor likely just produces a bit stream, while a complex device may have complex protocols and interactions.

Anyway, back to your specific question, there are several ways to do this depending on your specific requirements as well as the complexity of the hardware interaction.

Here is an example of how I would design the class hierarchy for a temperature sensor:

  • ITemperatureSource: interface that represents anything that can produce temperature data: a sensor, could even be a file wrapper or hard-coded data (think: mock testing).

  • Acme4680Sensor: ACME model 4680 sensor (great for detecting when the Roadrunner is nearby). This may implement multiple interfaces: perhaps this sensor detects both temperature and humidity. This object contains program-level state such as "is the sensor connected?" and "what was the last reading?"

  • Acme4680SensorComm: used solely for communicating with the physical device. It does not maintain much state. It is used for sending and receiving messages. It has a C# method for each of the messages the hardware understands.

  • HardwareManager: used for getting devices. This is essentially a factory that caches instances: there should only be one instance of a device object for each hardware device. It has to be smart enough to know that if thread A requests the ACME temperature sensor and thread B requests the ACME humidity sensor, these are actually the same object and should be returned to both threads.


At the top level you will have interfaces for each hardware type. They describe actions your C# code would take on the devices, using C# data types (not e.g. byte arrays which the raw device driver might use).

At the same level you have an enumeration class with one instance for each hardware type. Temperature sensor might be one type, humidity sensor another.

One level below this are the actual classes that implement those interfaces: they represent one device similar the Acme4680Sensor I described above. Any particular class may implement multiple interfaces if the device can perform multiple functions.

Each device class has its own private Comm (communication) class that handles the low-level task of talking to the hardware.

Outside of the hardware module, the only layer that is visible is the interfaces/enum plus the HardwareManager. The HardwareManager class is the factory abstraction that handles the instantiation of device classes, caching instances (you really do not want two device classes talking to the same hardware device), etc. A class that needs a particular type of sensor asks the HardwareManager to get the device for the particular enum, which it then figures out if it is already instantiated, if not how to create it and initialize it, etc.

The goal here is to decouple business logic from low-level hardware logic. When you are writing code that prints sensor data to the screen, that code should not care what type of sensor you have if and only if this decoupling is in place which centers on those hardware interfaces.


UML class diagram example showing the design described in this answer

Note: there are associations between the HardwareManager and each device class that I did not draw because the diagram would have turned into arrow soup.

  • Very interesting answer. I would ask for a quick edit: leave more explicit what is the inheritance/containment/collaboration between the four classes you mentioned. Thank you very much! – heltonbiker May 8 '15 at 20:00
  • I added a UML class diagram. Does this help? – user22815 May 8 '15 at 23:42
  • 2
    I'd say this is a killer answer, and it's not only going to help me a lot, but I believe could help a lot more people in the future. Besides, the class hierarchy example you provide maps our problem and solution domains remarkably well. Thank you very much again! – heltonbiker May 9 '15 at 23:21
  • When I read the question I realized there was more going on, so I went a little overkill and shared the whole design. This is more than a simple naming problem because the names are indicative of the design. I have worked with systems built this way and they worked fairly well. – user22815 May 9 '15 at 23:27

It may be a little difficult to find a single unifying convention here because these classes are spread out over a number of namespaces, (ConnectionInfoseems to be in CrystalDecisions, and DeviceInfo in System.Reporting.WebForms).

Looking at these examples, though, there seem to be two distinct uses of the suffix:

  • Distinguishing a class providing static methods with a class providing instance methods. This is the case for the System.IO classes, as underlined by their descriptions:

    Directory:

    Exposes static methods for creating, moving, and enumerating through directories and subdirectories. This class cannot be inherited.

    DirectoryInfo:

    Exposes instance methods for creating, moving, and enumerating through directories and subdirectories. This class cannot be inherited.

    Info seems like a slightly odd choice here, but it does make the difference relatively clear: a Directory class could reasonably either represent a particular directory or provide general directory-related helper methods without holding any state, whereas DirectoryInfo could only really be the former.

  • Emphasising that the class only holds information and does not provide behaviour that might reasonably be expected from the un-suffixed name.

    I think that the last part of that sentence might be the piece of the puzzle that distinguishes, say, ConnectionInfo from EmployeeInfo. If I had a class called Connection, I'd reasonably expect it to actually provide me with the functionality that a connection has- I'd be looking for methods like void Open(), etc. However, nobody in their right mind would expect that an Employee class could actually do what a real Employee does, or look for methods like void DoPaperwork() or bool TryDiscreetlyBrowseFacebook().

  • 1
    Thank you very much for your answer! I believe the first bullet in your answer definitely describes what goes on in .NET classes, but the second one has more value (and by the way is different), because it reveals better the intended abstraction: a thing to be used vs. a thing to be described. It was hard to choose which answer to accept. – heltonbiker May 9 '15 at 23:24

In general, an Info object encapsulates information about an object's state at some moment in time. If I ask the system to look at a file and give me a FileInfo object associated with its size, I would expect that object to report the size of the file at the time the request was given (or, to be more precise, the size of the file at some moment between when the call was made and when it returned). If the size of the file changes between the time the request returns and a time the FileInfo object is examined, I would not expect such a change to be reflected in the FileInfo object.

Note that this behavior would be very different from that of a File object. If a a request to open a disk file in non-exclusive mode yields a File object which has a Size property, I would expect the value returned thereby to change when the size of the disk file changes, since the File object doesn't merely represent the state of a file--it represents the file itself.

In many cases, objects that attach to a resource must be cleaned up when their services are no longer needed. Because *Info objects do not attach to resources, they do not require cleanup. As a consequence, in cases where an Info object will satisfy a client's requirements, it may be better to have code use one than to to use an object which would represent the underlying resource, but whose connection to that resource would have to be cleaned up.

  • 1
    This isn't exactly wrong, but it does not appear to explain .NET's naming patterns very well at all, since the File class cannot be instantiated and FileInfo objects do update with the underlying filesystem. – Nathan Tuggy May 8 '15 at 19:50
  • @NathanTuggy I agree that this does not relate well with this naming convention in .NET, but regarding my current problem, I believe it is very relevant and could induce a consistent solution. I upvoted this answer, but it's being hard to choose only one to accept... – heltonbiker May 8 '15 at 19:54
  • @NathanTuggy: I would not recommend using .NET as a model of any sort of consistency. It's a good and useful Framework that encapsulates a lot of good ideas, but a few bad ideas get captured in the mix as well. – supercat May 8 '15 at 19:55
  • @supercat: Sure; it just seems odd to answer a question about "why does .NET do things thisWay?" with "it would be a good idea to do things %THIS_WAY%". – Nathan Tuggy May 8 '15 at 19:57
  • @NathanTuggy: I didn't remember the exact particulars of the classes in question, but I thought FileInfo just held statically-captured information. Does it not? Also, I've never had occasion to open files in non-exclusive mode, but it should be possible, and I would expect there to exist a method that will report a file's current size, even if the object used for opening isn't called File (normally I just use ReadAllBytes, WriteAllBytes, ReadAllText, WriteAllText, etc.). – supercat May 8 '15 at 20:00

Considering the actual devices (sensors and receivers) are one thing, and their representation in software is another, I am thinking about naming some classes with the "Info" suffix name pattern.

For example, while a Sensor would be a class to represent the actual sensor (when it is actually connected to some working device), SensorInfo would be used to represent only the characteristics of such sensor. For example, upon file save, I would serialize a SensorInfo to the file header, instead of serializing a Sensor, which sort of wouldn't even make sense.

I don't like this distinction. All objects are "representation[s] in software." That's what the word "object" means.

Now, it might make sense to separate information about a peripheral from the actual code that interfaces with the peripheral. So, for instance, a Sensor has-a SensorInfo, which contains most of the instance variables, along with some methods that don't require hardware, while the Sensor class is responsible for actually interacting with the physical sensor. You don't have-a Sensor unless your computer has a sensor, but you could plausibly have-a SensorInfo.

The trouble is that this kind of design can be generalized to (almost) any class. So you have to be careful. You obviously wouldn't have a SensorInfoInfo class, for example. And if you have a Sensor variable, you may find yourself violating the law of Demeter by interacting with its SensorInfo member. None of this is fatal, of course, but API design isn't just for library authors. If you keep your own API clean and simple, your code will be more maintainable.

Filesystem resources like directories, in my opinion, are very close to this edge. There are some situations in which you want to describe a directory which is not locally accessible, true, but the average developer is probably not in one of those situations. Complicating the class structure in this fashion is, in my opinion, unhelpful. Contrast Python's approach in pathlib: There is a single class which "is most likely what you need" and various auxiliary classes which most developers can safely ignore. If you really need them, however, they provide largely the same interface, just with concrete methods stripped out.

  • Thanks for your answer, and kudos for your "have-a" construct ;) Your answer reminds me of the discomfort caused on some by the "DTO" (Data Transfer Object) concept - or its sibling the Parameter Object - which is frowned upon by more orthodox OO zealots because it usually does not contain methods (after all it is a data transfer object). In that sense, and summarizing also what others have said, I think the SensorInfo would be sort of a DTO, and/or also like a "snapshot", or even a "spec", representing only the data/state part of the actual Sensor object that "might be not even there". – heltonbiker May 9 '15 at 23:34
  • As with most design issues, there's no hard and fast rule. In the pathlib example I provided, PureWindowsPath acts a bit like an Info object, but it does have methods for doing things which don't require a Windows system (e.g. taking a subdirectory, splitting off a file extension, etc.). This is more helpful than just providing a glorified struct. – Kevin May 10 '15 at 0:11
  • 1
    Again, kudos for the "glorified struct" part :) . That reminds me of a related quote from Uncle Bob's "Clean Code" book, Chapter 6: "Mature programmers know that the idea that everything is an object is a myth. Sometimes you really do want simple data structures with procedures operating on them." – heltonbiker May 10 '15 at 16:21

I would say that context/domain matters, since we have high level business logic code and low level models, architecture components and so on...

'Info', 'Data', 'Manager', 'Object', 'Class', 'Model', 'Controller' etc. can be smelly suffixes, especially on a lower level, since every object has some information or data, so that information is not necessary.

Class names of the business domain should be like all stakeholder talk about it, no matter if it sounds weird or is not 100% correct language.

Good suffixes for data structures are e.g 'List', 'Map' and for hinting patterns 'Decorator', 'Adapter' if you think it's necessary.

To your sensor scenario, I would not expect SensorInfo to save what your sensor is, but SensorSpec. Info imho is more a derived information, like FileInfo is something like the size, you don't save or the filepath which is built from the path and the filename, etc.

Another point:

There are only two hard things in Computer Science: cache invalidation and naming things.

-- Phil Karlton

That always reminds me of thinking about a name just for a few seconds and if I didn't find any, I use weird names and 'TODO'-mark them. I can always change it later since my IDE provides refactoring support. It's not a company slogan which must be good, but just some code we can change every time we want. Keep that in mind.

ThingInfo can serve as a great read only Proxy for the Thing.

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

Proxy: "Provide a surrogate or placeholder for another object to control access to it."

Typically the ThingInfo will have public properties with no setters. These classes and the methods on the class are safe to use and will not commit any changes to the backing data, the object, or any other objects. No state changes or other side effects will occur. These can be used for reporting and web services or anywhere that you need information about the object but want to limit access to the actual object itself.

Use the ThingInfo whenever possible and limit the use of the actual Thing to the times you actually need to change the Thing object. It makes reading and debugging considerably faster when you get used to using this pattern.

  • Excellent. Earlier today I was analyzing my architectural needs, regarding the classes I used in the question, and came to this very same conclusion. In my case, I have a Receiver which receives data streams from many Sensors. The idea is: the receiver should abstract the actual sensors. But the problem is: I need the sensor info so that I can write them to some file header. The solution: each IReceiver will have a list of SensorInfo. If I send commands to the receiver which imply changing sensor state, these changes will be reflected (via getter) on the respective SensorInfo. – heltonbiker May 14 '15 at 3:00
  • (continued...) that is: I don't talk to the sensors themselves, I talk only to the Receiver via higher-level commands, and the Receiver in turn talks to each of the Sensors. But I eventually need information from the sensors, which I get from the Receiver instance via its List<SensorInfo> readonly property. – heltonbiker May 14 '15 at 3:02

So far nobody in this question seems to have picked up on the real reason for this naming convention.

A DirectoryInfo is not the directory. It is a DTO with data about the directory. There can be many such instances describing the same directory. It is not an entity. It is a throw-away value object. A DirectoryInfo does not represent the actual directory. You can also think of it as a handle or controller for a directory.

Contrast that with a class named Employee. This might be an ORM entity object and it is the single object describing that employee. If it was a value-object without identity it should be called EmployeeInfo. An Employee does indeed represent the actual employee. A value-like DTO class called EmployeeInfo would clearly not represent the employee but rather describe it or store data about it.

There's actually an example in the BCL where both classes exist: A ServiceController is a class that describes a Windows Service. There can be any number of such controllers for each service. A ServiceBase (or a derived class) is the actual service and it does not conceptually make sense to have multiple instances of it per distinct service.

  • This sounds similar to the Proxy pattern mentioned by @Shmoken, doesn't it? – heltonbiker May 18 '15 at 19:31
  • @heltonbiker it does not necessarily have to be a proxy. It could be an object that is not connected in any way to the thing it describes. For example you might get an EmployeeInfo from a web service. That's not a proxy. Further example: An AddressInfo does not even have a thing it can proxy because an address is not an entity. It's a stand-alone value. – usr May 18 '15 at 19:32
  • 1
    I see, that makes sense! – heltonbiker May 18 '15 at 19:34

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.