Hiding class pointers -- was it a good idea?

Robert Fraser fraserofthenight at gmail.com
Thu Aug 16 00:20:05 PDT 2007


James Dennett Wrote:

> It's trivially detected by various automated tools, which
> can flag any non-abstract base class.  (Such classes almost
> invariably indicate bad design in any case.)  

Now that's just patently untrue. I've never done any serious work in C++ outside of school, so perhaps that's a belief some people hold in the C++ world, but I doubt it's a very common one.

I'd say it's about an 80-20 split for code I write and 60-40 in standard libraries. That is, 80% of the time I'm extending something I am indeed extending an abstract base, but 20% of the time I'm extending a concrete class is helpful, and in the case of libraries you can't change, necessary.

Here's a real-world example. At work recently (I don't think this violates my NDA...) I was asked to write a fake POP & IMAP server (it gives a certain number of new messages per hour to every account for load testing) in Java. I have a class UserAcct which is used to track persistent accounts so that UIDs remain contiguous. When I added support for IMAP idle, some additional information was needed to handle idling subscriptions/send events/etc. My options were to either create an "imap idle subscription" class to wrap a subscription with a pointer to the UserAcct class (composition) or to extend the UserAcct with a SubscribedUserAcct (only a small fraction of the users would have IMAP idle enabled), or just to add some extra fields to UserAcct.

I selected the second option (extension) here. Arguably, composition may have been the better choice from a design standpoint, and I certainly respect this opinion. However:

- Composition removes polymorphism: The obvious. If I needed to override or change a function of the base class, this would not be an option with composition. In addition, were I using a wrapper class, I copuld no longer keep a map of "UserAcct" objects, I would need two different data structures, one of the wrapper and one of the accounts.

- Composition costs additional memory: the extra 8 bytes (in Java and D, not sure about C++) overhead of the additional object, plus another 4 for the reference (actually, that averages to 6 because the Sun JVM likes to align classes on 8-byte word boundaries). Since I had recently run into some OutOfMemory errors when it was loaded with 50,000+ accounts, I was loath to incur much additional heap usage than I had to.

Anyways, this is getting a bit off-topic. However a sweeping statement like "all base classes should be abstract" just seems very wrong to me.

I personally think D's behavior is perfect, though I think non-polymorphic struct inheritance (i.e. as syntactic sugar for mixins) and constructors would be nice. The distinction, IMO, shows D to be a more mature language with different use-cases for structs and classes clearly defined.

That, and you've mentioned specific idioms in a few of your messages. Idioms are great, but not everybody follows them (especially people new to a language). Making something part of the language standardizes it. That's why having unittests, asserts, preconditions, etc., in D is such a boon: while that could all be done using a library, there'd probably be three or four different libraries out there to do it and 75% of users wouldn't know about them or use those features at all.



More information about the Digitalmars-d mailing list