Booch Method

Chapter 2. The Object Model


Questions List

1. The Evolution of the Object Model

  1. What are OOP, OOD, and OOA?
  2. How are OOA, OOD, and OOP related?

2. Element of the Object Model

  1. What is the object model?
  2. What is abstraction?
  3. What kinds of abstraction are there?
  4. Examples of abstraction?
  5. What is encapsulation?
  6. Examples of encapsulation?
  7. What is modularity?
  8. How to achieve modularity?
  9. Examples of modularity?
  10. What is hierarchy?
  11. Examples of hierarchy? - Single inheritance
  12. Examples of hierarchy? - Aggregation
  13. What is typing?
  14. What is relation between typing and object modeling?
  15. Examples of strong typing?
  16. What are static and dynamic typing?
  17. Example of dynamic binding?
  18. What is concurrency?
  19. What is persistence?


Note:


1. The Evolution of the Object Model

Q1.1: What are OOP, OOD, and OOA? (Ref: 2.1, p.38)

OOP stands for "Object-Oriented Programming", OOD stands for "Object- Oriented Design", and OOA stands for "Object-Oriented Analysis". The following are Booch's definitions of OOP, OOD, and OOA.

OOP : Object-Oriented Programming
Object-oriented programming is a method of implementation in which programs are organized as cooperative collections of objects, each of which represents an instance of some class, and whose classes are all members of a hierarchy of classes united via inheritance relationship.

There are three important parts to this definition:

  1. uses objects, not algorithms, as its fundamental logical building blocks (the "part of" or "has a" hierarchy),
  2. each object is an instance of some class,
  3. class are related to one another via inheritance (the "is a" hierarchy).

A program may appear to be object-oriented, but if any of these elements is missing, it is not an object-oriented program. Specially, programming without inheritance is distinctly not object-oriented; Booch calls it programming with abstract data types.

OOD : Object-Oriented Design
Object-oriented design is a method of design encompassing the process of object-oriented decomposition and a notation for depicting both logical and physical as well as static and dynamic models of the system under design.

A pictorial description of these models is here.

OOA : Object-Oriented Analysis
Object-oriented analysis is a method of analysis that examines requirements from the perspective of the classes and objects found in the vocabulary of the problem domain.

This emphasizes the building of real-world models, using an object-oriented view of the world.

Q1.2: How are OOA, OOD, and OOP related? (Ref: 2.1, p.39)

The basic relations are as follows:




2. Element of the Object Model

Q2.1: What is the object model? (Ref: 2.2, p.40, Glossary p.516)

The object model is the collection of principles that form the foundation of object-oriented design. It is the conceptual framework of all things object-oriented.

There are four major elements of this model. Without any one of these these elements is not object-oriented.

There are three minor elements of the object model. Each of these elements is a useful, but not essential, part of the object model.

Without this conceptual framework, you may be programming in a language such as Smalltalk, Object Pascal, C++, CLOS, Eiffel, or Ada, but your design is going to smell like a FORTRAN, Pascal, or C application. You will have missed out on or otherwise abused the expressive power of the object-oriented language you are using for implementation. More importantly, you are not likely to have mastered the complexity of the problem at hand.

Q2.2: What is abstraction? (Ref: 2.2, p.41)

Abstraction is one of the fundamental ways that we as humans cope with complexity. Booch defines abstraction as follows:

An abstraction denotes the essential characteristics of an object that distinguish it from all other kinds of objects and thus provide crisply defined conceptual boundaries, relative to the perspective of the viewer.
An abstraction focuses on the outside view of an object, and so serves to separate an object's essential behavior from its implementation.

Deciding upon the right set of abstractions for a give domain is the central problem in object-oriented design.

Q2.3: What kinds of abstraction are there? (Ref: 2.2, p.42)

There is a spectrum of abstraction, from objects which closely model problem domain entities to objects which really have no reason for existence. From the most to the least useful, these kinds of abstractions include the following:

We strive to build entity abstractions, because they directly parallel the vocabulary of given problem domain.

Q2.4: Examples of abstraction? (Ref: 2.2, p.44)

The following examples show how we can concretely express abstractions. How to find the right abstractions for the given problem is another problem, which will be treated in chapter 4.

The problem to illustrate examples
On a hydroponics farm, plants are grown in a nutrient solution, without sand, gravel, or other soils. Maintaining the proper greenhouse environment is a delicate job, and depends upon the kind of plant being grown and its age. One must control diverse factors such as temperature, humidity, light, pH, and nutrient concentrations. On a large farm, it is not unusual to have an automated system that constantly monitors and adjusts these elements. Simply stated, the purpose of an automated gardener is to efficiently carry out, with minimal human invention, growing plans for the healthy production of multiple crops.

One of the key abstractions in this problem is that of sensor. Let's select the temperature sensor. What are the responsibilities of a temperature sensor? Or asked another way, what operations can a client perform upon a temperature sensor? Our design decision is

We might write the following declarations that capture our abstraction of a temperature sensor:
  // Temperature in degrees Fahrenheit
  typedef float Temperature;

  // Number uniquely denoting the location of a sensor
  typedef unsigned int Location;

  class TemperatureSensor{
  public:
     TemperatureSensor(Location);
    ~TemperatureSensor();

     void calibrate(Temperature actualTemperature);
     Temperature currentTemparature() const;

  private:
     ...
  };
The points of the above example are:
  1. The class TemperatureSensor captures the abstraction of a sensor itself; its representation is hidden in the private part of the class.
  2. Central to the idea of an abstraction is the concept of invariance. An invariant is some Boolean condition whose truth must be preserved. For each operation associated with an object, we may define preconditions (invariants assumed by the operation) as well as postconditions (invariants satisfied by the operation). If a pre- postconditions are violated, there is no guaranty of correct behavior of the operation. The invariants associated with the operation currentTemparature are:
Q2.5: What is encapsulation? (Ref: 2.2, p.49)

Abstraction and encapsulation are complementary concepts: abstraction focuses upon the observable behavior of an object, whereas encapsulation focuses upon the implementation that gives rise to this behavior. Encapsulation is most often archived through information hiding, which is the process of hiding all the secrets of an object that do not contribute to its essential characteristics; typically, the structure of an objects is hidden, as well as the implementation of its methods.

Booch defines encapsulations as follows:

Encapsulation is the process of compartmentalizing the elements of an abstraction that constitute its structure and behavior; encapsulation serves to separate the contractual interface of an abstraction and its implementation.
Q2.6: Examples of encapsulation? (Ref: 2.2, p.50)

Let's consider this problem again. Another key abstraction in this problem domains is that of a heater. What operations can a client perform upon a heater? Our design decision here is at a fairly low level:

We might write the following declarations that capture our abstraction of a heater:

  // Boolean type
  enum Boolean {FLASE, TURE};

  class Heater{
  public:
     Heater(Location)
    ~Heater();

     void turnOn();
     void turnOff();

     Boolean isOn() const;

  protected:
     const Location repLocation;
     Boolean repIsOn;
     SerialPort* rePort;
  };
In the above example, class SerialPort captures the abstraction of a serial port which connected to a heater. Messages to control a heater are sent through this port.

The points of the above example are:

  1. The class Heater captures the abstraction of a heater itself; its representation is hidden in the private part of the class.
  2. The three attributes repLocation, repIsOn, and repPort form the encapsulated representation of this class, i.e. compiling client code that tries to access these members objects directly will result in a semantic error.
  3. Suppose that for whatever reason our system engineers choose to use memory-mapped I/O instead of serial communication lines. We would not need to change the interface of the class Heater; we would only need to modify its implementation.
Q2.7: What is modularity? (Ref: 2.2, p.54)

In the object languages like Object Pascal, C++, etc., classes and objects form the logical structure of a system; we place these abstraction (classes and objects) in modules to produce the system's physical architecture. Especially for larger applications, in which we may have many hundreds of classes, the use of modules is essential to help manage complexity.

Modules serve as the physical containers in which we declare the classes and objects of our logical design.

In C++ for example, modules are nothing more than separately compiled files.

Booch defines modularity as follows:

Modularity is the property of a system that has been decomposed into a set of cohesive and loosely coupled modules.
Q2.8: How to achieve modularity? (Ref: 2.2, p.56)

There are several useful technical as well as nontechnical guidelines that can help us achieve an intelligent modularization of classes and objects.

The identification of classes and objects is part of the logical design of the system, but the identification of modules is part of the system's physical design. One cannot make all the logical design decisions before making all the physical ones, or vice versa; rather these design decisions happen iteratively.

Q2.9: Examples of modularity? (Ref: 2.2, p.58)

Let's consider this problem again. Suppose there are many kinds of growing plans (for example, fruit growing, grain growing, etc.), then we might create a module whose purpose is to collect all of classes associated with these.

We might write the header file for this module as:

  // gplan.h

  #ifndef _GPLAN_H
  #define _GPLAN_H

  #include "gtypes.h"
  #include "execpt.h"
  #include "actaions.h"

  class GrowingPlan{
  ...
  };
  class FruitGrowingPlan{
  ...
  };
  class GrainGrowingPlan{
  ...
  };
Q2.10: What is hierarchy? (Ref: 2.2, p.59)

Encapsulation helps manage the complexity by hiding the inside view of abstractions. Modularity helps also, by giving us a way to cluster logically related abstractions. Still, this is not enough. A set of abstractions often forms a hierarchy, and by identifying these hierarchies in our design, we greatly simplify our understanding of the problem.

Booch defines hierarchy as follows:

Hierarchy is a ranking or ordering of abstractions.

The two most important hierarchies in a complex systems are its

  1. class structure (the "is a" hierarchy),
  2. object structure (the "part of" hierarchy).
Q2.11: Examples of hierarchy? - Single inheritance (Ref: 2.2, p.60)

Inheritance is the most important "is a" hierarchy, it is an essential element of object-oriented systems. Basically, inheritance defines a relationship among classes, wherein one class shares the structure or behavior defined in one (single inheritance) or more (multiple inheritance) classes.

Let's consider this problem again. Suppose the growing plan for all fruits is generally the same, but is quite different from the plan for all vegetables, or for all floral crops. Because of this clustering of abstractions, it is reasonable to define a standard fruit-growing plan that encapsulates the specialized behavior common to all fruits.

  // Yield type
  typedef unsigned int Yield;

  class FruitGrowingPlan : public GrowingPlan {
  public:
     FruitGrowingPlan(char* name);
     virtual ~FruitGrowingPlan();

     virtual void establish(Day, Hour, Condition&);
     void scheduleHarvest(Day, Hour);

     Boolean isHarvested() const;
     unsigned dayUntilHarvest() const;
     Yield estimatedYield() const;

  protected:
     Bollean repHarvested;
     Yield repYield;
  };

This class declaration captures the following design decisions:

  1. FruitGrowingPlan "is a" kind of GrowingPlan,
  2. the member objects repHarvested and repYield are added,
  3. added four new member functions,
  4. overriding of the superclass operation establish.

We often speak of inheritance as being a generalization/specialization hierarchy.

Q2.12: Examples of hierarchy? - Aggregation (Ref: 2.2, p.64)

Whereas "is a" hierarchies denote generalization/specialization relationships, "part of" hierarchies describe aggregation relationship. Aggregation is not a concept unique to object-oriented programming languages. Indeed, any language that supports record-like structures supports aggregation. However, the combination of inheritance with aggregation is powerful: aggregation permits the physical grouping of logically related structure, and inheritance allows these common groups to be easily reused among different abstractions.

  class Garden{
  public:
     Garden();
     virtual ~Garden();
     ....
  protected:
     Plant* repPlants[100];
    GrowingPlan repPlan;
  };
This class declaration captures the following design decisions:
  1. Garden consists of a collection of Plant and GrowingPlan.
  2. Our abstraction of a garden permits different plants to be raised in a garden over time, but replacing a plant does not change the identity of the garden as a whole. This design decision is captured by including pointers to Plant objects rather than value.
  3. In contrast, we have decided that a GrowingPlan object is intrinsically associated with a Garden object, and does not exist independently of the garden. For this reason, we use a value of GrowingPlan.

Q2.13: What is typing? (Ref: 2.2, p.65)

For our purpose,we will use he terms type and class interchangeably. Although the concepts of a type and a class are similar, we include typing as a separate element of the object model because the concept of a type places a very different emphasis upon the meaning of abstraction.

Booch defines typing as follows:

Typing is the enforcement of the class of an object, such that objects of different types may not be interchanged, or at the most, they may be interchanged only in very restricted ways.

A given programming language may be strongly typed, weakly typed, or even untyped, yet still be called object-oriented.

strongly typed
In this language, type conformance is strictly enforced: operations cannot be called upon as object unless the exact signature of that operation is defined in the object's class or superclass. Violation of type conformance can be detected at the time of compilation.
Weakly typed or untyped
A client can send any message to any class. Violations of type conformance may not be known until execution, and usually manifest themselves as execution errors.
Languages such as C++ are hybrid: they have tendencies toward strong typing, but it is possible to ignore or suppress the typing rules.

Q2.14: What is relation between typing and object modeling? (Ref: 2.2, p.69)

Strong typing lets us use our programming language to enforce certain design decisions, and so is particularly relevant as the complexity of our system grows.

Q2.15: Examples of strong typing? (Ref: 2.2, p.69)

Consider the abstraction of the various kinds of storage tanks that might exist in a greenhouse. We are likely to have storage tanks for water as well as various nutrients; although one holds a liquid and other a solid, these abstractions are sufficiently similar to warrant a hierarchy of classes.

  // Number denoting level from 0 to 100%
  typedef float Level;

  class StorageTank{
  public:
     StoageTank();
     virtual ~StorageTank();

     virtual void fill();
     virtual void startDraining();
     virtual void stopDraining();

     Boolean isEmpty() const;
     Level level() const;

  protected:
     ...
  };

  class WaterTank : public StorageTank{
  public:
     WaterTank();
     virtual ~WaterTank();

     virtual void fill();
     virtual void startDraining();
     virtual void stopDraining();
     void startHeating();
     void stopHeating();

     Temperature currentTemparature() const;

  protected:
     ....
  };

  class NutrientTank : public StorageTank{
  public:
     NutrientTank();
     virtual ~NutrientTank();

     virtual void startDraining();
     virtual void stopDraining();

  protected:
     ...
  };

Then suppose we have the following declarations:
  StorageTank s1, s2;
  WaterTank w;
  NutrientTank n;
The strong typing nature of C++ makes the following three statements legal,
  Level lv = s1.level();
  w.startDraining();
  n.stopDraining();
However, the following two statement illegal:
  s1.startHeating();
  n.stopHeating();

Q2.16: What are static and dynamic typing? (Ref: 2.2, p.71)

Although strong typing refers to type consistency, static/dynamic typing refers to the time when names are bounded to types. These typings are also called:

static typing : static binding or early binding
dynamic typing : dynamic binding or late binding

Static binding means that the types of all variablesand expressions are fixed at the time of compilation. Dynamic binding means that the types of all variables and expressions are not known until runtime.

Dynamic binding is the base of feature called polymorphism, which means that a single name (such as a variable declaration) may denote objects of many different classes that are related b some common superclass.

Polymorphism exists when the feature of inheritance and dynamic binding interact. It is perhaps the most powerful feature of object-oriented programming languages next to their support of abstraction.

Q2.17: Example of dynamic binding? (Ref: 2.2, p.71)

Let's consider the example of strong typing. Consider the following nonmember function. Here nonmember function means that it is a function not directly associated with a class. Nonmember functions are also called free subprograms.

  void balanceLevels(StorageTank& s1, StorageTank& s2) {
  ...
  if (s1.level() > s2.level())
     s2.fill();
  ...
  }

Because the member function
level
is defined in the base class StorageTank as a non-virtual function, the call to
level
is statically bound at the compilation time. On the other hand, the call to
fill
is dynamically bound, because this operation is declared in the base class and then redefined only in the subclass WaterTank.

Q2.18: What is concurrency? (Ref: 2.2, p.72)

Concurrency means multiples processes running simultaneously in a system. Many contemporary operating systems now provide direct support for concurrency, and so there is greater opportunity and demand for concurrency within object-oriented system.

Whereas object-oriented programming focuses upon data abstraction, encapsulation, and inheritance, concurrency focuses upon process abstraction and synchronization. The object is a concept that unifies these two different viewpoints. Each object drawn from an abstraction of the real world may represent a separate thread of control (a process abstraction). such objects are called active. In a system based on an object-oriented design, we can conceptualize the world as consisting of a set of cooperative objects, some of which are active and thus serve as centers of independent activity. Concurrency allows different objects to act at the same time.

Booch defines concurrency as follows:

Concurrency is the property that distinguishes an active object from one that is not active.

Q2.19: What is persistence? (Ref: 2.2, p.75)

Booch defines persistence as follows:

Persistence is the property of an object through which its existence transcends time (i.e. the object continues to exist after its creator ceases to exit) and/or space (i.e. the object's location moves from the address space in which it was created).
The spectrum of object persistence encompasses the following: Traditional programming languages usually address only the first three kinds of object persistence; persistence of the last three kinds is typically the domain of database technology. Introducing the concept of persistence to the object model gives rise to object-oriented database.