1

(Please keep in mind I'm a relatively inexperienced programmer. I'm aware that this question can be viewed as too open ended because there are so many philosophies on inheritance in OOP. This question is directed more toward my thought process, i.e., am I approaching this scenario in a way that would seem correct by an experienced programmer? What is it about my ideas that are obviously accurate or inaccurate?)

I'm designing an inventory system that tracks information about IT hardware including computers, printers, switches, routers, mobile phones, hard drives, and other devices. I have a database designed and I'm now in the process of planning a front-end application. I intend for the application to use data access layer, business logic layer, and business entities. I'm still in conceptual planning stages and I realize I should not be considering implementation details at this point, but my mind tends to race forward. The way the search feature is supposed to work is: the user enters search criteria and executes the search. A list of matching devices (or the only matching device) is returned. The list of devices is displayed in some type of list view, and a details view is displayed for the device when it is selected.

I've been wondering if using inheritance in my eventual business entities would benefit my application, or if it will add unneeded complexity... or if it's just plain wrong. My initial thought is the setup as illustrated:

(This is not the actual design, just a simplified concept for this post) In addition to each device having type-specific attributes, any device at any given time can be in one of two states, active or archived. I don't know how to model this at all.

Conceptual Class Diagram

I started thinking about how queries might work when using this setup. It seems simple enough to query any specific subclass of a device from a data access layer, such as

Pseudocode:

Computer comp = getComputerBySerialNumber(sn);
List<Router> routers = getRouters();

// I can then write code to display a list of basic device information and
// additionally display all details of a computer or router when requested.
// Obviously display would be handled in a different layer.

The issue that lead me to writing this question is how to handle queries such as "get all devices from a specific location".

// Returns a list of devices regardless of device type
// as long as they have the same location
List<Device> devsFromLocation = getDevicesByLocation(loc);

// List contains computers, routers, printers, etc.

How would I display basic device details and then specific device details if I have a base class object reference? (Trying to avoid crazy casting or using reflection). Each of the devices from that location would be missing the specific attributes of their subtypes. If I wanted to display all of the data about a particular device I would have to query the database again to get the remaining fields. Is having to execute another query a sign of poor design? Furthermore, how would I determine the correct type of device? Possibly a large switch/case statement testing each Device.Type attribute, execute the correct query, return the full subtype and display details to the user? Or...Is it better to return separate lists containing objects of full subtypes and then iterate through all lists to display common attributes in the list view and then easily display subtype details in the details view?

Is this a useful case of inheritance or am I misusing it? I'm currently suffering from the problem of too much information. I read everything possible about OOD, and I have so many rules and guidelines in my head I never know if I'm doing anything right. I feel like my brain is looking to apply the information I've been absorbing, so I'm imagining an implementation that is incorrect. I keep thinking about all this business about programming to abstractions to keep code flexible, but at some point you need to deal with concrete classes, right? From my perspective, inheritance is about behavior and not attributes. Since I'm not actually modeling any behavior (or am I and I just can't see it?) and just collecting data about the devices it's making it difficult for me to interpret their relationships. Since these classes are essentially dumb collections of attributes I feel like they should all be separate classes. Then again, I will have duplicate fields in all the classes, but does it really matter in this case?

I know there are tons of books on OOD, inheritance, composition, etc. I've read some of those books; I'm currently reading some more and I've spent days researching online. Everyone conveniently uses obvious examples of inheritance. I'm having nightmares about Fruit, Animal, and Shape examples.

Thank you for taking the time to read my question, and thanks for any information or insight you can provide. Please feel free to offer any other tips, insights, trade secrets, maps to buried treasure, a sea-worthy boat and a sextant, or anything else you think might help.

TheSecretSquad
  • 303
  • 2
  • 14

1 Answers1

1

The most straightforward way is to add an information method to your base class that returns the information, and have each derived type override that method with the correct information.

This would allow you to do:

foreach (Base b in list)
  Write(b.Information());

If your output needs to be more dynamic consider a device information object.

Having skimmed your post only (so I might have missed some detail that makes this a bad idea) I would also suggest that you make Active State a member of the Device base class, since a Device 'has-a(n)' Active State (and Archive State if the same is true).

Other than that your design is nice and simple and makes sense.

EDIT:

So you might have a Report object that you want the information to be written on so you would have:

class Device{
  write_information_to_report(Report report);
}

This can be overridden by the derived classes to add the derived specific information.

You could abstract the Report out also to be somthing like InformationOutputter if you really wanted to.

A good example of this sort of thing looking at drawing shapes:

class Graphics{
  line(int, int, int, int) = 0;
  curve(float, float, float, float) = 0;

class OpenGLDrawer : Graphics {
  line(int, int, int, int) { // Use open gl to draw a line };
  curve(int, int, int, int) { // Use open gl to draw a line };
}

class DirextXDrawer : Graphics {
  line(int, int, int, int) { // Use directX to draw a line };
  curve(int, int, int, int) { // Use directX to draw a line };
}

class Shape{
  draw(Graphics g) = 0;
}

class Triangle : Shape{
  draw(Graphics g) { g.line(... /* draw a triangle using line */ };
}

class Circle : Shape{
  draw(Graphics g) { g.arc(... /* draw a circle using arc */};
}

Here you can see you can have each object responsible for its own function, common functionality can be shared at the baseclass level and any graphics object can draw any shape.

sji
  • 1,877
  • 1
  • 13
  • 28
  • Thank you for your response. What would this information method do? Just return a string of all the information? What if I want to bind specific attributes to UI fields? – TheSecretSquad Jul 04 '12 at 17:24
  • 1
    @reallythecrash Well it could do whatever you felt that it needed to do. Ideally you would pass some object to the information method and it could 'write' the information itself on that object. I will expand on this in my answer in an edit ... – sji Jul 06 '12 at 13:04