|
From: Leslie Sanford on 11 Jan 2008 14:45 As a general rule, I shy away from implementation inheritance. However, I do sometimes find it useful. My question is whether it's considered bad design for a derived class method to call the base class method it is overriding. Something like this: class BaseClass { public: virtual void DoSomething(); }; void BaseClass::DoSomething() { // Yadda, yadda... } class DerivedClass : public BaseClass { public: void DoSomething(); }; void DerivedClass::DoSomething() { BaseClass::DoSomething(); // Yadda, yadda... } Is seems to me that this sort of thing could be the source of ambiguity. A developer needs to know if calling the base class method is allowed, and if so, when the base class method should be called, either at the beginning of the overridden method or at the end. As I said, I usually avoid implementation inheritance if possible, but sometimes in refactoring code, I find it useful to push duplicated behavior as far up the inheritance tree as possible. But I've sometimes found that this means that an overridden method needs to call the base class method in order to maintain the class's invariants. I want to avoid creating a mess, though. And I want to be consistent in my design. Any feedback is appreciated.
From: Dmitry A. Kazakov on 11 Jan 2008 15:21 On Fri, 11 Jan 2008 13:45:25 -0600, Leslie Sanford wrote: > My question is whether it's considered bad design > for a derived class method to call the base class method it is overriding. Of course not. How constructors could work otherwise? It makes a lot sense to have extensible methods, when the base type could enforce execution of some code in the prologue or epilogue of specific body of the method. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de
From: H. S. Lahman on 11 Jan 2008 18:19 Responding to Sanford... > As a general rule, I shy away from implementation inheritance. However, I do > sometimes find it useful. My question is whether it's considered bad design > for a derived class method to call the base class method it is overriding. > Something like this: > > class BaseClass > { > public: > virtual void DoSomething(); > }; > > void BaseClass::DoSomething() > { > // Yadda, yadda... > } > > class DerivedClass : public BaseClass > { > public: > void DoSomething(); > }; > > void DerivedClass::DoSomething() > { > BaseClass::DoSomething(); > > // Yadda, yadda... > } But this is not implementation inheritance; the BaseClass::DoSomething method is explicitly called in the subclass implementation rather than being resolved by inheritance. This is no different than class Standalone1 { public: void DoSomething(); } class Standalone2 { public: void DoIt() private: Standalone1* myStandalone1; // implement peer association } void Standalone2::DoIt() { myStandalone1->DoSomething(); ... } The reason we avoid implementation inheritance is because it adds a dimension of complexity to resolving what implementation actually gets invoked when access is polymorphic. The problem lies in maintenance. During maintenance it is possible to break existing clients when the implementation definition is moved (very rare) or when implementation overrides are used (a much more common problem). That's because the actual implementation is resolved through the inheritance mechanism. Therefore changes to the generalization tree may change which implementation actually gets invoked even though clients may still access the tree the same way with the same expectations. But when one explicitly invokes the method in the leaf subclass it doesn't matter where that implementation is. Using the generalization tree is just a convenient mechanism for organizing how the developer thinks about the problem space. Consequently there is no ambiguity about which implementation the leaf subclass uses. Thus your example represents the preferred way to eliminate code redundancy because it is quite explicit which implementation is to be used _at the leaf subclass level_. IOW, it doesn't matter how the generalization tree is modified subsequently (e.g., how many levels are inserted or how many alternative implementations are added in other superclasses); the subclass will still invoke the same implementation. -- There is nothing wrong with me that could not be cured by a capful of Drano. H. S. Lahman hsl(a)pathfindermda.com Pathfinder Solutions http://www.pathfindermda.com blog: http://pathfinderpeople.blogs.com/hslahman "Model-Based Translation: The Next Step in Agile Development". Email info(a)pathfindermda.com for your copy. Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php. (888)OOA-PATH
From: Daniel T. on 11 Jan 2008 22:17 "Leslie Sanford" <jabberdabber(a)bitemehotmail.com> wrote: > As a general rule, I shy away from implementation inheritance. However, I do > sometimes find it useful. My question is whether it's considered bad design > for a derived class method to call the base class method it is overriding. I think HS's response was good, but I would like to expand on it some... > Something like this: > > class BaseClass > { > public: > virtual void DoSomething(); > }; > > void BaseClass::DoSomething() > { > // Yadda, yadda... > } > > class DerivedClass : public BaseClass > { > public: > void DoSomething(); > }; > > void DerivedClass::DoSomething() > { > BaseClass::DoSomething(); > > // Yadda, yadda... > } > > Is seems to me that this sort of thing could be the source of ambiguity. A > developer needs to know if calling the base class method is allowed, and if > so, when the base class method should be called, either at the beginning of > the overridden method or at the end. Note, the derived class needs to know that about every base class method (if and when to call it.) It's fair to say that such information is required of every method/function in the program. So its not much of a burden. To expand on HSs example... How is your code above any different than: class BaseInterface { public: virtual void DoSomething() = 0; }; class BaseImplementation { public: void DoSomething() { /* Yadda, yadda...*/ } }; class DerivedClass : public BaseInterface { BaseImplementation impl; public: void DoSomething() { impl.DoSomething(); // Yadda, yadda... } }; In your code, implementation inheritance is used, in my example it is not. As long as the BaseClass contains no data and has no non-virtual member functions, your example is every bit as flexible as mine and your example requires fewer classes. > As I said, I usually avoid implementation inheritance if possible, but > sometimes in refactoring code, I find it useful to push duplicated behavior > as far up the inheritance tree as possible. But I've sometimes found that > this means that an overridden method needs to call the base class method in > order to maintain the class's invariants. I want to avoid creating a mess, > though. And I want to be consistent in my design. Any feedback is > appreciated. I don't have a problem with implementation inheritance, except when it comes to data containment and non-virtual member-functions. I.E., those things which the derived classes simply cannot change. I also have a big problem with base classes that are not abstract (in static typed languages like C++,) that means you are missing an abstraction in your code.
From: Leslie Sanford on 11 Jan 2008 23:06
"Daniel T." wrote: > "Leslie Sanford" wrote: <snip> > Note, the derived class needs to know that about every base class method > (if and when to call it.) It's fair to say that such information is > required of every method/function in the program. So its not much of a > burden. I was using a third-party library recently in which I had a hard time tracking down an obscure bug. I had derived a class from one of the library's base classes. This class has many virtual methods that you can override to provide your own custom behavior. The problem turned out to be that I wasn't calling the base class method in my derived class's overriden method. This requirement wasn't mentioned any where in the documentation. I guess what would be nice would be some type of language mechanism that would enforce the correct policy, when/if to call the base class's method in a derived classes overriden version of that method. > To expand on HSs example... How is your code above any different than: > > class BaseInterface { > public: > virtual void DoSomething() = 0; > }; > > class BaseImplementation { > public: > void DoSomething() { /* Yadda, yadda...*/ } > }; > > class DerivedClass : public BaseInterface { > BaseImplementation impl; > public: > void DoSomething() { > impl.DoSomething(); > // Yadda, yadda... > } > }; > > In your code, implementation inheritance is used, in my example it is > not. As long as the BaseClass contains no data and has no non-virtual > member functions, your example is every bit as flexible as mine and your > example requires fewer classes. Ok, but what if the BaseClass does have data? In the situation I described above, the base class's virtual methods had behavior that managed the class's state. If a derived class overrides one of those methods and does not call the base class method when it is called, the class's invariants may be violated. I guess there's something about this that just doesn't sit right with me. You'd have to rely on documenation to know when/if you have to call the base class's method. Should a base class be designed in such a way that an overriden method can always call the base class's method without bad things happening? |