The concept of interfaces is central to object-oriented methodologies and is one of the most attractive features of Java and COM. Although Smalltalk always had interfaces implicitly, in Smalltalk interfaces are not first-class objects: they cannot be conversed with, referred to, or reflected upon. Consequently, Smalltalkers have been deprived of such an important and useful tool.
Since a fundamental feature of Smalltalk is that just about everything in the language is an implementation feature, explicit, static interfaces can be added to Smalltalk using Smalltalk itself with ease. However, such an addition would short-change the powerful dynamic aspects of Smalltalk.
In this article we present SmallInterfaces; a new ontology of dynamic interfaces which makes a powerful use of the dynamic nature of Smalltalk. SmallInterfaces adds interfaces as honorary members to Smalltalk's extensive reflection mechanism, in a manner portable across the many Smalltalk variants.
The term Interface is central to object-oriented methodologies [Reen96] and object foundation [Abad96], and has been recently popularised by COM and Java. Smalltalk had interfaces implicitly from its beginning. However, since Smalltalk does not have interfaces as first-class objects, they cannot be conversed with, referred to, or reflected upon. Because interfaces has proven to be extremely useful in supporting program understanding and facilitating the transition from a conceptual design to a concrete implementation, the lack of explicit interfaces in Smalltalk deprives Smalltalk developers of such an important and useful tool.
Since a fundamental feature of Smalltalk is that just about everything in the language is an implementation feature, explicit interfaces can be added to Smalltalk using Smalltalk itself with relative ease. However, since Smalltalk is not merely a language but a live, dynamic environment, adding static interfaces would lead to an ad-hoc solution. Moreover, because Smalltalk is also an environment, every solution which extends Smalltalk with interfaces has to integrate them into the Smalltalk IDE as well. On top of this challenge, since there exist multiple Smalltalk dialects, it is preferable that such a solution is portable across Smalltalk dialects and facilitates the exchange of interfaces in and out of the Smalltalk environment.
This paper describes how one solution, SmallInterfaces, has addressed the challenge and extended Smalltalk with explicit dynamic interfaces in a portable fashion. Moreover, the solution presented here does not limit itself to reproducing static interfaces in Smalltalk but defines a new ontology of interfaces. This ontology empowers interfaces to become dynamic and adaptive objects completely causally connected with their environment [Mae87], a definition which adapts better to Smalltalk's dynamic nature. SmallInterfaces is a freeware add-on to Smalltalk developed by Benny Sadeh, and so far has been ported to VisualWorks, VisualAge, Squeak, Smalltalk X, and GemStone variants of Smalltalk. For downloads and further details see: http://brain.cs.uiuc.edu/VisualWorks/SmallInterfaces.
The structure of this paper is as follows. We briefly describe what are interfaces, then we present a new ontology of dynamic interfaces for Smalltalk. We then illustrate how such interfaces solve different practical problems encountered by software developers, then follow with a description of how dynamic interfaces are integrated within the Smalltalk IDE. Finally, we compare other approaches before concluding.
Interfaces are fundamental aspects of object-oriented programming [Cann89], [Abad96]. The term Interface is central to object-oriented methodologies and is one manifestation of what commonly referred to as Type. Unlike a Class, which is a concrete type, an interface is an abstract type. An interface specifies messages an object will understand but has no method implementations for those messages, where a class specifies how those messages will be executed by having concrete method implementations for those messages.
As a language level construct an interface contains a set of method declarations, each being a method signature. The make of a signature varies substantially across languages. In its widest form a signature consists of a name, an ordered list of qualified incoming arguments (parameters), a qualified return argument (result), a set of possibly thrown exceptions, and a set of possibly triggered events. There are many opinions about what is the correct composition of a method signature; the question is open for debate and is outside the scope of this paper. SmallInterfaces takes the position that for Smalltalk it is sufficient that a signature is of the simplest form: it is solely the method selector (In Smalltalk the method selector implicitly includes the number of parameters). It does not include the incoming parameters types or the returned result's type as Smalltalk is dynamically typed.
Object-Oriented languages differ on how they facilitate interfaces. Some, like C++, fold interfaces implicitly into the Class construct (abstract virtual class). Some, like Objective-C and Java, provide Interface and Class explicitly as two distinct constructs. And yet others, like Smalltalk, provide interfaces implicitly via polymorphic message sends.
The benefits of distinguishing interfaces from classes are not new; interfaces have proven to be extremely useful in facilitating the transition from a conceptual design to a concrete implementation using a role based approach [Cive93a], [Reen96a], [Rieh98], [Kend99], [Rieh00]. Interfaces are used mainly in the following contexts. Firstly, within OO methodologies where roles in the analysis level can be mapped naturally to interfaces in the implementation level. Secondly, in statically typed languages like Java, interfaces allow different objects implementing a common interface to be manipulated via this interface's point of view, therefore facilitating polymorphism. Thirdly, in component-ware interfaces establish which components can talk to which and about what, to facilitate pluggability [Com], [Yell94]. Fourthly, interfaces facilitate better code comprehension.
Smalltalk environments traditionally have lacked support for explicit interfaces. However, since a fundamental feature of Smalltalk is that just about everything in the language is an implementation feature, explicit interfaces can be added to Smalltalk using Smalltalk and its reflective capabilities itself with relative ease [Foot89]. Hence adding interfaces to Smalltalk does not require to modify the language grammar.
Porting a concept from one domain to another presents an opportunity for redefinition; constraints that existed in the source domain might annul themselves in the target domain. The mere transformation might raise possibilities that did not exist in the source domain. When trying to incorporate the explicit interface concept into Smalltalk we have two alternatives: either translate it as is based on the static typed semantics, or redefine it for a dynamically typed environment.
Looking at a static typed definition of an interface such as the one defined for Java, we find a few deficiencies:
SmallInterfaces chose to adapt the interface concept to Smalltalk's dynamic nature. Interfaces in Smalltalk are now tangible and adaptive objects, just like any other object in Smalltalk. The most important difference is that the relationship between interfaces and classes is dynamically inferred by the environment instead of being hardwired by the developer.
Figure 1: Relationships between Interfaces and Classes.
Figure 2: Relationships among Interfaces.
In addition to adding interfaces as honorary members to Smalltalk's extensive reflection mechanism, SmallInterfaces defines a new ontology of interfaces for Smalltalk, which is quite different from the one defined by other languages as shown by the Figures 1 and 2.
This redefinition of interfaces has the following properties:
a) Each interface specifies a set of messages, which together constitutes its repertoire.
b) Repertoires are not mutually exclusive; a message can be part of many repertoires.
c) A behavior (class or metaclass) is a conformer of an interface if it understands its entire repertoire. This is a many-to-many relationship: a behavior can conform to many interfaces, and an interface can be understood by many behaviors.
d) An anObject isTypeOf: type is true if type is its class (or one of its superclasses), or one of the interfaces its class conforms to.
e) An interface can be composed of other interfaces. A composite interface is an interface that extends other interfaces; it contains messages from the interfaces it extends, and may add additional messages of its own. A composite interface also referred to as an extending interface. These classifications are not mutually exclusive; an interface can be extended and extending at the same time.
f) Interfaces form heterarchies. They relate to each other using the family tree metaphor. An interface can have parents - the immediate interfaces it extends, and ancestors - the progenitor of its family lineage. An interface can have children - the immediate interfaces extending it, and descendants - family lineage emanating from it. An interface can have twins - the interfaces equivalent to it, siblings - the interfaces who share all of its parents, and stepsiblings - the interfaces that share some of its parents.
g) At the top of the heterarchy1 are root interfaces, which are parentless interfaces; they extend no other interfaces. At the bottom of the heterarchy are the leaf interfaces which are childless interfaces; no other interfaces extend them. These classifications are not mutually exclusive (Consider the case where an environment contains a single interface - that interface is both a root and a leaf at the same time.)
h) An interface with no repertoire is considered empty, and from the system point it is transparent; both meaningless and harmless.
i) Of equivalence and containment: an interface is equivalent to another if their repertoires are equal. An interface may be defined via an aggregation of other interfaces.
j) Within a universe of objects (such as the Smalltalk image), interfaces form acyclical directed graphs that are not necessarily connected. This universe is dynamic: The inter-relationships among interfaces may change whenever an interface is added to or removed from this universe, or whenever a message is added to or removed from an interface. Likewise are the relationships between classes and interfaces. So the relationships between classes and interfaces as well as among interfaces are always inferred based on the actual make of classes and interfaces in the current time in a particular universe.
Now that we have introduced the notion of dynamic interfaces, we show how they can be used to bridge the transition from design to implementation and support program understanding.
1 Heterarchy: a form of organization resembling a network or fishnet. [Web Dictionary of Cybernetics and Systems]
In the realm of domain analysis, the use of roles has emerged as an important technique for classifying and describing collaborations among groups of objects [Reen96a], [Rieh98]. Roles reflect the various parts an object may play within a scenario it participates in and are used extensively in Aspect-Oriented Programming design [Kend99]. Roles also serve as a metaphor for communicating object-oriented software designs and recognition of their importance has grown in recent years. For example, the codification of object-oriented software design knowledge using Design Patterns is founded in part on using the metaphor of roles, which describe reusable collaborations between design elements [Gamm95]. Each design element plays an identifiable role with well-defined responsibilities. For example, the Observer pattern describes a collaboration involving two roles: Subject (or Observable) and Observer. An object may be a Subject in one scenario, and an Observer in another. Or both within one scenario.
A design process and a programming language work well together when there is support for a clear translation from the design's conceptual units to those of the language. Interface is the programming language level mechanism which maps well to Role in the design domain. So introducing interfaces in Smalltalk would ease the translation from a Role-based design to implementation.
Declaring an Interface
An implementation of the Observer Pattern in Smalltalk requires that Observer is responsible to understand the message update:, while Subject should propagate change events via changed: and manage its dependents (observers) using addDependent: and removeDependent:. How can we use interfaces to implement this pattern?
We create an Observable interface by first evaluating the definition:
in the Smalltalk IDE and later adding interactively method definitions (with or without comments) for each message in the Observable's repertoire. Alternatively we can create the interface and its repertoire by evaluating:
We can now query for all classes which conform to the Observable interface either by executing: Observable conformers or by using the Interface Browser. Note that every time an interface repertoire is changed its conformers are dynamically inferred. Hence if we now execute: Object interfaces we get a collection containing the Observable interface.
And after defining the Observer interface as follows:
the definition of the class Object would now reflect the fact that in Smalltalk any Object can play both the Observer and Observable roles:
As Smalltalk is dynamically typed, type information is not used to validate the correctness of the arguments at compile time. However, using interface names when naming method arguments is a good way to convey type information. For example, the following code shows that the method update:with: takes as first argument an object that should understand the interface Observer.
Documenting code as shown by the preceding example was already possible without explicit support for interfaces. However having tangible interfaces is important because it allows the programmer to browse and identify correct definition of an interface, and validate if a given class implements an interface. Using the Interface Browser or methods such as conformers and types, the reader can further investigate the intention of those interfaces as shown in subsequent sections. Moreover, with explicit interfaces we can support the automatic generation of stub methods. Hence executing: PluggableWidget implements: Observer generates the appropriate stub methods; the methods contained in the Observer that are not already implemented by the class hierarchy.
Supporting Documentation Coherence
With the following example we show how the use of explicit and dynamic interfaces can help with program understanding and having a coherent documentation.
The Problem with Implicit Interfaces
In most current Smalltalk dialects interfaces are not a language. Instead, they are implicit within class implementations. So Smalltalk developers usually refer to an interface indirectly by saying that classes A, B, and C are "polymorphically compatible", meaning those classes understand a certain group of messages. One of the common ways to indicate the existence of such an implicit interface is to implement an isX method to return true in all classes implementing an X interface. The following example taken from VisualWorks Smalltalk illustrates such a practice.
The example exhibits the following issues:
With the benefit of explicit interfaces we can now define and link ExceptionHandler and ExceptionCreator as one interface extending the other.
Consequently, all relationships among behaviors (classes and metaclasses) and interfaces are derived dynamically as shown by figures 3 & 4.
Figure 3: ExceptionCreator definition and classes implementing it.
Now given an interface, various information can be obtained, such as:
ExceptionHandler extenders => (ExceptionCreator)
ExceptionCreator extended => (ExceptionHandler)
ExceptionCreator conformers => (Signal GenericException class)
ExceptionCreator repertoire => (#raiseSignal #new #handles: #accepts:)
Figure 4: Interface views for classes implementing ExceptionHandler.
Smalltalk is a software development environment which comes with a rich and robust library of classes. Alas, this is one of the most common complaints of newcomers to Smalltalk: there is too much out there to know (in the IDE). Where does one start? What is important to know first? This situation is not unique to Smalltalk; it is a common response which many experience when introduced to a system with average complexity.
Interfaces give us another mental navigation tool while browsing. Explicit interfaces are useful in facilitating faster learning of the Smalltalk environment. One way to do so is to supply a specific collection of interfaces to the developers to direct their exploration of a system.
Bundled with SmallInterfaces are all interface specifications described by the ANSI Smalltalk standard [Ansi]. Using those, the developer can now concentrate on exploring the classes conforming to key interfaces while viewing only the aspects of a class which pertain to a specific interface. For example (as shown in the figure below), viewing only the puttableStreamProtocol portion of Stream.
Figure 5: puttableStreamProtocol view of Stream.
One possible path of exploration is starting from the root interfaces (execute: Interface rootsOfTheWorld), and then exploring the constrained interface-view of conforming classes. We can repeatedly drill down through the children of each root or maybe sidetrack exploring other interfaces of an interesting class we meet along the way.
We now discuss the implementation choices the first author has made while designing SmallInterfaces. These choices where driven by focusing on the developer as the target audience (not the compiler), and by the desire for portability and ease of integration across all Smalltalk environments.
Interfaces as Classes
In SmallInterfaces an interface is implemented as a class, and all interfaces are direct subclasses of Interface. Consequently, an interface can be created and browsed just like any other class. That aspect makes interfaces very visible to the developer.
By choosing to implement interfaces as classes and tailoring the incremental compilation for interfaces, a developer can browse and manipulate interfaces in a familiar fashion. Since all interfaces are classes, they can be filed in and out of the image (the Smalltalk way of exporting and importing source code.). This feature solves the issue of exchanging interfaces among images and across dialects.
When an interface is defined, a class representing it is created with specific methods representing its repertoire. Hence, the following definition creates an interface with methods as shown below:
As a consequence, the body of each interface method definition is of the form:
An Implementation Note. There is a difference between the inheritance relationship between the classes that represent interfaces and the inheritance relation (extension) between interfaces via their repertoire extension. Hence, the Observer interface inherits from the class Interface at the implementation level but it does not inherit from the Interface class repertoire: the two relationships, inheritance and repertoire extension are disconnected.
Interfaces behavior - a sampler
We briefly describe the public interface defined for interfaces.
Interfaces as templates
Stub methods generation
On Integrating Interfaces with the Smalltalk Environment
Since Smalltalk is not merely a language but also a dynamic object environment which facilitates an incremental development process based on incremental compilation, every solution which extends Smalltalk with interfaces has to deal with the challenge of integrating it into the Smalltalk environment as well.
In such a live environment the relationships between classes and interfaces as well as the relationships among interfaces are dynamic. In particular, the causality of connections between classes and interfaces has to be preserved at any moment; when a class, an interface, or a method is added/changed/removed to/from the environment, all relevant interfaces and classes should be immediately affected by the event. This is paramount because of Smalltalk's dynamic nature. The major implication is that the web of relationships needs to be inferred from the actual composition of classes and interfaces in a given universe, for a given moment. For example, the addition of a message to a given interface can change the conformance of classes to that interface. Therefore, it needs to be synchronized with all currently extending interfaces and conforming behaviors.
The causal connection between interfaces and classes and between interfaces themselves is achieved by hooking to the change notification mechanism within the Smalltalk environment. Upon notification of any pertinent change, the relationships between affected classes and/or interfaces are re-synched.
Portability across Smalltalk Dialects
On top of this challenge, since there are multiple Smalltalk dialects in existence, it is desirable that such a solution is portable across Smalltalk dialects and facilitates the transport of interfaces in and out of the Smalltalk image (intra), and across Smalltalk environments (inter). To make things harder still, it is desirable that the solution does not cause any base changes; none of the base classes shape or existing methods should be touched.
Portability is achieved by concentrating on making no base changes, and by isolating the points of differences among the various Smalltalk environments, then rewriting the bridge code for each environment. Most of the points of contention were in the reflection layer which is not yet standardized across Smalltalk dialects. Since one of the major differences among Smalltalk dialects is their GUI implementation, the Interfaces Browser (the GUI portion) was written on top of the Refactory Browser, thus bypassing the GUI portability issue. As a result, environments that did not have the Refactory Browser available do not have the GUI portion available as well.
[Gott96] introduces the notion of roles in Smalltalk. However, the roles are not connected to the classes in a dynamic way. [Yell94] introduces the notion of interfaces to describe and reason about components. Object interfaces are described as a state automata and the approach is not an integration of interfaces into an object model but a proposal for a new way of describing objects. [Hail90] introduces multiple interfaces that drive the views given by an object and possible access to it. These proposed interfaces are not simply an explicit representation of the class behavior but have an extra semantics and change radically the object model. [Lamp93] introduces the idea of specialization interfaces, i.e., interfaces that describes how methods call each other so that subclass designer can reason about these calling dependencies. The very same idea was extended by Reuse Contracts [Stey96].
Dolphin Smalltalk Protocols
Dolphin Smalltalk incorporates interfaces as well, referred to as Protocols [Dolp01]. Like with SmallInterfaces, all behaviors in Dolphin can specify conformance to an interface. However, Dolphin substantially differs in its choice of interface implementation. In Dolphin, the relationship between classes and interfaces are hard coded by the developer; there is no dynamic inference. Also, every interface is an island; interfaces do not relate to each other.
Dolphin interfaces are instance-based. Because interfaces are instances, they can bare any name, therefore avoiding name collisions with classes such as Object and keywords such as nil. This is a good thing. On the other hand, they are not as visible as classes; the developer has to edit an interface using a Protocol Browser, and cannot specify argument names and comments for each of the messages. Also, interfaces cannot be referenced directly in code.
Dolphin's current implementation of interfaces does not facilitate intra- and inter-exchange of interfaces; they exist only within the context of one image. Furthermore, when a class is filed out, the knowledge of which interfaces it conforms to is lost.
SmallScript is a superset of Smalltalk [SS01]. SmallScript incorporates interfaces at the language construct level, and as such have support for them within the virtual machine. Interfaces are a part of the SmallScript optional type declaration and they can be used for multi-method discrimination.
A SmallScript interface is more than an interface in the sense we define early in this paper; a SmallScript interface is actually a dynamic mixin. It is a full-fledged class that can be instantiated, unlike in SmallInterfaces where an interface is merely a behavior specification. SmallScript interfaces provide multiple inheritance via true delegation (unlike forwarding). A behavior can conform to an interface by dynamically acquiring via aggregation a concrete interface instance. Also, interfaces and their methods are generic; classes supporting a given interface can specialize interface methods at will.
This language construct is very attractive since it allows one to compose objects via aggregation; mix and match behaviors at will while avoiding the confinement which comes with composition via inheritance. Alas, since support for it has to happen in the virtual machine level, such characteristics cannot be added to all Smalltalk in a portable fashion.
Benny Sadeh is an independent consultant who lives in the Bay Area, California. His interests are: reflective systems, metaprogramming, frameworks, Extreme Programming (XP), and doing the right thing. He can be reached at: email@example.com
Dr. Stéphane Ducasse is postdoctorant researcher at University of Bern. His interests are: reflective systems, components, design and implementation of languages and applications, and reengineering of object-oriented applications. He is co-author of the book Object-Oriented Reengineering Patterns. He is the main organizer of the European Smalltalk User Group conference. Email: firstname.lastname@example.org.
Cite this article as follows: Benny Sadeh, Stéphane Ducasse: "Adding Dynamic Interfaces to Smalltalk", in Journal of Object Technology, vol. 1, no. 1, May-June 2002, pp. 63-79 http://www.jot.fm/issues/issue_2002_05/article1