When developing an object-oriented system, at some time a choice has to be made for an actual programming language or environment. It goes without saying that the optimal environment will be one that is in accord with the method chosen for design. Naturally, other desiderata (involving efficiency, portability or client-imposed constraints) may play an equally significant role.
Additional keywords and phrases:
programming languages, orthogonality, reliability, complexity, types, delegation, multiple paradigms, prototypes,
reflection
Whatever motivation lies behind the development of a programming
language, every language is meant to serve some purpose.
Whether implicitly or explicitly stated, a programming
language is characterized by its design goals and
the applications it is intended to support.
A fortiori this holds for object-oriented programming
languages.
The impetus to research in object-oriented programming
may be traced back to the development of Simula,
which was originally intended for discrete event simulation.
As observed in
Of the 69 (standalone) object-oriented languages surveyed,
53 were research projects and only 16 were commercial products.
Of these, 14 were extensions of either Lisp (10)
or C (4).
Among the remaining languages, quite a number were derived from
languages
such as Pascal, Ada or Prolog.
There is a great diversity between the different object-oriented languages.
However, following
POOL-T also supports the notion of active objects.
Active objects have a body which allows them to execute their
own activity in parallel with other active objects.
To answer a request to execute a method, an active object must
explicitly interrupt its activity (by means of an answer or
accept statement as in Ada).
POOL-T is interesting, primarily, because it is complemented
by extensive theoretical research into the semantical foundations
of parallel object-oriented computing.
See Alternative object models
Since the introduction of Smalltalk, the predominant
notion of objects has been based on the distinction
between classes and objects.
Classes serve to describe the functionality and behavior of objects,
while objects are instance of classes.
In other words, classes serve as templates to create objects.
Inheritance, then, may be regarded
as a means by which to share (descriptions of) object
behavior.
It is generally defined on classes in terms of a derivation mechanism,
that allows one to declare a class to be a subclass of another (super)
class.
The distinction between classes and objects leads to a number
of difficulties, both of a pragmatic and theoretical nature.
(See also sections dimensions and meta for a discussion of the theoretical
problems.)
For example, the existence of one-of-a-kind classes,
that is classes which have only one instance, is
often considered unnatural.
An example of a class-less language is the language Self.
Self has a Smalltalk-like syntax, but in contrast
to Smalltalk only supports objects (containing slots)
and messages, and hence no classes.
Slots may be designated to be parent-slots which means that
messages that cannot be handled by the object itself are delegated to the
parent object.
In contrast to inheritance, which is static since the inherited
functionality is computed at object creation time, delegation
to parent objects as in Self is dynamic, since parent slots
may be changed during the lifetime of an object.
Objects in Self may themselves be used to create other objects
(as copies of the original object) in a similar way as classes
may be used to create instances.
However, the changes made to an object are propagated when cloning
object copies.
Single objects, from which copies are taken, may in other words be regarded
as prototypes, approximating in a dynamic way the functionality of
their offspring,
whereas classes provide a more static, so to speak universal,
description of their object instances.
Self employs runtime compilation, which is claimed
to result in an efficiency comparable to C in
Alternative object models may also be encountered
in object-oriented database managements systems and in systems
embedding objects such as hypertext or hypermedia systems.
Object extensions of Lisp, C and Prolog
The notion of object is to a certain extent orthogonal
to, that is independent of, language constructs around
which programming languages may be constructed,
such as expressions, functions and procedures.
Hence, it should come as no surprise that a number of (popular)
object-oriented programming languages were originally
developed as extensions of existing languages or
language implementations.
See slide 5-extensions.
JPL
Jacl, Tcl Blend
JPython
Scripting has clear advantages for rapid prototyping. Disadvantages of scripting concern the lack of efficiency, and the absence of compile-time checks.
Script languages may be extended using C/C++,
and more recently Java.
The impact of Java becomes evident when considering
that there exists a Java implementation for almost
each scripting language, including Tcl/Tk, Perl and Python.
JPython, which is the realization of Python in Java,
even offers the possibility to integrate Python classes with Java
classes, and is announced as a candidate
scripting platform for Java in
Java has also in other respects stimulated programming language
research,
since it appears to be an ideal platform
for realising higher level programming languages.
Objects in Javascript
Javascript is a somewhat special case, since it allows for
the use of built-in objects,
in particular the objects defined by the
Document Object Model (DOM),
and its precursors.
Nevertheless, due to its dynamic nature, Javascript also allows
for creating user-defined objects,
as indicated in the example
below.
Which objects are available as built-in objects
depends on the environment in which Javascript programs
are executed.
In the example, there is an invocation of
the write method for a document object.
The document object, as well as other objects
corresponding to the browser environment and the contents of
the page loaded, are part of the Document Object Model,
which is discussed in more detail
in section DOM.
As an aside, Javascript has become surprisingly popular for writing
dynamic HTML pages, as well
as for writing server-side scripts.
It is also supported by many VRML (Virtual Reality Modeling Language)
browsers to define script nodes.
See section DIVA.
A reference implementation of Javascript is available,
for embedding Javascript in C/C++ applications.
Smalltalk (that is Smalltalk-80 of ParcPlace Systems)
comes with a large collection of general purpose and graphics
programming classes, that are identical for both MS-DOS and
Unix platforms.
Also Eiffel
comes with a standard collection of well-documented
libraries containing common data structures, container
classes and classes for graphics programming.
For both Smalltalk and Eiffel, the accompanying classes
may almost be considered to be part of the language
definition, in the sense that they provide a
standard means to solve particular problems.
In contrast, for C++ there is almost no standard
library support (except for IO stream classes).
Even worse, the various C++ compiler vendors
disagree considerably in what functionality the standard
class libraries of C++ must offer.
Fortunately, however, there is an increasingly large
number of third party libraries (commercially and non-commercially)
available.
The burden of choosing the appropriate libraries
is, however, placed on the shoulders of a user
or a company,
which has the advantage that a more optimal
solution may be obtained
than possible within the confines of standard libraries.
Java, on the other hand, offers an overwhelming amount
of APIs, including a Reflection API, for meta programming,
APIs for networking, communication, and APIs for multimedia
and 3D.
Perhaps the greatest benefit of Java is the effort put into
the standardization of these APIs.
Of the object-oriented programming languages
we consider,
Smalltalk definitely offers the most comprehensive
programming environment (including editors, browsers and
debuggers).
Eiffel comes with a number of
additional tools (such as a graphical browser, and a
program documentation tool) to support program
development (and maintenance).
In contrast, C++ usually comes with nothing at all.
However, increasingly many tools (including browsers
and debuggers) have become available.
For Java there are a number of IDEs (Integrated Development Environments)
available, most of which run only on the PC platform.
The issue of producing documentation from C++ is still open.
A number of tools exist (including a WEB-like system
for C++ and a tool to produce manual pages from C++
header files) but no standard has
yet emerged.
Moreover, some people truly dislike the terseness of C/C++.
Personally, I prefer the C/C++ syntax above the syntactical
conventions of both Eiffel and Smalltalk,
provided that it is used in a disciplined fashion.
Java programs may be documented using javadoc.
The javadoc program may be regarded as the standard
C++ has been waiting for, in vain.
An important feature of Eiffel
is that it supports assertions that may be validated
at runtime.
In combinations with exceptions, this provides
a powerful feature for the development
of reliable programs.
At the price of some additional coding (for example,
to save the current state to enable the use
of the old value), such
assertions may be expressed by using the assert macros
provided for C++.
In defense of C++,
it is important to acknowledge that C++ offers
adequate protection mechanisms to shield classes
derived by inheritance from the implementation details
of their ancestor classes.
Neither Smalltalk nor Eiffel offer such protection.
Java was introduced as a more reliable variant of C++.
Java's reliability comes partly from the shielded
environment offered by the Java virtual machine,
and partly from the absence of pointers and
the availability of built-in garbage collection.
Practical experience shows that for the average student/programmer
Java is indeed substantially less error-prone than C++.
As far as the assertion mechanism offered by Eiffel
is concerned,
The Java language offers only single (code) inheritance,
but allows for multiple interface inheritance.
The realization of (multiple) interfaces seems to be
a fairly good substitute for multiple (implementation) inheritance.
The compilation of Eiffel programs can result in programs
having adequate execution speed.
However, in Eiffel dynamic binding takes place in principle
for all methods.
Yet a clever compiler can significantly
reduce the number of indirections needed to execute a method.
In contrast to C++, in Eiffel all objects are created
on the heap.
The garbage collection needed to remove these objects
may affect the execution speed of programs.
C++ has been designed with efficiency in mind.
For instance, the availability of inline functions,
and the possibility to allocate objects on the runtime stack
(instead of on the heap),
and the possibility to declare friend functions
and classes that have direct access to the private
instance variables of a class
allow the programmer to squeeze out the last drop
of efficiency.
However, as a drawback, when higher level functionality
is needed
(as in automatic garbage collection)
it must be explicitly programmed, and a similar price
as when the functionality would have
been provided by the system has to be paid.
The only difference is that the programmer has a choice.
At the time of writing there does not exist a truly
efficient implementation of the Java language.
Significant improvements may be expected from
the JIT (Just In Time) compilers that produce native code
dynamically, employing techniques as originally developed for
the Self language, discussed in section Self.
Eiffel contains few language elements
that extend beyond object-oriented programming.
In particular, Eiffel does not allow for overloading
method names (according to signature) within a class.
This may lead to unnecessarily elaborate method names.
(The new version of Eiffel (Eiffel-3) does
allow for overloading method names.)
Without doubt, C++ is generally regarded as a highly complex
language.
In particular, the rules governing the overloading of operators
and functions are quite complicated.
The confusion even extends to the various
compiler suppliers, which is one of the
reasons why C++ is still barely portable.
Somewhat unfortunately,
the rules for overloading and type conversion
for C++ have to a large extent been determined
by the need to remain compatible with C.
Even experienced programmers need occasionally
to experiment to find out what will happen.
According to
Java is certainly less complex than C++.
For example, it offers no templates, no operator overloading
and no type coercion operators.
However, although Java is apparently easier to use, it is
far less elegant than C++ when it comes to creating user-defined types.
Class interfaces in Java are usually much more verbose
than similar interfaces in C++.
And, due to the absence of templates,
type casts are necessary in many places.
On the other hand, casts in Java are type safe.
An example of the declaration of an active object in sC++
is given in slide ex-active.
The sC++ language is implemented as an extension to the GNU C++ compiler.
The sC++ runtime environment offers the possibility
to validate a program by executing random walks,
which is a powerful way to check the various synchronization conditions.
The model of active objects supported by sC++
has also been realized as a Java library, see
As argumented in
Whenever a method is repeatedly invoked,
the address of the recipient object may be backpatched
in the caller.
In some cases, even the result may be inlined
to replace the request.
Both techniques make it appear that
message passing takes place, but at a much lower price.
More complicated techniques,
involving lazy compilation (by delaying the compilation
of infrequently visited code)
and message splitting (involving a dataflow
analysis and the reduction of redundancies)
may be applied to achieve more optimal results.
Benchmark tests have indicated a significant improvement
in execution speed (up to 60% of optimized C code)
for cases where type information could be dynamically obtained.
The reader is referred to
To be an object, a class itself must be an instance
of a class (which for convenience we will call
a metaclass).
Take, for example, the class Point.
This class must be an instance of a (meta)class
(say Class) which in its turn must be an instance of a (meta)
class (say MetaClass),
and so on.
Clearly, following the instance relation
leads to an infinite regress.
Hence, we must postulate some
system-defined MetaClass (at a certain level)
from which to instantiate the (metaclasses of)
actual classes such as Point.
A concise treatment of programming languages
is given in
(C) Æliens
04/09/2009
function object_display(msg) { // object method
return msg + ' (' + this.variable++ + ')';
}
function object() { // object constructor
this.variable=0;
this.display = object_display;
return this;
}
var a = new object(); // create object
document.write(a.display("a message"));
document.write(a.display("another message"));
</script>
Comparing Smalltalk, Eiffel, C++ and Java
subsections:
The languages Smalltalk, Eiffel, C++ and Java
may be regarded as the four most important
(and popular) representatives of classical
object-oriented languages, classical in the sense
of being based on a class/object distinction.
Criteria for comparison
Criteria for comparison
When choosing a particular programming language
as a vehicle for program development a number
of factors play a role, among which are the availability
of a class library, the existence of a good programming
environment, and, naturally,
the characteristics of the language itself.
Class libraries
An important criterion when selecting a language
may be the availability of sufficient class library support.
A general class library, and preferably libraries
suitable for the application domain one is working in,
may drastically reduce development time.
Another important benefit of using (well-tested)
class libraries is an improvement of the reliability of the
application.
Programming environments
Another selection criterion in choosing a language is
the existence of a good programming environment.
What constitutes a good programming environment
is not as simple as it may seem,
since that depends to a large extent upon the experience
and preferences of the user.
For example, with respect to operating systems,
many novice users favor a graphical interface as
originally offered by the Apple Macintosh computers,
while experienced users often feel constrained
by the limitations imposed by such systems.
In contrast, experienced users may delight in the
terseness and flexibility of the command-based
Unix operating system, which leads
to outright bewilderment with many novice users.
Language characteristics
Despite the commonality between Smalltalk, Eiffel, C++ and Java
(which may be characterized by saying that they all support
data abstraction, inheritance, polymorphism
and dynamic binding), these languages widely
disagree on a number of other properties,
such as those listed in slide 5-characteristics.
Language characteristics
Language characteristics
Smalltalk, Eiffel, C++ and Java differ with respect to
a number of language characteristics.
An indication of the differences between these
languages is given slide 5-language-characteristics.
This characterization conforms to the one given in
Uniformity
In Smalltalk, each data type is described by a class.
This includes booleans, integers, real numbers and control
constructs.
In Eiffel there is a distinction between elementary
data types (such as boolean, integer and real)
and (user-defined) classes.
However (in the later versions of Eiffel)
the built-in elementary types behave as if declared
by pre-declared classes.
For C++, the elementary data types and simple data structures
(as may be defined in C) do not behave as objects.
To a certain extent, however,
programmers may deal with this non-uniformity by some
work-around, for example by overloading functions
and operators or by embedding built-in types in a (wrapper) class.
Java may be regarded as a simplified version of C++.
Due to its restrictions, such as the absence of
operator overloading and type casts,
the language appears to be more uniform
for the programmers.
Documentation value
Smalltalk promotes a consistent style in writing programs,
due to the assumption that everything is an object.
One of perhaps the most important features
of Eiffel is the use of special keywords for
constructs to specify the correctness of programs
and the behavioral properties that determine the external
interface of objects.
Moreover, Eiffel provides a tool to extract a description
of the interface of the method classes
(including pre- and post-conditions associated with a method)
which may be used to document (a library of) classes.
To my taste, however, the Eiffel syntax leads to
somewhat verbose programs, at least in comparison
with programs written in C++.
Reliability
Smalltalk is a dynamically typed
language.
In other words, type checking, other than detecting runtime errors,
is completely absent.
Eiffel is generally regarded as a language possessing
all characteristics needed for writing reliable programs,
such as static type checking and constructs for stating
correctness assertions (which may be checked at runtime).
Due to its heritage from C, the
language C++ is still considered by many as unreliable.
In contrast to C, however, C++ does
provide full static type checking, including the signature
of functions and external object interfaces
as arise in independent compilation of module files.
Nevertheless, C++ only weakly supports type checking
across module boundaries.
Contrary to common
belief, Eiffel's type system is demonstrably inconsistent,
due to a feature that enables a user to dynamically define the
type of a newly created object in a virtual way
(see section self-reference).
This does not necessarily lead to type-insecure
programs though, since the Eiffel compiler
employs a special algorithm to detect such cases.
The type system of C++, on the other hand, is consistent
and conforms to the notion of subtype as introduced
informally in the previous part.
Nevertheless, C++ allows
the programmer to escape the rigor of the
type system by employing casts.
Inheritance
Smalltalk offers only single inheritance.
In contrast, both Eiffel and C++ offer multiple inheritance.
For statically typed languages, compile-time
optimizations may be applied that result in only
a low overhead.
In principle, multiple inheritance allows one to model
particular aspects of the application domain in a flexible
and natural way.
Efficiency
Smalltalk, being an interpreted language, is typically
slower than conventionally compiled languages.
Nevertheless, as discussed in section
self-implementation, interpreted object-based
languages allow for significant optimizations,
for example by employing runtime compilation techniques.
Language complexity
Smalltalk may be regarded as having a low language complexity.
Control is primarily effected by message passing,
yet, many of the familiar conditional and iterative
control constructs reappear in Smalltalk programs
emulated by sending messages.
This certainly has some elegance, but does not necessarily
lead to easily comprehensible programs.
Design dimensions of object-oriented languages
subsections:
Despites the widespread adoption of object-oriented terminology in
the various areas of computer science and software
development practice, there is considerable confusion about
the precise meaning of the terms employed and the (true)
nature of object-oriented computing.
In an attempt to resolve this confusion, Object-oriented language design
data abstraction -- state accessible by operations
strong typing -- compile time checking
Object-based versus object-oriented
\label{object-based}
How would you characterize Ada83? See Classes versus types
Another confusion that frequently arises
is due to the ill-defined relationship between
the notion of a class and the notion of a type.
The notion of types is already familiar
from procedural programming languages such as
Pascal and (in an ill-famed way) from C.
The type of variables and functions may be profitably
used to check for (syntactical) errors.
Strong static type checking may prevent errors
ranging from simple typos to using undefined
(or wrongly defined) functions.
The notion of a class originally has a more
operational meaning.
Operationally, a class is a template for object creation.
In other words, a class is a description of the collection
of its instances, that is the objects that are
created using the class description as a recipe.
Related to this notion of a class, inheritance was
originally defined as a means to share (parts of)
a description.
Sharing by (inheritance) derivation is, pragmatically,
very convenient.
It provides a more controlled way of code sharing
than, for example, the use of macros and file inclusion
(as were popular in the C community).
Since Towards an orthogonal approach -- type extensions
According to Orthogonal approach
Objects
are in essence modular computing agents.
They correspond to the need for encapsulation in design,
that is the construction of modular units to
which a principle of locality applies (due to
combining data and operations).
Object-oriented languages may, however, differ in
the degree to which they support encapsulation.
For example, in a distributed environment a
high degree of encapsulation must be offered,
prohibiting attempts to alter global variables
(from within an object) or local instance
variables (from without).
Moreover, the runtime object support system must
allow for what may best be called remote method invocation.
As far as parallel activity is concerned, only a few languages provide
constructs to define concurrently active objects.
See section Active for a more detailed discussion.
Whether objects support reactiveness,
that is sufficient flexibility to respond safely
to a message, depends largely upon (program) design.
Types
may be understood as a mechanism for expression classification.
From this perspective, Smalltalk may be regarded
as having a dynamic typing system:
dynamic, in the sense that the inability to evaluate an expression
will lead to a runtime error.
The existence of types obviates the need to have classes,
since a type may be considered as a more abstract description of the
behavior of an object.
Furthermore, subclasses (as may be derived through inheritance)
are more safely defined as subtypes in a polymorphic
type system. See section flavors.
At the opposite side of the type dimension we find the statically typed languages,
which allow us to determine the type of
the designation of a variable at compile-time.
In that case, the runtime support system need not carry any type information,
except a dispatch table to locate virtual functions.
Delegation
(in its most generic sense)
is a mechanism for resource sharing.
As has been shown in Abstraction
(although to some extent related to types)
is a mechanism that may be independently applied to provide
an interface specification for an object.
For example, in the presence of active objects
(that may execute in parallel)
we may need to be able to restrict dynamically the interface of an object as specified by its type
in order to maintain the object in a consistent state.
Also for purely sequential objects we may impose a particular protocol of interaction
(as may, for example, be expressed by a contract) to be able to
guarantee correct behavior.
Another important aspect of abstraction is protection.
Object-oriented languages may provide (at least) two kinds
of protection.
First, a language may have facilities to
protect the object from illegal access by a client (from without).
This is effected by annotations such as private and protected.
And secondly, a language may have facilities to protect the object
(as it were from within) from illegal access through delegation
(that is by instances of derived object classes).
Most languages support the first kind of protection.
Only few languages, among which are C++ and Java, support the second kind too.
The independence of abstraction and typing may further
be argued by pointing out that languages
supporting strong typing need not
enforce the use of abstract data types having a well-defined
behavior.
Multi-paradigm languages -- logic
Object-oriented programming has evolved as a new and
strong paradigm of programming.
Has it?
Of the languages mentioned, only Smalltalk
has what may be considered a radically new language design
(and to some extent also the language Self, that we will
discuss in the next section).
Most of the other languages, including Eiffel, C++
(and for that matter also CLOS and Oberon),
may be considered as object-oriented extensions of already
existing languages or, to put it more broadly, language
paradigms.
Most popular are, evidently, object-oriented extensions
based on procedural language paradigms,
closely followed by the (Lisp-based) extensions of the
functional language paradigm.
Less well-known are extensions based on the logic programming
paradigm, of which DLP is my favorite example.
In Open systems
Dimensions of modularity
Object-oriented logic programming
Logic programming is often characterized as relational
programming, since it allows the exhaustive
exploration of a search space defined by logical relations
(for instance, by backtracking as in Prolog).
The advantage of logic programming, from a modeling point of view,
is that it allows us to specify in a logical manner
(that is by logical clauses) the relations between
the entities of a particular domain.
A number of efforts to combine logic programming with
object-oriented features have been undertaken,
among which is the development of the language Vulcan.
Vulcan is based on the Concurrent Prolog language
and relies on a way of implementing objects as
perpetual processes.
Without going into detail, the idea (originally proposed in
Active objects -- synchronous Java/C++
When it comes to combining objects
(the building blocks in an object-oriented approach)
with processes (the building blocks in
parallel computing),
there are three approaches conceivable.
See slide 6-o-conc.
Object-based concurrency
Processes
The first, most straightforward approach,
is to simply add processes
as a primitive data type, allowing the
creation of independent threads of processing.
An example is Distributed Smalltalk (see Bennett, 1987).
Another example is Java, which provides support for
threads, synchronized methods and statements like wait
and notify to protect re-entrant concurrent methods.
The disadvantage of this approach,
however,
is that the programmer has full responsibility
for the most difficult part of parallel programming,
namely the synchronization between processes and the
avoidance of common errors
such as simultaneously assigning a value to a shared
variable.
Despite the fact that the literature, see Active objects
A second, and in my view preferable, approach
is to introduce explicitly a notion of active objects.
Within this approach, parallelism is introduced by having
multiple, simultaneously active objects.
An example of a language supporting active objects
is POOL, described in Asynchronous communication
Deadlock may come about by synchronous (indirect)
self-invocation.
An immediate solution to this problem is provided
by languages supporting asynchronous communication,
which provide message buffers allowing the caller
to proceed without waiting for an answer.
Asynchronous message passing, however,
radically deviates from the (synchronous) message passing
supported by the traditional (passive) object
model.
This has the following consequences.
First, for the programmer, it becomes impossible
to know when a message will be dealt with
and, consequently, when to expect an answer.
Secondly, for the language implementor,
allocating resources for storing
incoming messages and deciding when to deal
with messages waiting in a message buffer
becomes a responsibility for which it is hard to
find a general, yet efficient, solution.
Active objects with asynchronous message passing
constitute the so-called actor model,
which has
influenced several
language
designs. See Synchronous C++/Java
Prototypes -- delegation versus inheritance
subsections:
The classical object model
(which is constituted by classes, objects and inheritance)
not only has its theoretical weaknesses (as outlined
in the previous section)
but has also been criticized from a more pragmatic
perspective because of its inflexibility
when it comes to developing systems.
Code sharing has been mentioned as one of
the advantages of inheritance (as it allows incremental
development).
However, alternative (read more flexible) forms of
sharing have been proposed, employing
prototypes and delegation instead of inheritance.
Alternative forms of sharing
A class provides a generic description
of one or more objects, its instances.
From a formal point of view, classes are related to types,
and hence a class may be said to correspond to
the set of instances that may be generated from it.
This viewpoint leads to some anomalies,
as in the case of abstract
classes that at best correspond to partially defined sets.
As another problem, in the context of inheritance,
behavioral compatibility may be hard to arrive at,
and hence the notion of subtype
(which roughly corresponds with the subset relation)
may be too restrictive.
In practice, we may further encounter
one-of-a-kind objects, for which it is simply cumbersome
to construct an independent class.
In a by now classical paper, Prototypes -- exemplars
Design issues
State
Creation
Delegation
Implementation techniques -- Self
A major concern of software developers
is (often) the runtime efficiency of the
system developed.
An order of magnitude difference in execution speed
may, indeed, mean the difference between acceptance and rejection.
Improving performance
Dynamic compilation
The language Self
is quite pure and simple in design.
It supports objects with slots
(that may contain both values and code, representing methods),
shallow cloning,
and implicit delegation (via a designated parent slot).
Moreover, the developers of Self have introduced a number
of techniques to improve the efficiency
of prototype-based computing.
Self -- prototypes
Dynamic compilation -- type information
Meta-level architectures
Another weakness of the classical object model
(or perhaps one of its strengths)
is that the concept of a class easily
lends itself to being overloaded with additional
meanings and features such as class variables
and metaclasses.
These notions lead to extensions to
the original class/instance scheme
that are hard to unify in a single elegant
framework.
In this section
we will study a proposal based on a reflexive
relation between classes and objects.
Depending on one's perspective, a class may either
be regarded as a kind of abstract data type
(specifying the operational interface of its object instances)
or, more pragmatically, as a template for object creation
(that is, a means to generate new instances).
The class concept
Class variables
Clearly, the use of class variables
violates what we have called the distribution boundary
in section multi-paradigm,
since it allows objects to reach out of their
encapsulation borders.
Class variables may also be employed in C++ and Java
by defining data members as static.
Apart from class variables, Smalltalk also supports
the notion of class methods,
which may be regarded as routines having the class
and its instances as their scope.
Class methods in Smalltalk are typically used
for the creation and initialization of new instances of the class
for which they are defined.
In C++ and Java, creation and initialization is taken care
of by the constructor(s) of a class,
together with the (system supplied) new
operator.
Class methods, in C++ and Java, take the form
of static member functions that are like
ordinary functions
(apart from their restricted scope and their calling syntax,
which is of the form in C++ and in Java).
Contrary to classes in C++ and Java,
classes in Smalltalk have a functionality
similar to that of objects.
Classes in Smalltalk provide
encapsulation (encompassing class variables and
class methods) and message passing
(for example for the creation and initialization
of new instances).
To account for this object-like behavior,
the designers of Smalltalk have introduced the notion
of metaclass of which a class is an instance.
Metaclasses
In the classical object model, two relations
play a role when describing the
architectural properties of a system.
The first relation is the instance relation
to indicate that an object O is an instance of a class
C.
The second (equally important) relation is
the inheritance relation,
which indicates that a class C is a subclass
(or derived from) a given (ancestor) class P.
When adopting the philosophy
everything is an object
together with the idea that
each object is an instance of a class
(as the developers of Smalltalk did),
we evidently get into problems when we try to explain
the nature (and existence) of a class.
Reflection
Postulates -- class-based languages
Reflective definition of Class
Summary
The object paradigm
Comparing Smalltalk, Eiffel, C++ and Java
Design dimensions of object-oriented languages
Prototypes -- delegation versus inheritance
Meta-level architectures
Questions
Further reading