Say I have a customer
table and an address
table. Obviously a customer can have more than one address, and more than one customer can be at the same address, so I have a many to many relationship.
The name of the table that allows them to be joined together can simply be customerAddresses
.
So I think the scheme I use is to split the two entities into "thing that owns" and "thing that is owned", and that gives the simple name above.
Edit: It might be better to use the analogy of "thing that acts" and "thing being acted upon" for some cases.
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.
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.
Best Answer
I think this may well be the only case when prefixes are useful.
Classes and interfaces really do have different semantics, and because both are types, are easy to mix up.
When I see a class declaration, I can instantly tell what it derives from and what it implements:
It would be harder to understand if the definition looked like
And how would you call
ListClass
? Clearly it's not justListBase
because it's usable on its own.So, we either have to solve a problem we just invented, or adapt a prefix that separates classes from interfaces, which is what .NET Framework designers did.
Also, personally I find it useful when I can tell from the method signature that it works with interfaces.
It's like a signal to my head: hey, I can implement this! I can feed the method whatever matches the contract.
Of course one can argue it's not that important but this is one of those little things that make the prefix worth it for me. It's especially useful when writing a lot of code with IoC containers when you constantly work with interfaces and need to easily differentiate between the two.
By the way, not only the interfaces have prefixes in .NET type system. Don't forget about the generic type parameters:
This is another situation when it's extremely useful to be able to differentiate between classes and another kind of types.