Applying Traits to the Smalltalk Collection Classes∗
Total Page:16
File Type:pdf, Size:1020Kb
Applying Traits to the Smalltalk Collection Classes∗ Andrew P. Black Nathanael Scharli¨ Stephane´ Ducasse OGI School of Science & Software Composition Group Software Composition Group Engineering, Oregon Health and University of Bern University of Bern Science University, USA Switzerland Switzerland [email protected] [email protected] [email protected] ABSTRACT of objects. Classes also provide for reuse in two different ways: as factories, they can be used to instantiate many similar objects, Traits are a programming language technology that promote the while as superclasses, they group methods so that they can be in- reuse of methods between unrelated classes. This paper reports corporated into new subclasses. These two kinds of reuse often on a refactoring of the Smalltalk collections classes using traits. have conflicting requirements. As factories, classes must be com- The original collection classes contained much duplication of code; plete; when creating new subclasses, it is more convenient to be traits let us remove all of it. We also found places where the proto- able to incorporate incomplete fragments of behavior. cols of the collections lacked uniformity; traits allowed us to correct these non-uniformities without code duplication. Traits also make We have developed a new programming construct, which we call it possible to reuse fragments of collection code outside of the ex- a trait, to address this problem. Traits are intended as fine-grained isting hierarchy; for example, they make it easy to convert other units of code reuse. In essence, traits are first class collections of collection-like things into true collections. Our refactoring reduced methods that can be reused by classes anywhere in the inheritance the number of methods in the collection classes by approximately hierarchy. 10 per cent. More importantly, understandability maintainability The contributions of this paper are: and reusability of the code were significantly improved. • a study of the internal structure of the existing Smalltalk col- lections classes, with particular attention to the problems of Categories and Subject Descriptors code duplication, unnecessary inheritance, and method re- definition in inheritance chains; D.3.3 [Language Constructs and Features]: Classes and objects; D.2.3 [Coding Tools and Techniques]: Object-oriented program- • a report of our experience using traits to refactor the collec- ming tion classes so as to remove these problems; and • a description of the organization of the new classes that re- General Terms sulted from our refactoring. Design, Languages 2. WHAT IS THE PROBLEM? Keywords Single inheritance is a very popular programming technology, and Reuse, Mixins, Traits, Smalltalk, Collection hierarchy, Stream has been adopted widely since its introduction in Simula 67 [2]. In- classes, Refactoring, Inheritance, Multiple Inheritance heritance is also very powerful, and the basis for several major suc- cess stories, including Smalltalk and Java. But success and power should not blind us to the fact that sometimes single inheritance is 1. INTRODUCTION just not up to the task of supporting the wide range of abstractions We have long believed that classes have too many responsibilities that we expect to find in a modern object-oriented framework. in object-oriented programming [3]. In many languages, classes Let us illustrate this point with a small example. The class are the prime (or only) mechanism for the conceptual classification RectangleMorph in the Squeak dialect of Smalltalk represents a rectangular block of color that can be manipulated on the display. ∗ This research was partially supported by the National Science Foundation As such, it is a subclass of BorderedMorph, from which it inherits of the United States under awards CDA-9703218, CCR-0098323 and CCR- and reuses many methods; BorderedMorph in turn is a subclass of 0313401, and by Swiss National Foundation project 2000-067855.02. Morph. Squeak also defines a class Rectangle, which inherits from Object. However, a RectangleMorph is not a Rectangle; that is, RectangleMorph does not implement all of the protocol understood Rectangle Permission to make digital or hard copies of all or part of this work for by objects. personal or classroom use is granted without fee provided that copies are Rectangle adds 83 messages to the protocol of Object. Of these, not made or distributed for profit or commercial advantage and that copies only 13 messages are also understood by a RectangleMorph; the bear this notice and the full citation on the first page. To copy otherwise, to other 70 are missing from RectangleMorph’s protocol. We say republish, to post on servers or to redistribute to lists, requires prior specific “missing” because, to the client, a RectangleMorph clearly is a permission and/or a fee. OOPSLA’03, October 26–30, 2003, Anaheim, California, USA. Rectangle; moreover, the state of a RectangleMorph includes a field Copyright 2003 ACM 1-58113-712-5/03/0010 ...$5.00. named bounds that represents the Rectangle that it occupies. 1 sum overriding trait trait trait T U T a a x c a q a a x a b y d a r b a conflict a c ↑self i c a ↑self i composite trait definition Trait named: + #V uses: T b a y c a p trait trait d a v T+ U V a a x a a x b a y b a y c a conflict c a p d a r d a v class trait inheritance These diagrams illustrate the C T three composition operations involving traits. The ellipses var0 a a x depict the operations; the fat b a y arrows show their a inputs and outputs. The open a m c a ↑self i d a n arrow represents sub- classing. The notation a a m a composite subclass e k represents a method with definition name a and body m. C subclass: The sum operation takes two traits T and U as input; #D uses: T the result is a trait T+U that contains the union of all of var1 the non-conflicting methods. Where T and U conflict (e.g., at c), the resulting method is an explicit conflict subclass b a j marker. D d a w The overriding operation combines some explicit defi- i a ↑var1 nitions with an existing trait. In the figure, the explicit var0 var1 definitions of methods b and c override those obtained from the trait T, and the definition of d is a a x added. The resulting trait V contains no conflicts b a j because the definition of b has been overridden. c a ↑self i The inheritance operation is used to create a new d a w subclass D from an existing superclass C, an existing e a k trait T, and some new, explicitly given, definitions. Explicit definitions (e.g., of b) override those obtained a i ↑var1 from the trait; definitions in the trait (e.g., of a) override those obtained from the superclass. Figure 1: The major operations on traits: sum, overriding, and inheritance. 2 A programmer who wishes to fix this problem is faced with The overriding operation constructs a new composite Trait a number of unpleasant alternatives. One option is to copy named: #V uses: T . by extending an existing trait T with some the 70 missing methods from Rectangle and paste them into explicit local definitions. The local definitions override methods of RectangleMorph. This is a clear violation of the DRY (don’t repeat the same name in the trait T; because the local definitions are ex- yourself) principle [20]. plicit, it is always clear what is being overridden. Thus, in the Another option is to provide a conversion method figure, we see that the method for c with body p overrides the one RectangleMorph>>asRectangle1, and expect the client to re- with body ↑ self i. member to use this conversion method whenever there is a need Traits are incorporated into classes by means of an extended to send a message that a RectangleMorph does not understand. form of inheritance. In conventional inheritance, a new subclass For example, instead of saying myMorph area, the client must say B is constructed from a superclass C and from some local defini- myMorph asRectangle area. This moves a burden onto the morph’s tions of instance variables and methods. In Smaltalk we would client that we feel should be borne by the morph itself. write C subclass: #B . and then add the local definitions to B. A third option is to delegate the 70 missing methods. The sim- Our extended form of inheritance is similar; a new subclass D is plest way of doing this is to implement each of them as a one line constructed from a superclass and a trait T in addition to the lo- method that converts the receiver to a rectangle and resends the cal definitions. In Smalltalk we write C subclass: #D uses: T... message. So the area method would actually be implemented in We call D a composite class. Methods defined locally in as part RectangleMorph as follows. of the inheritance operation (such as b in figure 1) replace meth- ods obtained from the trait T. Methods defined locally (such as d) RectangleMorph>>area T a ↑ self asRectangle area and methods from (such as ) both override methods inherited from the superclass C, and can access the overridden methods us- This seems like the best choice, but it increases the size of the ob- ing super. In practice, the trait that is used to build a composite ject code and makes RectangleMorph harder to understand because class is often the sum of several more primitive traits. it adds a lot of conceptually uninteresting“noise”. Constructing all The aliasing operator @ creates a new trait by providing an addi- 70 delegation methods requires tool support; using them introduces tional name for an existing method.