Hello friends, Not really a question, I am just thinking out loud here Please comment on my decisions and/or style of thinking. Please excuse me if this is too long, I tried to simplify this as much as possible.
I have designed a class LegacyReader that reads (and parses) data from an InputStream. As of now, it is a BufferedReader but that's minute. The most significant methods are the getMetaData() and getData(), this is where the parsing is specific to the legacy format. Knowing all along that a "New Format" will rule one day, I have decided to use an interface DataReader as follows:
My legacy reader class then implemented the DataReader interface. Now, since I am doing all of this as on a self-study stroll I occasionally forget (or disregard ) about the correct way inadvertently I instantiated my an instance as follows:
After reading, and mowing around here at the ranch, I realized I should have done something like:
Of course, as soon I made that change I got all sort of compilation errors, forcing me to modify the interface as follows:
Now, I am ready for the "New Format". All I need is a new class, let's say NewReader which implements the DataReader interface, along with code to instantiate the appropriate class:
I started to code my NewReader class and I realized, based on the revision, that the DataReader should have really been an abstract class ! The getInfo, getCount, getMode simply return a value which is assigned internally (possible during construction). I would appreciate your comments on my thoughts. Also, if you think that I missed something. Furthermore, and strictly from an OO standpoint, when should I use an interface or is an abstract class a better choice? Thanks in advance, Leslie
In this case, I would probably use just the abstract class, because this seems to be somewhat limited functionality. However, It is theoretically possible that you could use both an abstract class and an interface, if you think that this functionality could be expanded.
a). The DataReader interface b). The AbstractDataReader, which implements DataReader and provides "default" implementations of the methods c). The LegacyReader class, which extends AbstractDataReader and overrides some (or all) of the methods. d). The NewDataReader class, which extends AbstractDataReader and overrides some (or all) of the methods. My reasoning for this is along these lines: i). I have common functionality. This implies that I should have a super class. ii). The super class cannot implement all details; this implies that it should be abstract. iii). This is functionality that, theoretically, other classes in other class hierarchies could use. Some of those classes could have their own super classes, and would thus have to implement the interface rather than extend the abstract class. (Of couse, when I do this, it becomes a cut-and-paste-the-methods-from-the-abstract-class-into-the-new-class type operation.)
Piscis Babelis est parvus, flavus, et hiridicus, et est probabiliter insolitissima raritas in toto mundo.
In the case of your readers, I think you did the right thing with using an abstract class and inheritance. Why? Because it just seems... well, natural for the LegacyReader and the NewReader to extend from DataReader. They're both data readers, but they handle specific ways to read data. That's when you use implementation inheritance. Your subclasses inherit some basic implementation, and specialize where needed. An abstract class should represent a generic "thing". It's subclasses should be directly related to it. A Java interface is good to use when you want an object to provide the interface into a certain behavior(s), but it doesn't make sense to derive that object from some parent. Interfaces show action, they represent actions. You can have many types of objects that aren't even related, doing the action of an interface. For instance, say we have a Fish class. A fish isn't an action, it's a really generic thing. So we make it an abstract class. Now we have a class called Bass. It makes sense for Bass to derive from Fish, there's an obvious relationship there. Now let's say we create a Swimmer. Well, sure a swimmer is a thing, but more importantly swimmers do something... they swim! So we make it an interface, and give it a method swim(). Fish swim, right? So we make Fish implement the Swimmer interface. Now, some fish swim the same way, some don't. So we have Fish provide some common functionality, and have subclasses override swim() as needed. So we might ask, "Why not just make a method in Fish called swim(), and have the subclasses override that?" Well, some mammals can also swim, so can some birds, some reptiles, some amphibians, some insects, some arachnids... you get the point. Does it make sense to have all these things inherit from Fish just so they can swim? I know this might seem like a really simple example, but that's the point. All you have to do is ask yourself if there is an obvious parent-child (superclass-subclass) relationship. Much like in real life, don't force the relationship if it just doesn't seem natural
Jason, Thanks for that excellent explanation. While I'm comfortable with the actual differences between interfaces and abstract classes, your description did a great deal to put the issue in a simple, concrete context. Thanks!
that post of links had some excellent resources. i have often seen the following progression in developers' thinking as they become more accomplished "at" OO: 1. inheritance heirachies deepen, then flatten. first, developers equate OO too strongly with inheritance and also find concrete class implementations easier to handle conceptually that the interface/abstract class/concrete class mix. Rarely (but sometimes) you see Parent p = new Child(); Most of the time you see Child c = new Child(); 2. interfaces start to be used. often people are influenced by well designed 3rd party APIs that force the use of interfaces for client implementations - other times, an improved understanding of OO makes the approach in (1) unsustainable for all programming problems. as a result, references change : so Parent p = new Child(); becomes (e.g.) SomethingAble f = new Child(); 3. The the Parent class gets a re-examination, and often becomes an abstract class that implements the interface. for public APIs, the interface is still used - we don't require clients to extend the abstract class to use the features of the lib; they get back an interface reference to a concrete instance that extends the abstract class. so - a recent good implementation in our team has localised the inheritance to concrete classes that extend abstract classes as a means of implementing common behaviour - but providing the interface as the public interface to clients. why has this been good? we have been able to improve the software implementation (in the abstract class heirachy) without impact on clients (who use the interface). i fully admit this works for us but might well not for others. so if the question is "Interface or abstract class", i would say "why not both?" hope this helps, peter [ June 08, 2003: Message edited by: peter greaves ]