Extending class to be a base class *properly*

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Matthias Kaeppler

    Extending class to be a base class *properly*

    Sorry if this has been discussed before (I'm almost certain it has), but
    I didn't know what to google for.

    My problem is, I have a class, a gtkmm widget, and I want it to serve as
    a base class now, but I'm not sure if I'm taking the proper steps in
    order to not break the whole class.

    Are there any guidelines what I have to watch out for?

    One question would e.g. be:
    Some (or many) members which have been private so far, probably need to
    be protected now. One problem is that I can't really tell in advance
    which of those member should stay private and which ones should become
    protected, because that mostly depends on what people intend to do with
    their class when deriving from my widget.
    But I suppose it's a bad idea to take this as an argument for making ALL
    private members protected now?

    Another question:
    Is it sufficient to make the members in question protected, or should
    they stay private and protected accessor methods introduced?

    --
    Matthias Kaeppler
  • Kanenas

    #2
    Re: Extending class to be a base class *properly*

    On Fri, 15 Apr 2005 19:49:51 +0200, Matthias Kaeppler
    <nospam@digital raid.com> wrote:
    [color=blue]
    >Sorry if this has been discussed before (I'm almost certain it has), but
    >I didn't know what to google for.
    >
    >My problem is, I have a class, a gtkmm widget, and I want it to serve as
    >a base class now, but I'm not sure if I'm taking the proper steps in
    >order to not break the whole class.
    >
    >Are there any guidelines what I have to watch out for?
    >[/color]
    One of the few general principles I can think of is to make sure
    objects have the appropriate polymorphic behavior. What happens if a
    Descendant* is used as a Base*, or a Descendant& is used as a Base&?
    Most everything else depends too much on the design specifics;
    consequently, some of my suggestions below may not work for your case.
    [color=blue]
    >One question would e.g. be:
    >Some (or many) members which have been private so far, probably need to
    >be protected now. One problem is that I can't really tell in advance
    >which of those member should stay private and which ones should become
    >protected, because that mostly depends on what people intend to do with
    >their class when deriving from my widget.
    >But I suppose it's a bad idea to take this as an argument for making ALL
    >private members protected now?
    >[/color]

    C++ FAQ section 19, answers 7-9 deal with public/protected interfaces:

    Just remember that when the author talks about return on investment,
    investment in the protected interface will reduce costs for
    descendants associated with interface maintenance and upgrade.

    What to make protected and what to make private is a difficult design
    issue to fully address in the general case. My first bit of advice is
    to transfer few private members to the protected interface at the
    start. You can always add to the protected interface in later
    versions.

    Some of the same design considerations for public interfaces will
    apply to protected interfaces. For instance, divorce implementation
    from an object's more abstract properties as much as possible. You
    won't get as much of a separation in the protected interface as in the
    public, but you will get some. This can be as simple as typedef-ing
    types for protected fields. The separation protects descendants from
    changes in implementation. Another consideration is speed versus
    stability. Private methods can be unstable in that they don't perform
    many safety checks. Public methods should provide more in terms of
    stability, perhaps at a speed cost. Protected methods could be
    somewhere in between, checking subtle errors but leaving more obvious
    ones to the developers of the descendants. Just don't forget to
    document what future developers need to handle.

    If you've got the time, another tool which sometimes applies is to
    define invariants on the data stored in an object. All public
    operations on the object must not invalidate the invariant. If a
    private method breaks an invariant, don't make it protected. Similar
    to what you later suggest, you can add protected methods which
    accomplish the affects of particular sequences of private methods.
    You can define relaxed versions of the public invariants for the
    protected interface. Each protected method respects the protected
    invariants, and particular sequences of protected methods will
    preserve the public invariants. In the documentation for the
    protected interface, require all public methods of descendants which
    use a protected method must use it as part of such a sequence. This
    isn't as safe as the public invariants, but is sometimes all you can
    achieve.

    Related to invariants are preconditions and postconditions,
    specifically of the private methods. For safety's sake, sometimes
    you'll want to create protected methods which check the preconditions
    and then call a private method only if the check succeeds, or create
    protected methods which clean-up after calling some private method.
    [color=blue]
    >Another question:
    >Is it sufficient to make the members in question protected, or should
    >they stay private and protected accessor methods introduced?[/color]

    Creating protected accessors to private members is an intermediate
    step (and a final one for some private fields). A more final solution
    would be to define protected methods which "partially expose" the
    private interface. These protected methods hide implementation
    details, perform aggregates of the private operations (as when dealing
    with invariants) and define operations on private fields (which may be
    unneeded for the base's member functions).

    For example, suppose a base class has a set of files. The set is
    invisible publically, but it makes sense for subclasses to add to,
    remove from or query what's in the set. The set is implemented as a
    priority queue (and has no method corresponding to "member of"), but
    this detail is to be hidden from subclasses. Thus the base is given
    protected methods which treat the priority queue as a set. Not a
    great example, as it makes sense for a priority queue to support the
    interface of a set, but still illustrative.

    Kanenas

    Comment

    Working...