State of the Art
- Information Hiding
- leads to minimal interfaces and
a distinction of user and implementor view
- Abstract Datentyps (Encapsulation)
Data and operations are packed tobether.
- Way for Reuse
-
Let us do an example. Imagine you are writing software for banks and bookkeepers
and want to start developing an "data type library". One very basic data type is an account.
What do we expect an account has to do?
Used Types:
Account, owner (person/company), Deposit ( money or papers),
Character, Bool, ... .
What has to be done by the account ?
- Create the account
-
An account will be created for the owner with an initial deposit.
- Delete the account
-
After deleting the account the deposit will be payed to the owner.
- Add
-
The amount added to the account can later be subtracted if no other bookings
(subtractions) are done inbetween.
- Subtract
-
The amount will be given to the owner, if its available on the account.
- Print log
-
The internal data of the account will be displaed in a manner readable by a
human being (character string).
Types = (Account,Owner,Deposit (currency),
Character, Bool, ...)
Operations
Create: Owner x Deposit -> Account,
Remove: Account -> Deposit,
Add : Account X Deposit -> Account,
Subtract: Account x Deposit -> Account,
Log : Account -> Character,
+ : Account x Account -> Deposit,
= : Account x Account -> Bool,
....
Equations (Axiome)
Subtract ( Add(account1,b),b)=account1
Abover the View of the user of an account is described.
- no data defined
- no algorithms for operations
- no implementation structures
Another easi example is the stack:
Types = (entry,stack,bool) or
(stack(entry),bool)
Operations
PUSH: entry x stack -> stack,
POP: stack -> entry x stack,
EMPTY: stack-> bool,
lam : -> stack ( NEW: -> stack ?)
lam = empty stack
FULL : stack -> bool ?
Examples (Axiome)
POP(PUSH(e,s)) = e,s
EMPTY(lam) = true
EMPTY(PUSH( , ))= false
POP(lam) = lam oder error ?
Definition used by B. Meyer:
POP: stack -> stack
TOP: stack -> entry
POP(PUSH(e,s)) = s
TOP(PUSH(e,s)) = e
The target is to get simple interfaces and general useable data types.
The principles we used here already are:
- Encapsulation:
-
Internal data of a data type is only readable or changeable via operations.
- Information hiding:
-
Interfaces of operations should not depend on implementation. This is a very hard
condition not always to archieve. But at least on "abstract" level this should be
realized. We come again to this point when we speak about class relations.
Discussion
Is it possible to implement abstract data types in classical languages ?
There can be done a lot with big discipline of the programmers. But this is of no more use
if software has a long lifecycle. The programmers change often and a lot of changes
are done. Example where it was done: Building Blocks at IBM Lab Boeblingen.
But there are limits:
- No possibility to protect internal data. A DSECT the user knows he can
access.
- Storage handling.
ABSTRACT DATA TYPE = EXTERNAL DESCRIPTION OF AN OBJECT
OBJECT = COMPLETE IMPLEMENTED ABSTRACT DATA TYPE READY TO RECEIVE
MESSAGES.
An object has two sides:
- consumer side (user)
- implementor side
How to use an Object ?
Every object has methods to be executed when receeiving a message.
METHOD = PROGRAM executing an OPERATION of an abstract Data type
MESSAGE = CALL of a METHOD.
Methods and messages in different languages:
- Smalltalk
- Every method returns a result object.
Therefore methods in Smalltalk are functions.
Default object to be returned is SELF (receiver of message).
- Objects and constants can be addressed via variables.
- Variables are always POINTER to objects in Smalltalk.
- A variable can point to any object of any class.
Therefore Smalltalk is not a typed language.
- C++
- Methods are functions. They may return void.
- Objects can be addressed via variables.
- A variable may be a pointer to an object or contain the object itself.
- A variable may only contain objects of a specific class.
- Therefore C++ is a typed language
Example
- Smalltalk
5 square
- Object 5 receives message "square".
- Object 5 checks, if method "square" is known.
- If so "square" is executed an result (object 25) will be returned.
- Otherwise an errorobject will be returned. In this case a debugger
will appear with message "Object does not understand "square" ".
- C++
MyDouble f=5,r;
r= f.square;
- Object f receives message "square".
- "square" must have been declared in the header file containing the definition of
class MyDouble. So the compiler checks, if this method is defined.
- Function MyDouble MyDouble::square(const MyDouble &) will be executed and the
result assigned to r.
There are three ways to look on an object:
- implemented Data Type
- piece of storage to be altered only by predefined operations.
- doughnuts
- objects have two parts
enclosed: Implementor side
around: User side
- persons
- objects are persons with predefined responsibilities.
- class = internal and external description of an object.
- abstract data type = consumer side of a class .
- object = instantiation of a class in the computer. (class as a 'cookie cutter')
In C++ a class consists of its declaration part (H-file) and its definition part (C-file).
Unfortunately these two parts can be merged into one: i.e. all may be included to the
H-file (and sometimes must be - f.e. inline methods). A good idea would have been
to put into the header file only the external r consumer side description of a class.
In C++ even some internal or implementation parts of the description (protected members,
internal methods) has to be included in the H-file.
The implementor side of a class consists of the coded methods and the specificaton
of internal variables. Here it is told how things are to be done. But this side is invisible
from the consumer side. This is called infomation hiding. The consumer knows the behaviour
of a class - the interface. But he/she does not know how the class obtains the results
returned by its methods and how the data is internally represented.
A class can be viewed in the following ways:
- A class is a collection of similiar objects.
- A class is a template ('cookie cutter') for a object.
- An object is the realization of a class in the computer.
Classes can be ordered hierarchically. This is explained in the section about
inheritance .
A class contains:
- instance variables
- In Smalltalk instance variables are pointers to objects describing the internal state
of an object. In C++ instance variables are clalled data members. They may fully be included
into an object and not only be pointers. Whenever an object is created it gets its own copy of
every instance variable defined.
- class variables
In C++ class variables do not exist as a concept of the language. But any static data member
of a C++ class has the properties of a class variable. In Smalltalk class variables point to objects known
to all objects of he class and are accessible by them.
A class variable is instantiated only once for the entire cass.
- methods
- coded method for every operation defined for the class.
Example :
Realisation of the class Account in a classical language:
DCL 1 KONTO BASED(*),
2 KONTOID CHAR(8),
2 BETRAG BIN FIXED(31),
2 INHABER CHAR(240),
2 ...;
EINZAHLE: PROCEDURE(KPOINTER,EBETRAG,KID);
...
END;
Usage:
DCL KONTO1 CHAR(LENGTH(KONTO));
...
CALL EINZAHLE (ADDR(KONTO1),200,'K147I');
Realisation of class Account in Smalltalk:
Object subclass: #Konto
instanceVariableNames:'kontoid betrag inhaber ...'
classVariableNames:'Bankleitzahllist'
einzahle: einBetrag
" der uebergebene Betrag wird auf den Empfaenger eingezahlt "
betrag := betrag + einBetrag.
Class Account in C++
class Konto {
protected:
Int Kontoid;
Waehrung Stand;
Person* Inhaber;
public:
static Array Bankleitzahllist;
public:
int Einzahle (const Waehrung & Betrag){
Stand+=Betrag;
}
int Eroeffne (Person* cc, const Waehrung & Betrag){
if (! cc->check()) return FALSE;
Stand = Betrag;
Inhaber= cc;
retrn TRUE;
}
} // end class Konto
How classes are used?
There exist two types of methods:
- class methods
- In Smalltalk the receiver of messages calling class methods is
always a class. In C++ classes can never be the receiver of a message.
Therefore most static methods are class methods. Class methods never
operate on objects (exception is creation of an object) but on static
class data.
Constructors and destructors can be also viewed as class methods. In
Smalltalk the method new creating a new object is a class method. Objects
are never destroed explicitely in Smalltalk. This is done by the garbage collector.
In C++ no garbage collector exist.
- instance methods
- Receivers of messages calling a instance method are always objects.
Instance methods may alter the state of an object.
Example
Usage of Smalltalk class Account:
k1 := Konto new .
k1 einzahle: (DM new:200 ). "DM subclass of Waehrung"
The method new allocates storage to the instance variables of k1.
These instance variables still point to undefined objects. Therefore the
method 'einzahle' will fail.
Instance method init
init: einBetrag
betrag := einBetrag.
class method eroeffne
eroeffne: einBetrag
^(self new) init: einBetrag
Usage in C++
Konto K1 ;
/* similiar to K1:= Konto new */
Person Person1;
DM Betrag(200) ; // DM subclass of Waehrung
....
K1.Eroeffne(&Person1,Betrag); //initialize state of K1
DM Betrag2(150);
K1.einzahle(Betrag2);
The declaration of K1 operates as new in Smalltalk. The state of the
object K1 is undefined if no initialization is defined in the constuctor method
for class Konto.
Even when object oriented methodology is used black box reuse seem to be not possible
in every case. At least subclassing with overwriting methods is often necessary. In the
end object orientation brought software engineering a big step forward but did not reach all.
So the question of reusability of designs arises.
As described in (12) in architecture 'patterns' are used to describe elements solving problems
occurring again and again when designing a building. Such patterns can also be collected for
software elements. Beside cataloging design ideas solving frequently arising problems patterns
define a language in which we can explain and communicate designs.
Target of design patterns is:
- communicate design techniques
- catalog of reusable designs. This can be used to find a appropriate design
for an akute problem to be solved without mixing several patterns which would limit reusability.
- documenting design
Elements of patterns are
- name
- the name should describe the purpose of the pattern. Factory is a pattern creating
objects. Strategy is a pattern describing an algorithm.
- problem
- describes when to apply this pattern and differences to similiar patterns.
- solution
- describes general arrangemants of objects and classes but no concrete design. Here
only a template for a design is given. Sample code/designs might be added.
- consequences
- list additional behaviour of a pattern. F.e. space/time trade-offs, extensibility, etc.
An example of a design pattern is a Model-View-Controler (MVC). In a concrete Design the model would
represent one or several business objects (f.e. figures in our graphical editor), the views would be the
different presentations of figures (star character on printer page or graphical view on screen ...). A controller
would be added if user interactions are defined.
For a further study of this matter have a look into (13). Own experience in OO design is recommended.
- (8) Bertrand Meyer: Object Oriented Software
Construction; Prentice Hall; 1988
- (9) Grady Booch: Object Oriented Design with
Applications; Benjamin/Cummings; 1991
- (10) Ruth Breu: Algebraic Specifications Techniques in Object
Oriented Programming Environments; Lecture Notes in Computer
Science 562; Springer; 1991
- (11) J.D. McGregor, T.Korson: Supporting dimensions of
classification in OOD in: Journal of OOP Vol.5 No.9 Feb. 1993
- (12) E.Gamma, R. Helm, R.Johnson, J. Vissides: Design Patterns -
Elements of Reusable OO Software