.

Design Patterns for Data Structure? 8 Dung (“Zung”) Nguyen Department of Mathematics/Computer Science Pepperdine University Malibu, CA 90263 1 / [email protected]

Synopsis stmctures, the more elegant and flexible the implementationswill be. This paper describeshow object- Design patterns provide ways to structure software oriented design patterns shape our thinking and aid us in componentsinto systemsthat are flexible, extensible, and designing data representationsto the degree of abstraction have a high degreeof reusability. The statepattern, the , we desire. By focusing on common data structuressuch as object pattern, and the are used to lists and trees, it illustrates how these patterns help narrow implement common data structures such as lists and trees. the gap between the abstract views of the data structures These ‘patterns help narrow the gap between me abstract and their concreteimplementations. The patternsalso help views of the data structures and their concrete exploit polymorphism in an effective way and keep control implementations. The smaller the gap, the higher the level structuresto a minimum Code complexity is reduced,and of abstraction.’ The more abstract, the’less complex the the overall complexity of the system becomes more coding structure. As‘ a result, algorithms are easier-~to manageable. understand and more manageable. This paper advocates teaching the above design patterns in data structures Section 2 examines lists and trees and views them as courses. -.- - special cases of container classes. They are systems of 1. _ objects that have states and that can change states dynamically. The key is to’ encapsulate the states of a 1. i Introduction- systemas classes. This calls for the . Section 3 describesin some details the state pattern and the null- Seasonedsoftware developersdo not write programs from object pattern, and applies them to partially implement the scratch. They always~lookfor patterns that~bestmatch the linked list structure. The null-object pattern is used to context of the problem they are trying to solve, and apply representthe empty state. The system behavesdifferently them to designing their software. Design patterns are in different states,as if it changesclasses dynamically. The design solutions to recurring- problems in ~software state pattern provides a ,flexible and elegant way to construction. The authors of Design Parferns [3] assign implement this behavior. Section4 completesthe design of names to the most used and proven patterns, and catalog lists and trees by incorporating the singleton pattern with them. Design patterns are often misconstruedas applicable the state pattern. Conceptually, there is only one empty only to programming in the large. In reality, they can be. state. The singleton pattern will ensure the uniquenessof applied to solving problems in programming in the small the empty state, and allow it to be shared by all data such as implementing data structures. We assume the structures of the same class.’ Section 5 concludes with a reader is familiar with the concepts of object-orientation brief description of other &sign patterns that can be and data structures. Berhapsless well known are the design applied to other data structuressuch as stacksand queues. patterns. Their ‘usefulness and their relevance are most apparent when they are presentedin the context of design We have implementedall the above data structuresin Java. problems and the solutions they offer. We use the Unified Modeling Language (UML) notation’ to representall class and state diagrams. However, due to When we teach data structures, we first describe their spacerestriction, we will be able to exhibit only a few code abstract specifications, and then proceed to show how to samplesand a few class diagrams. Interested readers can implement them. Abstraction is the computer scientist’s obtain our completeset of code and diagramsby sending e- tool to master complexity. ’ The more closely the mail to the addresslisted in the author information above. implementations map to the abstract views of the data 2. Container Classes ath Container Structures Permissionto m+e digital/hardcopies ofall or pat ofthis materialfor personalor classroomuse is grantedwithout fee providedthat the cppies ore not madeor distributedfor profit or commercialadvantage. the copy- In programming, it is often necessaryto have objects with right notice,the title ofthe publicationand its doteappear, end noticeis which one can store data, retrieve data when needed,and given that copyright is by permissionofthe ACM, Inc. To copy otherwise, to republish,to post on serversor to redistributeto lists,requires specific permissionand/or fee. ’ http://www.rational.com/uml/index.html. SIGSCE98 AtlantaGA USA Copyright 19980-89791-994-7/98/2..%5.00 336 remove data when no longer needed. Such objects are a BST is empty, it contains no data object. If a BST is not instances of what we call container classes. A container empty, it contains a data object called the root, and two class is an abstraction defined by three method& insert, distinct binary search trees called the left and the right remove, and retrieve. In Java, this abstraction can be subtrees. All the elementsin the lefr subtree are less than representedin the form of an interface as shown in Listing the root, and the root is less than all the elementsin the 1 below. right subtree. “

package containers; Container structuressuch as the abovelists and treescan be public interface IContainer implemented using arrays or dynamic memory allocation. 1 The list implementation using dynamic memory is usually Object retrieve (Object key); called a, linked list. In this paper, we focus only on /*Post: Jf there is an object associated with key then this c&ct is returned else null is returned. dynamic memory allocation. Most implementationsin this */ case use the to represent the empty structure. Object remove (Object key); Becausethe semanticsof a null pointer is too low-level to /*Post: retrieve (key) retums null, and if there & an object adequately encapsulate/ the behavior of ,an object, such associatedwithkeythenthisobjectisretumedelsenullis implementations have a high degree of code complexity, returned. and are cumbersometo use. A null pointer (or a null */ reference in Java) has no behavior and thus cannot map void insert (Object key, Object value); well to the mathematicalconcept of the empty set, which is /*Post: The pair (key, value) is stored in this c&iini5r withho an object that exists and that has behavior. We seek to duplication and in such a way that retrieve (key) returns value. narrow the gap between the conceptualview of a container */ structure and its implementation. In our convention, the 1 null pointer is to represent the non-existence of an object Listing 1: Container Interface. ( only. Emptiness and non-emptinessare simply statesof a container structure. The key here is to encapsulatestates as It is by no coincidence that the post-conditions for the classes. This is the gist of a well-known insert and the remove methodsinvolve the retrieve method. called the statepattern [3]. When the insert or remove method is carried out, the state of the container object changes and dictates what the 3. ,State Pattern’ and Dynamic Reclassification retrieve method should return. The interrelationship betweenmethods of a classoccurs quite frequently. A container structure is a system that may change its state from empty to non-empty, and vice-versa. For example,an There are basically two schemesfor organizing the objects empty container changes its state to non-empty after for storage:a linear schemeand a non-linear scheme. This insertion of an object; and when the last element of a leads to the notion of container structures. The linear container is removed, its changesits state to empty. Figure container structure is called a list. The non-linear structure 1 below diagrams the !,state transition of a container can be subclassified into many sub-types such as the structqre. various tree structure-s and hash tables. Container structure-sare abstract, For example, the list structure, or Contaiger Structure State list for short, is defined as follow.

A list is a container that can be empty‘or not empty. If a list is empty, it contains no data object. If is not empty, it contains a data object called the head, and a list object remove(k) , remove(k) [not last element] called the tail.

The abstractdefinition of a non-linear structure such as the (6$/eceB ins&&, v) binary searchtree structure, or binary searchtree for short, is similar though somewhat more involved, as in the following. remove(k) [last element]

A binary search tree (BST) is a container that can be Figure 1: State diagram for a container structure. empty or not empty. The objects it contains are totally ordered according to a relation called “is less thaS2. If For each distinct state,,the algorithms to implement the 2 The concept of totally orderedrelations is usually covered methodsdiffer. For example, the algorithm for the retrieve in a discretemathematics course. method is trivial in the empty state -it simply returns null-

331

*. .. . I .,._ e.,.. ..G.._,_.,e...- . while’it is more complicated in“the non-empty stat& The -link = n; ,: isystem thus Sbehdvesas if it changes classesdynamically. This phenomenonis called “dynamic reclassifi&ion.” The Aublic List 01 #, state pattern is,a,design solution for languagesthat do not //Post: this iist existsaa is empty. . support dyntic reclassification directly.’ This pattern can //constructprccdegoeshere... be summarizedas follow. I public void insert (Object key, Object v) { l Definetan’abstract class for the states of the-system. //Re:keyandvarenotnuU. if This abstractstate class should provide all the abstract _link.insert (this, key, v); 1 methodsfor all the concretesubclasses. 1 1 i’ Define a concrete subclassof the above abstract class //***othercon~lructorsand m&h&... : for each state of the system.Each concrete state must I 1implement iti’6wn concretemethods. abstract class AListNode { l Rkpresent’thesystem by a class containing an-instance //VisibIeonlytopackage. of a concretestate. This instancerepresents the current abstract state of the system. void insert (List 1, Object k, Object v); 4 Define method&for the system to ‘return the current //Pre:l,kandvarenotnull. state and to changestate. l Delegateall requestsmade to the system to the current //***Otherabstra~t‘~ethods~.. ’ state instance. ’ Since ’ this instance can -‘change I' ’ ” dynam&ally, the 8ystem’Cll behaveas ifit can change ” its classdynamically. class NonEmptyListNode extends AListNode ( ! :I, ,, 1 I, //Visibleonlytopackage. private Object -key;, Application of thg state ‘pattern to de&&@ a 9i&ed list private Object val;' class is straightforward. We name this class, List, and the private List -Gil; ‘ab9ract clash for list States,tiiiitNode (as in abstract list ,* node). AListNode has two concrete sub&s&s: NonFimptyListNode (Object k, ,Object v) ( EmptyListNode, and NonE~ptyListNode. The //Pre:kandvarenotnull. EmptyListNode has no data while the,NonEmptyLis&de //Post:thisnodeexistsandcontainsk, v,andanemptytail. contains a data object, and a tail, which is a List. One can ,-key = k; seehow closely this im$leinentation maps to the following val = vi portion’bf thelabstractdefinition ‘6f a list: Ifa list is empty, Itail = new List 0; I it contains no data object. If is not empty, it contaiti a I , dhta object called the head, and a list object called the tail. void insert (List 1,: Objedt k, Object v) { Thd classList contains an instanceof a concret6subclass of if h(k.equals (-key)) { " “ALis tNode. ’ Via polymorphism, it- “can be an -val = v; EmptyListNode or a NotimptyListNode at run time; In 1 order to qualify it as a container class, we add- to- the else { behavior of class List the three co&&r methods: insert, -tail.insert (k,'v); j remove, and retrieve. This design closely reflects the first 1.) i portion of the abstract list definition: A list is a container //***Othermethc&.. that can be empty or not empty. -Listing 2 below shows the 1 i j implementation of the state design pattern for linked lists in i Java. Due to spacere&iction, we only show the code for class EmptyListNode extends AListNode ( : insert. //Visible onlytopackage. void insert (List 1, Object k, Object v) { ! package containers; ! l.changeLink2 (new NonEmptyListNode (k, ; public'claq List im@&ents IContsiner VI i ! 1 I private AListNode‘_link; -//state. AListNode link (1 I , //Visible only td package. Listing 2: StateDesign Pattkrn for Linked Lists. .return._- link;~_ 1 The state design pattern, as ilhistrated in Listing 2 above, puts polymorphism to use an effective way. It distributes 'void chang&L&k2 @ListNode n) { //Change~~~t~f~ible~~topackagk. , the complexity of the algorithms for insert and remove over / , f, two disjoint concrete ~subclasses of AListNode.

338 Polymorphism via the abstractstate class will dynamically comparative disc&ion between the various select the appropriate method of the appr6priate concrete implementations. subclass to execute. The programmer need not w-rite conditionti statementsto check on the state of the system for each method invocation. As a result, the algorithms in acontext= each of the subclasseshave minimal control structure, and / List are easier to understand. The code complexity of the -link: AUstNode algorithm is reducedand becomesmuch more’manageable. +head( ) : Object +tail( ) : Ust 4. Singleton Pattern and the Empty State +ins?rt(k: Object, v: Object) and retrieve are +remove(k: Object) : ?bject In Listing 2, the classesList, AListNode, EmptyListNode, +retrIeve(k:Object):Object and NonEmptyListNode belong to package containers. +link( ) : ALlstNode +changeUnld(n: AListNode) Clients of this package can only access#List and need not L and should not know anything about the node classes. Information hiding implemented as such, protects the current state I 1 clients from any accidental misuse of the classList. It also -I shields the clients from any unforeseenm&cation of the -stately non-public classes in the package. Clients invoke the . . ALfstNode constructor List 0 to createas many empty lists as needed. i ’ {abstract} The notion of emptinessis embodied in the mathematical ~+head():Object concept of the empty set. The emptiness of a list is ~+tail( ) : Ust representednot by a null pointer, but by an EmptyListNode +insert(l: List, k: Object, v: Object) object. Ideally, this EmptyListNode object should be +remove(k: 0bject):Object unique and sharedby all empty List objects. The singleton +retrieve(k: Object) : Object pattern provides a design solution to this problem. The following code fi-agment for class EmptyListNode illustrates how the singleton pattern is implemented. anull object, singletons NonEmpt IstNode 1 class EmptyListNode extends AlistNode { private static AListNode unique = null; static AListNode singleton () { if (null == -unique) -unique = new EmptyListNode 0; return -unique; I Figure 2: Design Patterns for the linked list structure.

ClassList is a client of this singleton. Its constructor List simply the class method EmptyListNode.singleton toinitializeitselfto the empty state,as shown in the following codefragment. public List () { sub&es 1 -link = EmptyListNode.singleton 0; I parent o1 Insa The design of the linked list structure is now complete. Its NcnbnptyBSTNode class diagram is shown in figure 2 below. With a slight ‘{concrete} Mae and trivial modification, it can be reused to implement tree !te} structures. Figure 3 showsthe class diagram for the binary searchfree. There is not enough spacehere to exhibit the code and discusshow code complexity is reduced. Figures flgure 3: Deslgn P&terns for binary search trees. 4 and 5 are the designsof the binary searchtree by Berman and Duvall [2], and by Adams [l], respectively. These de-signsdo not use the patterns described in this paper. Unfortunately, space limitations do not allow a software engineering principles early, and help prepare left, right them for careersin computing. (ab%) subtrees 6. References

1. Adams, J. Knowing Your Roots: Object-OrientelI Binary Search TreesRevisited. SIGCSE Bulletin, 28, 4, December1996, pp.36-40. 2. Berman, A, Duvall, R. Thinking About Binary Search TreesIn An Object-OrientedWorld. SIGCSE Bulletin, 28,1, March 1996,pp. 185-189. 3. Gamma,E, Helm, R, Johnson,R, Vlissides, J. Design Patterns, Elements Of Reusable Object-Oriented Rgure 4: Binary sear& tree by B&rim and tiaiik Sofrware. Addison-Wesley, 1995.

_--.. _.

left, right J ,.

*ptyBST I Ncn6nptyBST {concretej ” ! {concrete} ,; , i -- --_ -. ,’ I:: I , 1 Figure’s: Bikry search tree by Adam.

5. Cqnclusion ’ ’ ^.‘I ’ Many other design patterns have application in data structures. -The above design for binary searchtrees canbe extended to-implement balanced tree structures as -well: a free can be in a- balanced or an unbalanced state. The ,! hides the details in traversing container structures. The strategypattern decouplesa queuefrom its queueing policy. A stack is simply a queue with a last-in- first-out queueing policy. The adapterpattern converts the interface of a queue to that of a stack. I i I The authors of Design Patterns write: “Designing obj-&t- oriented software is’ hard, and designing reusable object- oriented software isieven harder.!’ Taken out of context, this statementappears highly negative. In realizedit serves as the main source of motivation for the authorsto write a book on de$$ patterns to help remedy the.situation. If object-orientedprogramming is hard, teaching it is at least as hard. By introducing design patterns on a smaller scale such as using them in implementing data structures,we can teach students solid object-oriented designs and sound

340