Session E-PATT

Object-Oriented Heuristics

Steven Black
steveb@stevenblack.com


Steven has been a Fox developer since 1986. He markets Steven Black's INTL Toolkit , a multi-lingual framework for FoxPro and Visual FoxPro, which he created in 1993 and continues to refine. He has been a featured speaker at FoxPro DevCons and regional conferences, and his contributions occasionally darken the pages of VFP books and magazines. His company, SBC / UP!, is based in Kingston, Ontario, and operates worldwide. He specializes in multi-lingual, multi-site, and other challenging FoxPro projects, including out-of-control project turn-arounds and cleanups. He consults with small developers as well as Fortune 500 companies, national and international government agencies, and software development companies to elevate their development teams. He is also the creator and webmaster of the Fox Wiki, a popular collaborative topic-based Visual FoxPro website at http://fox.wikis.com.

heu·ris·tic: 1. to discover; involving or serving as an aid to learning, discovery, or problem-solving by experimental and especially trial-and-error methods. 2. of or relating to exploratory problem-solving techniques that utilize self-educating techniques (as the evaluation of feedback) to improve performance. 3. Rule of thumb.

In this session for Visual FoxPro developers of all levels, Steven will discuss some common object-oriented heuristics, and discuss how they apply (or don’t apply) in Visual FoxPro.

Introduction

A heuristic is a “rule of thumb”, something to help you make decisions. To prepare this session, I relied quite heavily on the first and thus far the best formal presentation of object-oriented heuristics, a book titled Object Oriented Design Heuristics (1996) by Arthur Riel, ISBN 020163385X. This book describes 68 heuristics that help you make design decisions, 68 rules for dealing with design issues you may face every day in your development work.

Let’s look at some of these of these heuristics, and some others, and how they might (or might not) apply in Visual FoxPro.  

VFP Class and Object Heuristics

These are rules related to classes and objects.

Heuristic: “All properties should be hidden within its class.”

Applies to VFP, always.Rarely seen in practice.

This means that class properties should be hidden behind accessor methods. Thus prefer x.setFoo(“bar”) to x.Foo=“bar”. 

The main motive for this heuristic is maintenance: Respecting this heuristic makes you more free to modify the behavior and implementation of the Foo property without unduly impacting client code. When leaving a property public, the developer should ask himself “what will other classes be doing with this property, and why doesn’t this class perform this operation for me?”.

When you follow this heuristic you are less likely to inadvertently mess-up the state of the object. Creating member functions is extra coding, but on balance this extra work is worth it.

There are some who feel that it’s okay, when accessing properties from within the class, to use direct property access. In fact that’s how I typically do it because it’s so convenient. At some point, especially if the project is a large one, direct property access gets in the way and some sort of incremental switchover to indirect property access starts to happen for some properties. The end result is a bit of a mess, with some direct access properties and some indirect ones. On balance this is tolerable but it would be best if only one mechanism was used throughout.

Hiding members, making them PROTECTED in VFP-speak, becomes especially important when you consider “intellisense”. With intellisense, the properties and methods appear in a dropdown list as you type code. You get intellisense when using VFP COM objects in VB and VBA, and intellisense is promised inside VFP for Version 7. This is great! However, if you expose all the spurious native and custom properties, intellisense is far less useful: you get a list with dozens and dozens of methods and properties. If you create a subclass of the Custom base class, for example, of what practical use are the Height, Width, and Picture properties? They just clutter up intellisense

Heuristic: “Minimize the number of messages in the protocol of a class.”

Applies to VFP, always.

A real problem with vast interfaces is it’s hard to find what you need, and this greatly hurts reusability and maintainability.Having too many methods means either the class is doing too much, or that the methods have the wrong granularity. For example, compare the following calls:
x= Create(“SomeClass”)
x.OpenFiles()
x.DoSomething()
x.CloseFiles()

x= Create(“SomeClass”)
x.Process()

The code on the left begs the question: why is the client responsible for opening and closing files for this object? Why doesn’t the object manage such mundane tasks itself? More to the point, if operations can be combined, why not?

This is related to this next heuristic:

Heuristic: “The arguments of a method should only include operands, no options.”

Applies to VFP, always.

In my travels, I often see violations of this heuristic, which is also known as the Operand Principle. An operand argument to a routine represents an object on which the routine will operate. An option argument represents a mode of operation. An argument is an option if, assuming the client had not supplied its value, it would have been possible to find a reasonable default.

In other words, method parameters should never include items for which a sensible default can be found. If options (the sensible defaults) are to be varied, these items should be changed with Setxxx() methods prior to invoking the routine.

This heuristic has many benefits:

But on the downside, are we just trading argument complexity for call complexity? After all, the calls will be much simpler, but there will be more of them since we must include calls to option-setting procedures.

No! The only extra code will be for options set other than the default. Here the complexity is the same as with option arguments. (You may have a few more keystrokes to type, but what counts is the number of pieces of information you have to provide, and it is the same with both approaches.) The big difference is that you need only pay attention to the options that are relevant, whereas option arguments force you to specify all options explicitly.

Also note that frequently a certain option will apply to many successive calls. In that case, using option arguments forces you to specify it each time. With this heuristic, you gain even if the value is not the default: you set it the first time around, and it stays in place until explicitly changed.

Heuristic: “Implement a minimal class interface that all classes understand.”

Applies to VFP, always. 

One of the great things about Delphi is all objects inherit from the tObject base class. Add a property or method to tObject, and all objects get it. We can’t do this in VFP, unfortunately, so implementing this heuristic is a little more work than it may be in Delphi but it is worthwhile nonetheless. In Visual FoxPro there are many times when we need to provide common behavior into many classes.

Here is a partial list of functionality that I like every class to have.

If your application uses hooks and hook operations, then consider including the properties and methods that support hooks in the minimal class interface.

A nice touch: Visual MaxFrame Professional uses a zReadMe() pseudo-method, bracketed with #IF .F. .... #ENDIF, wherein class documentation is stored.The “z” makes it sort to the bottom of the properties sheet.

This is my personal list. All frameworks implement their own range of common class interfaces. It’s a sensible thing to do.

Heuristic: “Events Should Always Call Methods.”

Applies to VFP, always.

Possibly the worst place to put behavior code is in an event snippet. Why? Mostly because events are typically generated by the user interface layer, and putting behavior in the user interface layer is your one of the classic OOP rookie mistakes since it mixes the presentation layer with stuff that may better be placed in a business layer, or otherwise isolated in some way from the user interface control.

Instead, consider this rule of thumb: events should always call methods. The methods could be anywhere, but hopefully not in another user interface control. Now if your behavior is centralized somewhere smart, in addition to it being invoked by events, you can also invoke it programmatically. In other words, you're creating an API for your behavior, which means it can be controlled from the outside as well as from within.

For a great example of this, see form Browser.SCX in the Class Browser source code directory. All user interface events like click(), rightclick() call Thisform.somemethod(). The result: everything in the Class Browser that you can do with a mouse and keyboard can also be done in the command window or programmatically.

But still, sometimes it's just too convenient to put a few lines of code in an event for a particular object instance. There are times when a design principle should defer to practical implementation issues - we all have to make those decisions in our apps. But the main point is that this sort of deviation should be done knowingly rather than out of ignorance. That's why something like this is considered a rookie mistake since the reason you see code in events is not because the author had an overriding reason for doing so, but because they simply didn't know any better.

Heuristic: “Do not put implementation details such as common-code private functions into the public interface of a class.”

Applies to VFP, always. 

The urge to refactor common code is too strong to pass up. We see common code, and we immediately make a common method with it. What could be more natural? One common mistake, however is to not make the common method PROTECTED. If it’s not used outside the class, and especially if it is used incidentally within the class, make it PROTECTED!.

Heuristic: “A class should capture one and only one key abstraction.”

Applies to VFP, always.Not always seen in practice.

If you have multiple ideas/abstractions in one class, then that class may be a “god” class. If on the other hand you use many classes to represent a single idea/abstraction, then what you have, in essence, is functions masquerading as classes.

According to Riel. one indicator of good class design is most methods should use most of the properties most of the time.If on the other hand you have some methods using some properties, and other methods using other properties, then the class is a candidate for splitting since possibly two different abstractions are being represented, or possibly what you have is a “god” class.

Heuristic: “Keep related properties and behavior in one place.”

Applies to VFP, always.  Rarely seen in practice.

Violating this heuristic means that to implement some system requirement, you’ll need to make adjustments in two or more places. If the properties and behavior (methods) are consolidated then that centralizes the changes.

This is good because it provides intuitive collection of operations, and it drastically reduces the number of arguments in some messages.

If your classes have many Get/Set accessor methods, then this is a sign that the heuristic is being violated. It means that the class doesn’t do much, and that other classes are doing the interesting stuff with the properties from Get/Set operations.

Here is an example you might be familiar with – it comes from Grady Booch’s seminal book Object-Oriented Analysis And Design With Applications, Addison Wesley, Reading, MA, ISBN 0-8053-5340-2. Assume you have a room that needs heating.  You could design it like this:

Figure 1 Booch's original implementation.

Here are two alternate and possibly improved version of the room heating example, courtesy of Arthur Rie

Figure 2 Riel's alternatives.

Note how clean the interfaces are when all the behavior and data is kept in the same place. In the alternatives proposed by Riel, rooms either know if they need heat, or they know how to ask for heat, and in either case the data about temperature, desired temperature, and occupancy is kept within each room.

Heuristic: “Be sure the abstractions that you model are classes and not simply the roles objects play.”

Applies to VFP, always.Rarely seen in practice.

The distinction between classes and roles objects play is situation dependent. Is Mother or Father a class, or are they roles that certain Person objects play? The answer depends on the domain we're modeling. If we need a Go_Into_Labor() method, then that indicates that Mother and Father should be separate classes. But if the greater need is for, say Change_Diapers(), then this argues for Person objects to which we can flexibly attach roles.

Inheritance Heuristics

Heuristic: “Respect the ‘is-a’ rule of inheritance.”

Applies to VFP, always.

Do not make class B inherit from class A unless you can somehow make the argument that one can view every instance of B also as an instance of A.This possibly goes without saying, but it’s helpful to be reminded of it.

Heuristic: “Program to an interface, not an implementation”

Applies to VFP, always.Rarely seen in practice.

This is possibly the most fundamental principle of object-oriented programming. Inheritance a mechanism for extending functionality by reusing functionality in parent classes. It lets you define a new kind of object rapidly in terms of an old one.

But reusing implementations is only half the story. The other half, some say the better half, is polymorphism. Inheritance's ability to define families of objects with identical interfaces (usually by inheriting from an abstract class) is also important.

There are two benefits to manipulating objects solely in terms of the interface defined by abstract classes:

·         Clients remain unaware of the specific types of objects they use, as long as the objects adhere to the interface that clients expect.

·         Clients remain unaware of the classes that implement these objects. Clients only know about the abstract classes that define the interface.

This so greatly reduces implementation dependencies between subsystems that it leads to this heuristic. Forget about implementation details, and commit only to an interface defined by an abstract class.

Note that creating an object by instantiating a class name in a ‘hard-coded’ way commits you to a particular implementation. Better to use a factory to abstract the process of object creation, so you get different ways to associate an interface with its implementation transparently at instantiation. Using creational design patterns is one sign that your code is written in terms of interfaces, not implementations.

Applies to VFP, always. 

This heuristic maximizes the number of derived classes that take advantage of a common abstraction. This makes for a minimum of code duplication and a maximization of commonality among classes. Pushing material “up” the class hierarchy also helps avoid the painful distribution of properties and behavior throughout the hierarchy. Where’s the code? Answer: hopefully up, along with the rest of the code that the code we seek needs to function.

Applies to VFP, always. 

This is fundamentally easy to understand, and is the basis for much of the refactoring that happens when developing class libraries.So doing this comes quite naturally to most OO developers.

If subclasses are developed independently, or combined through refactoring, you often find that they have duplicate features. The trick is to recognize this when the duplicate features happen to have different names (as often happens).

Be careful when promoting methods to a super class. Other dependent code and properties should be promoted also, or at minimum abstractly defined in the superclass.

Making this heuristic work is sometimes known in refactoring parlance as “Pull-Up Field” and “Pull-Up Method”

Applies to VFP, always.Rarely seen in practice.

Let’s say we have a number of classes where we need to use an abstraction called “telephone number(s)”. People have one or more telephone numbers, businesses have one or more telephone numbers, and so on. One way to program this to place telephone number properties in each class that requires them.

This heuristic, however, suggests that the telephone numbers could be a single abstraction, a class, an instance of which is attached to the person and business objects. This makes the telephone number abstraction more centralized and readily reusable. For example it’s easy then to attach phone numbers to departments, or products, or (hey, someday a fax number) to a vehicle, or your coffee maker at home, and whatever else is (or may) be associated with a phone line in the future.

Applies to VFP, always.

It’s generally a mistake to subclass in order to create an instance. For example, FortyNiners and Dolphins are not subclasses of class FootballTeam, they are instances of class FootballTeam. If these teams are in fact fundamentally different, then these differences can usually be handled polymorphically. Thus each team might have a stadium reference, a management behavior reference, players and coaches collections, so on.

Attaching properties and behaviors dynamically is usually far more flexible than trying to create and maintain a property and behavior-based hierarchy.

Applies to VFP, always.

Optional components of a class are often incorrectly modeled with inheritance. It’s easy to do.Consider a house that may have a variety of systems (heating, cooling, electric, and so on). These systems are optional components of the house. Using an inheritance model, we easily get an exponential explosion of classes as shown in the following figure.

Figure 3 An example where containment is better than a class explosion.

The better solution is to use containment, as shown in the second part of the figure above, and hold optional references to objects that provide these services.

Relationship Heuristics

Applies to VFP, always.

The great thing about containment relationships is the responsibility for object lifetime is completely unambiguous. When the container dies, the contained objects die. Or do they?

VFP5 and 6 are pretty good at releasing containers in spite of any rogue references that may exist to the container and to its contained objects, though some problems remain and, quite frankly, the developer is responsible for these problems through lack of information hiding.

Compare this to referencing associations, whereby setting a property to .NULL. may (or may not) cause an object to go out of scope, and where a rogue reference may prevent the object from releasing properly.

Object lifetime issues aside, containership is somewhat more natural in Visual FoxPro since it can be created and maintained with visual tools as well as code, whereas reference associations must be engineered in code only.

Applies to VFP, always.

The greater the number of collaborations required of a class, the less reusable it is. This heuristic greatly promotes reusability by minimizing the number of collaborations. 

Figure 4. Collaborations minimized.

Applies to VFP, always.

It follows that once the number of collaborations is controlled, then it makes sense to minimize the amount of collaboration between classes. Note the sequence in that last sentence: the amount of coupling is a small problem compared with the complexity of a new collaboration. Thus focus first and foremost on minimizing the number ofinter-object collaborations, then minimize the amount of inter-object collaboration that remains.

Many reasons for this, in particular if you ascribe to data hiding, then container classes will be hiding their objects from the outside, and thus must forward all messages inwards as appropriate since direct addressing is not possible.

Does not apply to VFP, mostly.

This generally accepted heuristic definitely does NOT apply in Visual FoxPro. This heuristic implies that contained objects should not be sending messages to their container. This is a bogus heuristic as far as VFP is concerned since all objects have implicit This.parent, ThisForm and ThisFormSet. Thus we have implicit inside-out messaging with no coupling costs whatsoever.

The implications of this automatic implicit referencing are profound, and this affects the design of most if not all frameworks and projects in my experience. The biggest impact of Visual FoxPro’s implicit inside-out containership referencing is the ease of creating a containership-based mediator, wherein objects can notify the container of events and changes, and the mediator contains a centralized location for code for the whole contained society of objects.

Applies to VFP, always.

Objects share lexical scope if they are contained within the same containing class. A “uses relationship” exists between two objects when they access each other’s interfaces directly. Whenever objects in the same container have permanent references to each other we have a problem: ambiguous ownership. These stray pointers may cause difficulties in releasing the container. We also have a maintenance and reusability problem since sibling objects will have dependencies among themselves.

Better, if messages must be sent among sibling objects, to use VFP’s native implicit container class referencing. Thus if an InteractiveChange() must affect some other control, message the container about the change, and let the container propagate the messages for updating other controls.

Topology Heuristics

Applies to VFP, always. Rarely observed in practice.

I’ve merged two of Riel’s heuristics into one here. God classes have, by definition, a lot of built-in behavior that is directed towards other objects. This implies that the dependent classes rely on the god class for direction and behavior. Thus the dependent classes usually are light on behavior, heavy on properties.

Behavior-laden  classes are difficult to quasi-impossible to reuse, and that 'dumb' behavior-less classes can also be difficult to reuse, since they rely on those not-so-reusable god classes. The trick is to find the right balance of properties and behavior. Given a class-design continuum like this:

Figure 5 Distributing data and behavior among is among the chief difficulties of OO design.

The art of class design is finding the right balance point for classes (and hence the flavor of the whole system), and this heuristic says the right balance point between behavior and properties is probably not near either extreme end.

Applies to VFP, always. Rarely seen on practice.

This is a sign that properties and behavior are not being kept in the same place. The relevant question is: Why do clients need to know these properties, and why is the class not providing the services itself?

Applies to VFP, always.

This is a fundamental principle of layering, wherein higher layers (in this case the UI) delegate to lower layers (in this case the OO model) but the lower layers don’t notify, if at all possible, the upper layers. The generic CRC diagram for layers is as follows:

 

 

Class: Layer J

 

  Responsibility: Provides services used by Layer J+1 and delegates subtasks to Layer J-1

 

  Collaborator: Layer J-1

 

Note in particular that the collaborator is Layer J-1, not Layer J+1. If possible.

Applies to VFP, always.  Rarely seen in practice.

Be especially suspicious of classes that have just one piece of meaningful behavior (Not counting accessor methods).Ask yourself if this single bit of behavior, masquerading as a class, could not be made part of another class, or part of a yet undiscovered class in your system. Ask yourself whether this one-behavior class is really capturing a key abstraction, or it is simply a wayward method that belongs elsewhere. A typical example of a single-operation class that we see a lot in VFP are classes with an Execute() method, and little else. It may be legitimate, but the question is still worth asking.

Applies to VFP, always.

Here is a riddle posed by Meiler Page-Jones from the OOPSLA ’87 conference: On an object-oriented farm is a cow with some object-oriented milk. Should the object-oriented cow send the object-oriented mild the uncow yourself message, or should the object-oriented milk send the object-oriented cow the unmilk yourself message?

There is, of course, a key element missing from this riddle: the object-oriented farmer, which is an example of an agent class.Agents are sometimes necessary, but often agents classes are spurious and complicate the model.

When an agent classes appears necessary, beware that it isn’t too necessary. At the limit, agent classes can easily be De Facto god classes -- composed mostly of behavior and little (if any) data.

On the other hand, agent classes may do nothing but forward messages between objects. In our simplified object-oriented farm, the object-oriented farmer serves as a link between the object-oriented cow and the object-oriented milk, passing messages between the two. If we subsequently find that the farmer does little else than bridge the milk from the cows, we might be able to simplify the object model by minimizing interactions between the object-oriented farmer, the object-oriented caw, and its object-oriented milk, something that, incidentally, seems to be happening on modern dairy farms J.

Riel’s Heuristics Summary

The following heuristics are taken from Object-Oriented Design Heuristics by Arthur J. Riel.


2.1: All data should be hidden within its class.

2.2: Users of a class must be dependent on its public interface, but a class should not be dependent on its users.

2.3: Minimize the number of messages in the protocol of a class.

2.4: Implement a minimal public interface which all classes understand (e.g. operations such as copy (deep versus shallow), equality testing, pretty printing, parsing from a ASCII description, etc.).

2.5: Do not put implementation details such as common-code private functions into the public interface of a class.

2.6: Do not clutter the public interface of a class with things that users of that class are not able to use or are not interested in using..

2.7: Classes should only exhibit nil or export coupling with other classes, i.e. a class should only use operations in the public interface of another class or have nothing to do with that class.

2.8: A class should capture one and only one key abstraction.

2.9: Keep related data and behavior in one place.

2.10: Spin off non-related information into another class (i.e. non-communicating behavior).

2.11: Be sure the abstraction that you model are classes and not simply the roles objects play.

3.1: Distribute system intelligence horizontally as uniformly as possible, i.e. the top level classes in a design should share the work uniformly.

3.2: Do not create god classes/objects in your system. Be very suspicious of an abstraction whose name contains Driver, Manager, System, or Subsystem.

3.3: Beware of classes that have many accessor methods defined in their public interface, many of them imply that related data and behavior are not being kept in one place.

3.4: Beware of classes which have too much non-communicating behavior, i.e. methods which operate on a proper subset of the data members of a class. God classes often exhibit lots of non-communicating behavior.

3.5: In applications which consist of an object-oriented model interacting with a user interface, the model should never be dependent on the interface. The interface should be dependent on the model.

3.6: Model the real world whenever possible. (This heuristic is often violated for reasons of system intelligence distribution, avoidance of god classes, and the keeping of related data and behavior in one place).

3.7: Eliminate irrelevant classes from your design.

3.8: Eliminate classes that are outside the system.

3.9: Do not turn an operation into a class. Be suspicious of any class whose name is a verb or derived from a verb. Especially those which have only one piece of meaningful behavior (i.e. do not count sets, gets, and prints). Ask if that piece of meaningful behavior needs to be migrated to some existing or undiscovered class.

3.10: Agent classes are often placed in the analysis model of an application. During design time, many agents are found to be irrelevant and should be removed.

4.1: Minimize the number of classes with which another class collaborates.

4.2: Minimize the number of message sends between a class and its collaborator.

4.3: Minimize the amount of collaboration between a class and its collaborator, i.e. the number of different messages sent.

4.4 : Minimize fanout in a class, i.e. the product of the number of messages defined by the class and the messages they send.

4.5: If a class contains objects of another class then the containing class should be sending messages to the contained objects, i.e. the containment relationship should always imply a uses relationship.

4.6: Most of the methods defined on a class should be using most of the data members most of the time.

4.7: Classes should not contain more objects than a developer can fit in his or her short term memory. A favorite value for this number is six.

4.8: Distribute system intelligence vertically down narrow and deep containment hierarchies.

4.9: When implementing semantic constraints, it is best to implement them in terms of the class definition.

4.10: When implementing semantic constraints in the constructor of a class, place the constraint test in the constructor as far down a containment hierarchy as the domain allows.

4.11: The semantic information on which a constraint is based is best placed in a central third-party object when that information is volatile.

4.12: The semantic information on which a constraint is based is best decentralized among the classes involved in the constraint when that information is stable.

4.13: A class must know what it contains, but it should never know who contains it.

4.14: Objects which share lexical scope, i.e. those contained in the same containing class, should not have uses relationships between them.

5.1: Inheritance should only be used to model a specialization hierarchy.

5.2: Derived classes must have knowledge of their base class by definition, but base classes should not know anything about their derived classes.

5.3: All data in a base class should be private, i.e. do not use protected data.

5.4: Theoretically, inheritance hierarchies should be deep, i.e. the deeper the better.

5.5: Pragmatically, inheritance hierarchies should be no deeper than an average person can keep in their short term memory. A popular value for this depth is six.

5.6: All abstract classes must be base classes.

5.7: All base classes should be abstract classes.

5.8: Factor the commonality of data, behavior, and/or interface as high as possible in the inheritance hierarchy.

5.9: If two or more classes only share common data (no common behavior) then that common data should be placed in a class which will be contained by each sharing class.

5.10: If two or more classes have common data and behavior (i.e. methods) then those classes should each inherit from a common base class which captures those data and methods.

5.11: If two or more classes only share common interface (i.e. messages, not methods) then they should inherit from a common base class only if they will be used polymorphically.

5.12: Explicit case analysis on the type of an object is usually an error, the designer should use polymorphism in most of these cases.

5.13: Explicit case analysis on the value of an attribute is often an error. The class should be decomposed into an inheritance hierarchy where each value of the attribute is transformed into a derived class.

5.14: Do not model the dynamic semantics of a class through the use of the inheritance relationship. An attempt to model dynamic semantics with a static semantic relationship will lead to a toggling of types at runtime.

5.15: Do not turn objects of a class into derived classes of the class. Be very suspicious of any derived class for which there is only one instance.

5.16: If you think you need to create new classes at runtime, take a step back and realize that what you are trying to create are objects. Now generalize these objects into a class.

5.17: It should be illegal for a derived class to override a base class method with a NOP method, i.e. a method which does nothing.

5.18: Do not confuse optional containment with the need for inheritance, modelling optional containment with inheritance will lead to a proliferation of classes.

5.19: When building an inheritance hierarchy try to construct reusable frameworks rather than reusable components.

6.1: If you have an example of multiple inheritance in your design, assume you have made a mistake and prove otherwise.

6.2: Whenever there is inheritance in an object-oriented design ask yourself two questions: 1) Am I a special type of the thing I'm inheriting from? and 2) Is the thing I'm inheriting from part of me?

6.3: Whenever you have found a multiple inheritance relationship in a object-oriented design be sure that no base class is actually a derived class of another base class, i.e. accidental multiple inheritance.

7.1: When given a choice in an object-oriented design between a containment relationship and an association relationship, choose the containment relationship.

8.1: Do not use global data or functions to perform bookkeeping information on the objects of a class, class variables or methods should be used instead.

9.1: Object-oriented designers should never allow physical design criteria to corrupt their logical designs. However, very often physical design criteria is used in the decision making process at logical design time.