richard@pantor.UUCP (Richard Sargent) (01/18/90)
We have encountered a problem designing a tree of classes, that I hope someone out there can help resolve. I'm sure that it is just a case of looking at the problem "the wrong way", but I can't see the right way. [Apology: I'll use some PC terminology in the description, but the platform and hardware is not the problem, per se.] We want to be able to provide a base class called Display, which has derived classes for specific types of displays (such as Vista, Targa, VGA, raster file, etc.). Our first pass results in the following tree: Display | +--------+-----+--------+--------+ | | | | | Vista Targa VGA RasterFile etc. Now the problem comes when we try to code for the display device. Ideally, we want to be able to use an instance of class Display, so that our code is *written* device independent. We would like to simply code "Display device" and apply operations to "device", without concern for just which device it really is. The thinking is the constructor for class Display is able to determine what device it present and construct the appropriate class. An alternative, but less elegant solution, is to code something like the following, but I don't even know if this is valid: switch (inq_device_type()){ case dv_VISTA: Vista device; break; case dv_TARGA: Targa device; break; case dv_VGA: VGA device; break; . . . } So the bottom line is: How can I set up a hierarchy of device classes that will allow me to code (my main application, at least) to be independent of the display device? All help is appreciated. If any clarifications are needed, please ask. Thank you. Richard Sargent Internet: richard@pantor.UUCP Systems Analyst UUCP: ...!mnetor!becker!pantor!richard
ark@alice.UUCP (Andrew Koenig) (01/19/90)
In article <44.UUL1.3#5109@pantor.UUCP>, richard@pantor.UUCP (Richard Sargent) writes: > So the bottom line is: How can I set up a hierarchy of device > classes that will allow me to code (my main application, at least) > to be independent of the display device? I don't see anything wrong with what you've done so far. You're missing just one little trick. The idea is to never use the object directly. Rather, refer to it through a pointer or reference to a base class object. For example: class Display { /* ... */ }; class ColorDisplay: public Display { /* ... */ }; class HighResDisplay: public Display { /* ... */ }; Somewhere you have to make a decision on what kind of display you're using. Probably the most direct way to do it is something like this: Display* dp; if (cond1) dp = new ColorDisplay; else if (cond2) dp = new HighResDisplay; else /* ... */ Now you code all display operations in terms of pointer operations on dp dp->clear(); dp->draw(image); // and so on When you're finally done, you can free it: delete dp; but for this to work, your Display base class must have a virtual destructor. Perhaps a better way of structuring it, if you can, is to do all the real work in a subroutine that takes the Display object as a formal parameter. Then the allocating and freeing are done in one routine and everything else is done somewhere else. If you do this, you can make that parameter a reference to a Display rather than a pointer to a Display, which you may find more natural: void MyApplication(Display& d) { // ... d.clear(); // and so on } main() { if (cond1) { ColorDisplay d; MyApplication(d); } else if (cond2) { // ... } // ... } Now you don't have to allocate and free Display objects on the free store -- they're local to the various branches of the `if'. Hope this helps. -- --Andrew Koenig ark@europa.att.com
shap@delrey.sgi.com (Jonathan Shapiro) (01/20/90)
In article <44.UUL1.3#5109@pantor.UUCP> richard@pantor.UUCP (Richard Sargent) writes: >So the bottom line is: How can I set up a hierarchy of device >classes that will allow me to code (my main application, at least) >to be independent of the display device? > >Richard Sargent Internet: richard@pantor.UUCP >Systems Analyst UUCP: ...!mnetor!becker!pantor!richard Right idea. Wrong implementation. By the time you hit the Device constructor, you are essentially saying that you already are constructing the appropriate kind. What is needed is a single library routine that determines the device type, allocates the right kind, and returns a Device *: Device * getDevice() { kind = determine_which_graphics_head(); switch(kind) { case Targa: return new Targa; case Wombat: return new Wombat; ... } return 0; } Jonathan Shapiro Silicon Graphics, Inc.
beard@ux1.lbl.gov (Patrick C Beard) (01/22/90)
In article <44.UUL1.3#5109@pantor.UUCP> richard@pantor.UUCP (Richard Sargent) writes: #We have encountered a problem designing a tree of classes, that I #hope someone out there can help resolve. # #We want to be able to provide a base class called Display, which has #derived classes for specific types of displays (such as Vista, Targa, #VGA, raster file, etc.). Our first pass results in the following #tree: # # Display # | # +--------+-----+--------+--------+ # | | | | | # Vista Targa VGA RasterFile etc. # This seems like a perfectly reasonable approach to display design. You would want virtual functions defined in the Display class for every operation you would want to support across all of the devices. #Now the problem comes when we try to code for the display device. #Ideally, we want to be able to use an instance of class Display, #so that our code is *written* device independent [sic]. #to simply code "Display device" and apply operations to "device", Here is the case where a switch statement is appropriate: you must decide at run-time what particular instance to allocate. This decision only has to be made once, and is automated throughout the rest of your program by the use of virtual functions. Write a function of the form: Display* AllocDisplay() { Display *device = nil; // default is a display we don't know about. switch (inq_device_type()){ case dv_VISTA: device = new Vista; break; case dv_TARGA: device = new Targa; break; case dv_VGA: device = new VGA; break; /* etc... */ } return device; } [Notice you have to use new to get the object to persist beyond the AllocDisplay function.] This isn't inelegant, it is the only way to do it. Another approach might be to use multiple inheritance and have the object you allocate inherit methods for all devices, and knows internally what device it is talking to, but I think that it would probably be a rat's nest to implement. The one drawback I see to the above method, is that if you add more display types, you'll have to change this function to handle more types. But the changes will be isolated to this one function, all the rest of your code only has to know about the base class. I think this brings up an important issue: use object oriented technology where appropriate, standard functional programming where appropriate. ------------------------------------------------------------------------------- - Patrick Beard, Macintosh Programmer (beard@lbl.gov) - - Berkeley Systems, Inc. ".......<dead air>.......Good day!" - Paul Harvey - -------------------------------------------------------------------------------