			  Principles of OOD

(author is Robert Martin, check his page on www://www.oma.com/ )

--------------------------------------------------------------------
   1. Software entities (classes, modules, etc) should be open for
      extension, but closed for modification.  (The open/closed
      principle -- Bertrand Meyer)
--------------------------------------------------------------------

The open/closed principle is the single most important guide for an
object oriented designer.   Designing modules whose behavior can be
modified without making source code modifications to that module is
what yields the benefits of OOD.

Consider the following:

class Shape                 {public: virtual void Draw() const = 0;};
class Square : public Shape {public: virtual void Draw() const;};
class Circle : public Shape {public: virtual void Draw() const;};

typedef Shape *ShapePointer;

void DrawAllShapes(ShapePointer list[], int n)
{
  for (int i=0; i<n; i++)
    list[i]->Draw();
}

Notice that the function 'DrawAllShapes' need not be modified when new
kinds of shapes are added to the Shape hierarchy.  In fact, the
function need not even be recompiled.

Yet, even though I don't make changes to the module, I *can* change
its behavior.  Although the function currently only draws circles and
squares, I can enhance it to draw any other shape at all, so long as
that shape is represented by an object that derives from the class
Shape.

So, I can extend the behavior of the module 'DrawAllShapes', without
modifying it at all.

Now, consider how this would have been done in a language like C.

enum ShapeType {circle, square};
struct Shape  {ShapeType itsType;};
struct Circle {ShapeType itsType; double itsRadius; Point itsCenter;};
struct Square {SahepType itsType; double itsSide; Point itsTopLeft;};

void DrawSquare(struct Square*);
void DrawCircle(struct Circle*);

typedef struct Shape *ShapePointer;

void DrawAllShapes(ShapePointer list[], int n)
{
  int i;
  for (i=0; i<n; i++)
  {
    struct Shape* s = list[i];
    switch (s->itsType)
    {
    case square:
      DrawSquare((struct Square*)s);
    break;

    case circle:
      DrawCircle((struct Circle*)s);
    break;
    }
  }
}

It should be very clear that the 'DrawAllShapes' function, as written
in C, cannot be extended without direct modification.  If I need to
create a new shape, such as a Triangle, I must modify the
'DrawAllShapes' function.

This, by itself, would not be too bad.  However, in a complex
application the switch/case statement above is repeated over and over
again for every kind of operation that can be performed on a shape.
These statements are difficult to find, and it is easy to make
mistakes when modifying them.

Worse, every module that contains such a switch/case statement retains
a dependency upon every possible shape that can be drawn, thus,
whenever one of the shapes is modified in any way, the modules all
need recompilation, and possibly modification.

A major factor in software maintenance is the fact that for every
change that is made, there is a risk that bugs will be introduced.
This wreaks havoc with managers and users because old features that
used to work just fine will sometimes break when a new, and apparently
unrelated, feature is added.

However, when the majority of modules in an application conform to the
open/closed principle, then new features can be added to the
application by *adding new code* rather than by *changing working
code*.  Thus, the working code is not exposed to breakage.  This
creates a significant amount of isolation between features, and allows
for much easier maintenance.

--------------------------------------------------------------------
   2. Derived classes must be usable through the base class interface
      without the need for the user to know the difference.  (The
      Liskov Substitution Principle)
--------------------------------------------------------------------

This rule is a logical extension of the open/closed principle.
Consider function F that uses type T.  Given S a subtype of T, F
should be able to use objects of type S without knowing it.  In fact,
any subtype of T should be substitutable as an argument of F.  If this
were not true, then F would have to have tests to determine which of
the various subtypes it was using.  And this breaks the open/closed
principle

For example:  Consider the problem of the Square and the Rectangle.
Mathematically a Square *is* a Rectangle.  This tempts us to invoke
inheritance between them:

        class Rectangle
        {
          public:
            Rectangle(const Point& tl, double h, double w)
            : itsTopLeftPoint(tl)
            , itsHeight(h)
            , itsWidth(w)
            {}

            virtual void SetHeight(double h) {itsHeight=h;}
            virtual void SetWidth(double w)  {itsWidth=w;}

            virtual double GetWidth()  const {return itsWidth;}
            virtual double GetHeight() const {return itsHeight;}

          private:
            Point itsTopLeftPoint;
            double itsHeight;
            double itsWidth;
        };

        class Square : public Rectangle
        {
          public:
            Square(const Point& tl, double s)
            : Rectangle(tl, s, s)
            {}

            virtual void SetWidth(double w)
            {
              Rectangle::SetWidth(w);
              Rectangle::SetHeight(w);
            }

            virtual void SetHeight(double h)
            {
              Rectangle::SetWidth(h);
              Rectangle::SetHeight(h);
            }
        };

In the example above, Square inherits from Rectangle, but the
functions of Square are overridden to enforce that the height and
width of the Rectangle are always the same.

Now, consider a function which takes a Rectangle as an argument, and
adjusts its width so that it has an aspect ratio of 1/2.  i.e. the
width is half the height.

        void HalfAspect(Rectangle& r)
        {
           r.SetWidth(r.GetHeight()/2);
        }

This function fails, rather dismally, if a Square is passed into it as
follows:

        Square s;
        HalfAspect(s); // this just shrinks both sides by 2

Since a user of Rectangle can malfunction when passed a Square, Square
is not substitutable for Rectangle.  To make a general fix, we would
have to add code to HalfAspect to test the incoming Rectangle to see
if it was a Square:

        void HalfAspect(Rectangle& r) throw(BadType)
        {
           if (typeid(r) == typeid(Square))
               throw BadType;

           r.SetWidth(r.GetHeight()/2);
        }

And this creates a dependency upon Square.  Moreover, every time a new
unsubstitutable derivative of Rectangle is created, a new test must be
added to all functions that would malfunction.   This violates the
open/closed principle.

--------------------------------------------------------------------
   3. Details should depend upon abstractions.  Abstractions should
      not depend upon details.  (Principle of Dependency Inversion)
--------------------------------------------------------------------

This is the direction that all dependencies should take in an object
oriented program.  All high level functions and data structures should
be utterly independent of low level functions and data structures.

Consider a program that controls a home furnace.

    Furnace()
    {
        while(;;)
        {
            while (ReadTemp() > theMinTemp)
                wait(1);

            StartFurnace();

            while (ReadTemp() < theMaxTemp)
                wait(1);

            StopFurnace();
        }
    }

This is clearly a simple minded algorithm, but it will serve to
demonstrate our point.  Notice that this algorithm will apply to any
furnace at all.  Thus this high level algorithm is an abstraction, it
is independent of the type of furnace we have.

However, most of the time, programmers will take a high level policy
and code it in terms of the low level details.

    #define THERMOMETER 0x86
    #define FURNACE     0x87
    #define FURNACE_ON  0x01
    #define FURNACE_OFF 0x00

    Furnace()
    {
        while(;;)
        {
            while (in(THERMOMETER) > theMinTemp)
                wait(1);

            out(FURNACE, FURNACE_ON);

            while (in(THERMOMETER) < theMaxTemp)
                wait(1);

            out(FURNACE, FURNACE_OFF);
        }
    }

It should be pretty clear that this high level function is polluted by
the details of the actual furnace controller.  In fact, the
abstraction depends upon the details.

It should also be clear that this function cannot be reused with a
different kind of furnace.  When abstractions depend upon details, the
abstractions cannot be reused.

We might think that we can improve this by employing structured design
as follows:

    #include "furnace.h"

    Furnace()
    {
        while(;;)
        {
            while (ReadTemp() > theMinTemp)
                wait(1);

            StartFurnace();

            while (ReadTemp() < theMaxTemp)
                wait(1);

            StopFurnace();
        }
    }

    --------------furnace.h------------

    double ReadTemp();
    void StartFurnace();
    void StopFurnace();

    -----------------furnace.c-------------
    #include "furnace.h"
    #define THERMOMETER 0x86
    #define FURNACE     0x87
    #define FURNACE_ON  0x01
    #define FURNACE_OFF 0x00

    double ReadTemp()     {return in(THERMOMETER);}
    void   StartFurnace() {out(FURNACE, FURNACE_ON);}
    void   StopFurnace()  {out(FURNACE, FURNACE_OFF);}

However, while this does break the direct dependency of the Furnace
function upon the details, there is still a one to one binding from
the name 'StartFurnace' to the function implemented in "furnace.h".
Thus, although we can some reusability by recoding "furnace.c", we
cannot gain reusability in the same program or application.

However, consider the following object oriented design.

    -----------furnace.h-------------

    class Furnace
    {
      public:
        virtual void Start() = 0;
        virtual void Stop() = 0;
    };

    ---------thermometer.h----------

    class Thermometer
    {
      public:
        virtual double Read() = 0;
    };

    ------------ regulator.h ------------
    class Furnace;
    class Thermometer;

    class Regulator
    {
      public:
        Regulator(Furnace& f, Thermometer& t);
        void Regulate(double minTemp, double maxTemp);
      private:
        Furnace& itsFurnace;
        Thermometer& itsThermometer;
    };

    ---------------- regulator.cc ------------
    #include "regulator.h"
    #include "furnace.h"
    #include "thermometer.h"

    Regulator::Regulator(Furnace& f, Thermometer& t)
    : itsFurnace(f)
    , itsThermometer(t)
    {}

    void Regulator::Regulate(double minTemp, double maxTemp)
    {
        while(;;)
        {
            while (itsThermometer.Read() > theMinTemp)
                wait(1);

            itsFurnace.Start();

            while (itsThermometer.Read() < theMaxTemp)
                wait(1);

            itsFurnace.Stop();
        }
    }

    -------------------detailedFurnace.h---------------------
    #include "furnace.h"
    class DetailedFurnace : public Furnace
    {
      public:
        virtual void Stop();
        virtual void Start();
    };

    -------------------detailedFurnace.cc-------------------
    #define FURNACE     0x87
    #define FURNACE_ON  0x01
    #define FURNACE_OFF 0x00

    void   DetailedFurnace::Start() {out(FURNACE, FURNACE_ON);}
    void   DetailedFurnace::Stop()  {out(FURNACE, FURNACE_OFF);}

    -----------detailedThermometer.h --------------

    class DetailedThermometer : public Thermometer
    {
      public:
        virtual double Read();
    };

    ------------detailedThermometer.cc-------------

    #define THERMOMETER 0x86

    double DetailedThermometer::Read() {return in(THERMOMETER);}
    -------------------------------------------------------------

Notice that now the high level algorithm depends only upon abstract
classes.  Notice also that the details depend only upon the
abstractions.  Notice that the Regulator class can be reused with any
derivative of a Furnace and a Thermometer.  Morever, any application
can have many instance of Regulators, Thermometers, and Furnaces.
Thus, we could reuse this algorithm in, say, an application that
controls the reaction temperature of many different vessels....

Notice also that there is much more code in the OO version.  It takes
code to invert dependencies.  This is the price we pay for the reusability
and maintainability that dependency inversion affords.

--------------------------------------------------------------------
   4. The granule of reuse is the same as the granule of release.
      Only components that are released through a tracking system can
      be effectively reused.
--------------------------------------------------------------------

Reuse does not come automatically.  One cannot simply write a class
and claim that it is reusable.  First, a reusable class probably has
several classes that it collaborates with.  Thus the class is not
reusable in isolation, it must be reused in conjunction with other
classes.  Second, reusers do not want the classes that they are
reusing to change out from under them.  So they will require some kind
of release tracking system to be put into place.

It should be stated at this point that "code copying" is an inferior
form of code reuse.  Code copying puts the burden of maintenance upon
the reuser, not upon the author.  In such cases, the only thing that
is being reused is the initial design of the code.  Yet we all know
that initial design is worth a small percentage of the total effort
through the lifecycle of a project.  Thus we want reusable code to be
maintained by the author, or by a common maintainer.  We want all the
reusers to benefit from the single source of maintenance.

In order to achieve reuse of this kind, the reusable software must be
put into a form that the reusers can manage.  Specifically, this means
that it must be segregated into "chunks" which are relatively
independent of each other, and given version numbers so that the
reusers can track which version of a chunk they are currently using.

The chunks must be small enough so that reusers can pick and choose
the software that they want to reuse.  Reuse is not worth much when it
is an all-or-nothing proposition.

When changes are made to these released "chunks", new version numbers
must be assigned, and notification must be given to all reusers.
Reusers will then decide if they wish to use the new release
immediately, delay such use until their software can be made
compatible, or reject such use and take over maintenance of the
previous version themselves.

In the absence of a release strategy and version numbering scheme,
reuse of commonly maintained software modules is extremely difficult,
if not impossible.  Thus, we say that "the granule of reuse is the
same as the granule of release."

Now this has implications for the way that object oriented programs
should be structured.   One such structuring mechanism is employed by
Booch.  He organizes a group of cohesive classes into an entity called
a class category.  Class categories can be used as the granule of
release, and therefore the granule of reuse.

--------------------------------------------------------------------
   5. Classes within a released component should share common closure.
      That is, if one needs to be changed, they all are likely to need
      to be changed.  What affects one, affects all.  (The principle
      of Common Closure).
--------------------------------------------------------------------

Released components are the targets for dependencies.  In fact, the
reason that they are "released" is to limit the affect of changes upon
the users of the component.  This implies that releases should not
occur often, and that they should occur in isolation where possible.

The worst case scenario is when a change to the system causes
modification of all the released components, such that they all need
to be re-released.  When changes occur, we want those changes to
affect the smallest possible number of released components.

In OOD, the released coponent is the "category".  And the principle we
are discussing is the principle of "Common Closure within Categories."
This priciple is the first and most important of the three rules for
category cohesion.  A category is cohesive, if the classes within it
are all closed to the same kinds of modifications.

The open closed principle states that a class should be open for
extension but closed for modification.  This is an ideal that cannot
be completely achieved.  Regardless of the design of a class, there
will always exist some kind of change that will force the class to be
opened.  However, do not have to protect ourselves from every kind of
modification.  We can anticipate the kinds of modifications that are
likely to be requested, and protect ourselves from those.

Thus, we design our classes to be closed to the most likely kinds of
changes that we can forsee.  This means that the closure of a class is
partial, and specifically targeted at certain kinds of changes.

The principle of common closure states that, given a particular kind
of change, either all of the classes within a category are closed to
it, or they are all open to it.

When this principle can be achieved, changes do not propogate through
the system.  Rather they focus upon one, or a few categories, while
the rest of the categories remain unaffected.  This greatly lessens
the number of categories that are affected by a change, and reduces
the frequency with which categories must be released.

--------------------------------------------------------------------
   6. Classes within a released componen should be reused together.
      That is, it is impossible to separate the components from each
      other in order to reuse less than the total.
--------------------------------------------------------------------

When a user decides to reuse a component, he creates a dependency upon
the whole component.  When a new release of that component is created, the
user must reintroduce that new release into his existing applications.
Therefore, the user will want each component to be as focused as
possible.  If the component contains classes which the user does not
make use of, then the user may be placed in the position of accepting
the burden of a new release which does not affect any of the classes
within the component that he is using.   Thus, comoponents should be
kept relatively narrow.  If the user uses one of the classes in the
component, then it should be extremely likely that he should reuse
all the other classes in the component.

Generally, this kind of focus is enforced by the relationships that
exist between classes that are reused together.  Those relatioships
represent dependencies that force the user to bring along all the
classes within a component.

It is important that dependencies that are associated with reuse be
encapsulated within a single component where possible.  If the reuse
of a particular class within a component forces the reuse of other
components, then the user has a bigger reuse burden.

--------------------------------------------------------------------
   7. The dependency structure for released components must be a DAG.
      There can be no cycles.
--------------------------------------------------------------------

Consider an application that is divided up into many different class
categories.  A given class category can be released, only when it is
compatible with those categories that it depends upon.

This ability to release an application in peices facilitates
development.  If applications cannot be released in pieces, then the
developers interfere with each other as they are making changes to
their code.

The typical horror story is of an engineer who works all day to get
his stuff working.  He goes home a night satisfied.  However upon
returning the next morning, he is dismayed to find that his stuff no
longer works.  The reason:  Sombody stayed later than he did and
changed something that he depended upon.

If this happened only once in a while, it would not be so bad, but in
large projects it can happen on a daily (even hourly) basis.  Thus we
need a way to release the application in pieces so that developers can
choose to depend upon older releases of the categories that they
depend upon.

However, when there are dependency cycles, then this rational scheme
for developing applications breaks down.  All of the categories within
the cycle must be released simultaneously.

Consider the following diagram:

                  +---------------+
                  | Control Panel |
                  +---------------+
                     / 	     \
	   +-----------+     +---------+
           | Transport |     | Revenue |
           +-----------+     +---------+
	    /       \               \
   +----------+ +----------+   +----------+
   | Elevator | | Conveyor |   | Database |
   +----------+ +----------+   +----------+
       	     \ 	  /
	   +-------+
           | Alarm |
	   +-------+

The developers that are working in the 'Transport' category choose to
work with older, more stable, versions of Elevator and Conveyor, which
in turn depend upon an old version of Alarm.  When Transport wants to
make a release, they simply test their category with the appopriate
releases of the dependent categories, and then release it.

However, if Alarm called a function that belonged to 'Control Panel',
thus creating a cyclic dependency, then Transport could not work
without old and stable versions of Elevator and Conveyor, which could
not work without a stable verion of Alarm, which could not work
without a stable version of Control Panel, which needs a stable
version of Transport.  Since we are trying to make changes to
Transport, all of the other categories are suddenly invalidated, and
the entire cycle must be released at the same time.

Thus, cycles in the dependency structure interfere with the ability to
release an application in pieces.  However there is even more harm
that they can do.  Consider again the diagram above.  The developers
working in 'Elevators' want to run some unit tests.  They must link
with Alarm.  However, if we assume that the same cycle exists, (i.e.
Alarm calls a function in Control Panel) then the unit test must link
with Control Panel, which must link with Revenue, which must link with
the Database.  And the database doesn't work.

The developers of 'Elevator' are really angry.  They don't need the
database.  They don't want to link with the database.  The database
code is 14 megabytes and takes 45 minutes to link in; so why do they
have to use it?  The 'Elevator' developers go find the developer in
'Alarm' who created the cyclic dependency upon 'Control Panel' and
take him out back for a little lesson in software engineering.

***  How can the cycle be broken.  Simple.  Whenever there is a cycle
in the dependency graph of class categories, that cycle can be broken
by splitting one of the categories into two.  In this case, we could
split the 'Control Panel' category into two categories as shown below:

                  +---------------+
          --------| Control Panel |
        /         +---------------+
      /              / 	     \
    /      +-----------+     +---------+
  /        | Transport |     | Revenue |
|          +-----------+     +---------+
|           /       \               \
|  +----------+ +----------+   +----------+
|  | Elevator | | Conveyor |   | Database |
|  +----------+ +----------+   +----------+
|      	     \ 	  /
|          +-------+
|          | Alarm |
 \         +-------+
   \   	     /
  +---------------+
  | Control Panel |
  |   Utilities   |
  +---------------+

The new category 'Control Panel Utilities' contains the function that
Alarm wants to call.

--------------------------------------------------------------------
   8. Dependencies between released categories must run in the
      direction of stability.  The dependee must be more stable than
      the depender.
--------------------------------------------------------------------

One could view this as a axiom, rather than a principle, since it
is impossible for a category to be more stable than the categories
that it depends upon.  When a category changes it always affects the
dependent categories (even if for nothing more than a
retest/revalidation).  However the principle is meant as a guide to
designers.  Never cause a category to depend upon less stable
categories.  

What is stability?  The probable change rate.  A category that is
likely to undergo frequent changes is instable.  A category that will
change infrequently, if at all, is stable.

There is an indirect method for measuring stability.  It employs the
axiomatic nature of this principle.  Stability can be measured as a
ratio of the couplings to classes outside the category.

A category which many other categories depend upon is inherently
stable.  The reason is that such a category is difficult to change.
Changing it causes all the dependent categories to change.  

On the other hand, a category which depends on many other categories
is instable, since it must be changed whenever any of the categories
it depends upon change.

A category which has many dependents, but no dependees is ultimately
stable since it has lots of reason not to change and no reason to
change. (This ignores the categories intrinsic need to change based
upon bugs and feature drift). 

A category that depends upon many categories but has no dependents is
ultimately instable since it has no reason not to change, and is
subject to all the changes coming from the categories it depends upon.

So this notion of stability is positional rather than absolute.  It
measures stability in terms of a category's position in the dependency
hierarchy.  It says nothing about the subjective reasons that a
category might need changing, and focuses only upon the objective,
physical reasons that facilitate or constrain changes.

To calculate the Instability of a category (I) count the number of
classes, outside of the category, that depend upon classes within the
category.  Call this number Ca.  Now count the number of classes
outside the category that classes within the category depend upon.
Call this number Ce.  I = Ce / (Ca + Ce).  This metric ranges from 0
to 1, where 0 is ultimately stable, and 1 is ultimately instable. 

In a dependency hierarchy, wherever a category with a low I value
depends upon a category with a high I value, the dependent category
will be subject to the higher rate of change of the category that it
depends upon.  That is, the category with the high I metric acts as a
collector for all the changes below it, and funnels those changes up
to the category with the low I metric. 

Said another way, a low I metric indicates that there are many
relatively many dependents.  We don't want these dependents to be
subject to high rates of change.  Thus, if at all possible, categories
should be arranged such that the categories with high I metrics should
depend upon the categories with low I metrics.

--------------------------------------------------------------------
   9. The more stable a class category is, the more it must
      consist of abstract classes.  A completely stable category
      should consist of nothing but abstract classes.
--------------------------------------------------------------------

The stability being referred to in this principle, is again the I
metric which is based upon "positional" stability.  That is, a module
that is in a highly stable position in the dependency graph should
also be highly abstract.  

The justification for this principle is based upon the idea that
executable code (The implementations of methods) changes more often
than the interfaces between modules.  Therefore interfaces have more
intrinsic stability than executable code.  In C++, it is more likely
that you will change a .cc file than a .h file.

In some languages, the division between implementation and interface
is very clear cut.  But in others (like C++) it is blurred.  The
stability of the .h file is not as great as it could be because
private functions and private variables are likely to need changing as
the implementation evolves.  In such languages, the maximum stability
comes from pure interfaces.  A class that contains pure interfaces is
an abstract class.

Thus, we can measure the abstraction of a category by computing the
ratio of abstract classes to total classes:

      A = (# abstract classes) / (# of classes).

For example, if a class category contains 10 classes, of which 6 are
abstract then A = .6.  A will always range from [0,1].

Principle 9 says that categories that are placed into positions of
stability ought to be abstract.  The reason for this that the higher
the abstraction of the category, the higher its intrinsic stability.
And since intrinsic stability is limited by positional stability,
intrinsically stable modules should also have positional stability.

Consider a category with very low abstraction.  Such a category has
mostly concrete classes in it.  If it is put in a place of stability,
then many other categories will depend upon it.  And thus, it will be
difficult to change its implementation.  On the other hand, consider a
category with very high abstraction.  Such a category consists mostly
of abstract classes.  If we put this category into a position of very
low stability, then very few other categories will depend upon it.
And then, of what use is the abstract interface?

But there is another reason to put abstractions in positions of
stability.  And that reason goes back to principle #1; the open closed
principle.  We want concrete categories to depend upon abstract
categories so that the derivatives of those abstract categories can be
controlled by the concrete categories.  This is reuse.  The concrete
categories can be reused with different derivatives of the abstract
categories.  Also, when the implementations of those derivative
categories change, the abstract category is not affected, and so the
concrete categories that depend upon it are also unaffected.  Thus,
the concrete categories are open to be extended, but do not need to be
modified in order to achieve that extension.

The open closed principle leads us to the realization that we do not
want all of our categories to be stable.  Stability is inflexibility.
A stable category is difficult to change.  And we do not want our
applications to be difficult to change.  But the open closed principle
allows that a module that is highly stable (closed for modification)
can also be open for extension.  Yet this can only happen if the
extension takes place in a derivative of the stable abstraction.
Thus, in the ideal world our models would consist of two kinds of
categories.  Completely abstract and stable categories that are
depended upon by completely concrete and instable categories.

We are now in a position to define the relationship between stability
(I) and abstractness (A).  We can create a graph with A on the
vertical axis and I on the horizontal axis.  If we plot the two "good"
kinds of categories on this graph, we will find the categories that
are maximally stable and abstract at the upper left at (0,1).  The
categories that are maximally instable and concrete are at the lower
right at (1,0).

But not all categories can fall into one of these two positions.
Categories have degrees of abstraction and stability.  For example, it
is very common that one abstract class derives from another abstract
class.  The derivative is an abstraction that has a dependency.  Thus,
though it is maximally abstract, it will not be maximally stable.  Its
dependency will decrease its stability.

Consider a category with A=0 and I=0.  This is a highly stable and
concrete category.  Such a category is not desirable because it is
rigid.  It cannot be extended because it is not abstract.  And it is
very difficult to change because of its stability.

Consider a category with A=1 and I=1.  This category is also
undesirable (perhaps impossible) because it is maximally abstract and
yet has no dependents.  It, too, is rigid because the abstractions are
impossible to extend.

But what about a category with A=.5 and I=.5?  This category is
partially extensible because it is partially abstract.  Moreover, it
is partially stable so that the extensions are not subject to maximal
instability.  Such a category seems "balanced".  Its stability is in
balance with its abstractness.

Consider again the A-I graph (below).  We can draw a line from (0,1)
to (1,0).  This line represents categories whose abstractness is
"balanced" with stability.  Because of its similarity to a graph used
in astronomy, I call this line the "Main Sequence".

               |
               |
	      1= (0,1)
	       |\
	       | \
   Abstractness|  \ The
	       |   \ Main
	       |    \ Sequence
	       |     \
	       |      \
	       |       \  (1,0)
	       +--------:--
	        	1
	          Instability


A category that sits on the main sequence is not "too abstract" for
its stability, nor is "too instable" for its abstractness.  It has the
"right" number of concrete and abstract classes in proportion to its
positional stability.  Clearly, the most desirable positions for a
category to hold are at one of the two endpoints of the main sequence.
However, in my experience only about half the categories in a project
can have such ideal characteristics.  Those other categories have the
best characteristics if they are on or close to the main sequence.

This leads us to another metric.  If it is desirable for categories
to be on or close to the main sequence, we can create a metric which
measures how far away a category is from this ideal.

D : Distance : |(A+I-1)/root2| : The perpendicular distance of a
    category from the main sequence.  This metric ranges from
    [0,~0.707].  (One can normalize this metric to range between [0,1]
    by using the simpler form |(A+I-1)|.  I call this metric D').

Given this metric, a design can be analyzed for its overall
conformance to the main sequence.  The D metric for each category can
be calculated.  Any category that has a D value that is not near zero
can be reexamined and restructured.

Statistical analysis of a design is also possible.  One can calculate
the mean and variance of all the D metrics within a design.  One would
expect a conformant design to have a mean and variance which were
close to zero.  The variance can be used to establish "control limits"
which can identify categories that are "exceptional" in comparison to
all the others.







