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.
There are three important parts to this definition:
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.
A pictorial description of these models is here.
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:
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:
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
// 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:
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:
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.
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
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:
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:
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.
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
levelis defined in the base class StorageTank as a non-virtual function, the call to
levelis statically bound at the compilation time. On the other hand, the call to
fillis 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: