353 66 4MB
English Pages 353 Year 2002
Buy the book if you really like it.
Table of Contents • • Index Design Patterns Java™ Workbook By Steven John Metsker Publisher : Addison Wesley Pub Date : March 25, 2002 ISBN : 0-201-74397-3 Pages : 496 Slots :1
Java program m ers, you now have t he resource you need t o harness t he considerable power of design pat t erns. This unique book present s exam ples, exercises, and challenges t hat will help you apply design pat t ern t heory t o real- world problem s. St eve Met sker's learn- by- doing approach helps you enhance your pract ical skills and build t he confidence you need t o use design pat t erns effect ively in m ission- crit ical applicat ions. Design Pat t erns Java( TM) Workbook feat ures t he t went y- t hree foundat ional design pat t erns int roduced in t he classic book Design Pat t erns ( Addison- Wesley, 1995) . I n t his new, hands- on workbook, t he pat t erns are organized int o five m aj or cat egories: int erfaces, responsibilit y, const ruct ion, operat ions, and ext ensions. Each cat egory begins wit h a chapt er t hat reviews and challenges your abilit y t o apply facilit ies built int o Java. These int roduct ory sect ions are followed by chapt ers t hat explain a part icular pat t ern in det ail, dem onst rat e t he pat t ern in use wit h UML diagram s and Java code, and provide program m ing problem s for you t o solve. Wit h t his book you will build expert ise in im port ant areas such as: •
Adapting domain data to Swing components
•
Creating a FACADE for Swing
•
Handling recursion in composites
•
Understanding the role of BRIDGE in Java database connectivity
•
Making the connection between Model/View/Controller and OBSERVER
•
Maintaining relational integrity with a mediator
•
Using proxies to communicate between computers
•
Letting a service provider decide which class to instantiate
•
Supporting undo operations with MEMENTO
•
Prototyping with clones
•
Using COMMAND to supply a service
•
Developing thread-safe iterators
•
Extending classes with DECORATOR and VISITOR
Solut ions t o t he design pat t ern challenges appear in t he back of t he book, so you can com pare your own work t o expert approaches. A brief guide t o UML explains t he m odeling not at ion, and an accom panying Web sit e provides all t he code exam ples from t he book. Through t he inst ruct ion and exercises offered in Design Pat t erns Java( TM) Workbook, you can fully underst and t he role of design pat t erns in Java applicat ion developm ent , and enhance your abilit y t o put design pat t erns t o work.
Copyright Praise for Design Patterns Java™ Workbook Foreword Preface Chapter 1. Introduction To Patterns Why Patterns? Why Design Patterns? Why Java? Why UML? Why a Workbook? The Organization of This Book Welcome to Oozinoz! Source Code Disclaimer Summary Part I. Interface Patterns Chapter 2. Introducing Interfaces Ordinary Interfaces Interfaces and Obligations Placing Constants in Interfaces Summary BEYOND ORDINARY INTERFACES Chapter 3. Adapter Adapting in the Presence of Foresight Class and Object Adapters Unforeseen Adaptation Recognizing Adapter Summary Chapter 4. Facade Refactoring to Facade
Facades, Utilities, and Demos Summary Chapter 5. Composite An Ordinary Composite Recursive Behavior in Composites Trees in Graph Theory Composites with Cycles Consequences of Cycles Summary Chapter 6. Bridge A Classic Example of Bridge: Drivers Refactoring to Bridge A Bridge Using the List Interface Summary
Part II. Responsibility Patterns Chapter 7. Introducing Responsibility Ordinary Responsibility Controlling Responsibility with Visibility Summary Beyond Ordinary Responsibility Chapter 8. Singleton Singleton Mechanics Singletons and Threads Recognizing Singleton Summary Chapter 9. Observer A Classic Example: Observer in Swing Model/View/Controller Maintaining an Observable Object Summary Chapter 10. Mediator A Classic Example: GUI Mediators Relational Integrity Mediators Summary Chapter 11. Proxy A Classic Example: Image Proxies Image Proxies Reconsidered Remote Proxies
Summary Chapter 12. Chain of Responsibility Varieties of Lookup Refactoring to Chain of Responsibility Anchoring a Chain Chain of Responsibility without Composite Summary Chapter 13. Flyweight Recognizing Flyweight Immutability Extracting the Immutable Part of a Flyweight Sharing Flyweights Summary
Part III. Construction Patterns Chapter 14. Introducing Construction Ordinary Construction Superclass Collaboration Collaboration within a Class Summary Beyond Ordinary Construction Chapter 15. Builder Building from a Parser Building under Constraints Building a Counteroffer Summary Chapter 16. Factory Method Recognizing Factory Method A Classic Example of Factory Method: Iterators Taking Control of Which Class to Instantiate Factory Method in Parallel Hierarchies Summary Chapter 17. Abstract Factory Abstract Factories for Families of Objects Packages and Abstract Factories Abstract Factories for Look-and-Feel Summary Chapter 18. Prototype Prototypes as Factories
Prototyping with Clones Using Object.clone() Summary Chapter 19. Memento Memento Durability Applying Memento Persisting Mementos across Sessions Using Strings as Mementos Summary
Part IV. Operation Patterns Chapter 20. Introducing Operations Operations, Methods, and Algorithms The Mechanics of Methods Exceptions in Methods Summary Beyond Ordinary Operations Chapter 21. Template Method A Classic Example of Template Method: Sorting Completing an Algorithm Template Method Hooks Refactoring to Template Method Summary Chapter 22. State Modeling States Refactoring to State Making States Constant Summary Chapter 23. Strategy Modeling Strategies Refactoring to Strategy Comparing Strategy and State Comparing Strategy and Template Method Summary Chapter 24. Command A Classic Example: Menu Commands Using Command to Supply a Service Command in Relation to Other Patterns
Summary Chapter 25. Interpreter An Interpreter Example Interpreters, Languages, and Parsers Summary
Part V. Extension Patterns Chapter 26. Introducing Extensions Reuse as an Alternative to Extension Extending by Subclassing The Liskov Substitution Principle Extending by Delegating Summary Beyond Ordinary Extension Chapter 27. Decorator A Classic Example of Decorator: Streams Function Decorators Decorating without Decorator Summary Chapter 28. Iterator Type-Safe Collections Iterating Over a Composite Thread-Safe Iterators Summary Chapter 29. Visitor Supporting Visitor Extending with Visitor Visitor Cycles Visitor Controversy Summary
Part VI. Appendixes Appendix A. Directions Get the Most from This Book Understand the Classics Weave Patterns into Your Code
Keep Learning Appendix B. Solutions Introducing Interfaces (Chapter 2) Adapter (Chapter 3) Facade (Chapter 4) Composite (Chapter 5) Bridge (Chapter 6) Introducing Responsibility (Chapter 7) Singleton (Chapter 8) Observer (Chapter 9) Mediator (Chapter 10) Proxy (Chapter 11) Chain of Responsibility (Chapter 12) Flyweight (Chapter 13) Introducing Construction (Chapter 14) Builder (Chapter 15) Factory Method (Chapter 16) Abstract Factory (Chapter 17) Prototype (Chapter 18) Memento (Chapter 19) Introducing Operations (Chapter 20) Template Method (Chapter 21) State (Chapter 22) Strategy (Chapter 23) Command (Chapter 24) Interpreter (Chapter 25) Introducing Extensions (Chapter 26) Decorator (Chapter 27) Iterator (Chapter 28) Visitor (Chapter 29) Appendix C. UML AT A GLANCE Classes Class Relationships Interfaces Objects States Glossary Bibliography
Index
Praise for Design Patterns Java™
Workbook
" An excellent book…I 'm incredibly im pressed wit h how readable it is. I underst ood every single chapt er, and I t hink any reader wit h any Java fam iliarit y would. This book is going t o be required reading in a lot of places, including m y office." —Joshua Engel " Provides a new, m ore Java- lit erat e way t o underst and t he 23 GoF pat t erns." —Bob Hanm er " This book t ranslat es Design Pat t erns int o what Java program m ers need t o know. I t is full of short , engaging program m ing and design problem s wit h solut ions—m aking it easy for program m ers t o work t hrough solut ions and really m ake pat t erns 'st ick.'" —Rebecca Wirfs- Brock " This is one excit ing book. I t 's approachable, readable, int erest ing, inst ruct ive, and j ust plain valuable. I t 'll eclipse all ot her books purport ing t o t each people t he GoF pat t erns in Java—and perhaps any ot her language." —John Vlissides
Foreword Tell m e and I forget . Teach m e and I rem em ber. I nvolve m e and I learn. —Benj am in Franklin Wit h Design Pat t erns Java™ Workbook, St eve Met sker has done som et hing t ruly am azing: He's packed a book wit h ext ensive coding exam ples and dozens of exercises t hat challenge you t o t ruly grok design pat t erns. I t uses soft ware for a fict ional com pany t hat m anufact ures and sells fireworks and put s on firework displays as an exam ple. Not only are t he coding exam ples m ore ent ert aining t han t he t ired old ATM m achine exam ples, but you'll find yourself learning obscure firework fact s as you learn design pat t erns. The book is fun as well as invit ing! And because it describes how each design pat t ern fit s in wit h and ext ends Java language const ruct s, you m ay find yourself learning m ore about Java, t oo! A pat t ern is a way of doing som et hing, a way of pursuing an int ent . A design pat t ern is a way of pursuing an int ent using obj ect t echnology: classes and t heir m et hods, inherit ance, and int erfaces. Each pat t ern has a nam e. I f you and your t eam m at es know about design pat t erns, you can work m ore effect ively—because you share a com m on vocabulary, it 's like speaking in short hand! You can discuss your int ent ions wit hout groping for t he right words. And developers who rout inely apply design pat t erns t o t heir code end up wit h code t hat is m ore flexible and easier t o read and m odify. Design pat t erns were originally described in t he book Design Pat t erns, writ t en by Erich Gam m a and his colleagues ( Addison- Wesley, 1995) . That book present s a cat alog of 23 proven design pat t erns for st ruct uring, creat ing, and m anipulat ing obj ect s. I n Design Pat t erns Java™ Workbook, St eve clearly explains each original design pat t ern from a Java program m er's perspect ive.
I f you t ake up t he challenges in t his book, you'll have plent y of opport unit y t o learn pat t erns by writ ing and ext ending exist ing code, answering quest ions t hat force you t o t hink carefully, and solving som e int erest ing design problem s. No m at t er how m uch you read about som et hing, t he best way t o really learn is t o put it t o pract ice. Rebecca Wirfs- Brock Sherwood, Oregon January 2002
Preface At OOPSLA[ 1] 2000 in Minneapolis, Minnesot a, I asked Mike Hendrickson of AddisonWesley what t ypes of books he t hought readers want ed. I was int erest ed t o hear t hat he felt t hat t here is st ill a m arket for books t o help readers underst and design pat t erns. I suggest ed t he idea of a Java workbook t hat would give readers a chance t o expand and t o exercise t heir underst anding of pat t erns. This sounded good t o Mike, and he int roduced m e t o Paul Becker, who support s Addison- Wesley's Soft ware Pat t erns series. Paul's im m ediat e response was t hat such a book " should have been writ t en five years ago." I would like t o t hank Mike and Paul for t heir init ial encouragem ent , which inspired m e t o t ake on t his t ask. [1]
OOPSLA is a conference on object-oriented programming, systems, and applications, sponsored by the Association for Computing Machinery. Since t hat init ial m eet ing, Paul has support ed m e t hroughout t he ent ire developm ent process, guiding t his book t oward publicat ion. Early on, Paul asked John Vlissides, t he Soft ware Pat t erns series edit or, for his views on t he proj ect . John's reply was t hat Paul should support t he proj ect " in all wise," inspirat ional words t hat have st ayed wit h m e t hroughout . John Vlissides is also, of course, one of t he four aut hors of Design Pat t erns. John and his coaut hors—Erich Gam m a, Ralph Johnson, and Richard Helm —produced t he work t hat is in every way t he foundat ion of t his book. I referred t o Design Pat t erns nearly every day t hat I worked on t his book and can hardly overst at e m y reliance on it . I have also relied on m any ot her exist ing books, which are list ed in t he bibliography at t he end of t his book. I n part icular, I have depended on The Unified Modeling Language User Guide ( Booch, Rum baugh, and Jacobson 1999) for it s clear explanat ions of UML. For accuracy in Java- relat ed t opics I have consult ed Java ™ in a Nut shell ( Flanagan 1999b) alm ost daily. I have also repeat edly drawn on t he insight s in Pat t erns in Java™ ( Grand 1998) and Java™ Design Pat t erns ( Cooper 2000) . During t he m ont hs t hat I was working on t his book, I also worked at a financial services inst it ut ion t hat has facilit ies in m any locat ions. As t he book em erged, I developed an inst ruct or's course t o go wit h it . I t aught t he course in Richm ond, Virginia, and m y associat es Tim Snyder and Bill Trudell t aught t he course concurrent ly at ot her locat ions. I would like t o t hank t hese inst ruct ors and t he st udent s from all t hree courses for t heir inspirat ion and t heir m any insight s. I n part icular, I would like t o t hank Srinivasarao Kat epalli, Brad Hughes, Thiaga Manian, Randy Fields, Macon Pegram , Joe Paulchell, Ron DiFrango, Rit ch Linklat er, Pat t i Richards, and Ben Lewis for t heir help and suggest ions. I would also like t o t hank m y friends Bill Wake and Gagan Kanj lia for t heir reviews of t his book in it s early st ages and Kiran Raghunat han for his help in t he lat er st ages. Finally, I 'd like t o t hank m y friend Jeff Dam ukait is for his suggest ions, part icularly his insist ence t hat I m ake t he code for t his book available t o readers. ( I t is, at oozinoz.com ) .
As t he book cam e along, Paul Becker arranged for m any excellent reviewers t o help guide it s progress. I 'd like t o t hank John Vlissides again for his reviews. I n every review, John som ehow convinced m e t hat he liked t he book while sim ult aneously point ing out scores of significant im provem ent s. I 'd like t o t hank Luke Hohm ann, Bob Hanm er, Robert Mart in, and Joshua Kerievsky for t heir help at various st ages. Each of t hem m ade t his book bet t er. I 'd like t o t hank Joshua Engel, who has an am azing abilit y t o blend sharp insight wit h a gent le t ouch. Finally, I 'd like t o t hank Rebecca Wirfs- Brock, who had m any great suggest ions, including com plet ely reorganizing t he book. I had init ially not t aken care t o put im port ant but underst andable pat t erns up front . The book is m uch st ronger now because of Rebecca's advice and t he help of all t he book's reviewers. St eve Met sker ( St eve.Met sker@acm .org)
Chapter 1. Introduction To Patterns This book is for developers who know Java and who have had som e exposure t o t he book Design Pat t erns ( Gam m a et al. 1995) . The prem ise of t his book is t hat you want t o • • •
Deepen your understanding of the patterns that Design Patterns describes Build confidence in your ability to recognize these patterns Strengthen your ability to apply these patterns in your own Java programs
Why Patterns? A pa t t e r n is a way of doing som et hing, or a way of pursuing an int ent . This idea applies t o cooking, m aking fireworks, developing soft ware, and t o any ot her craft . I n any craft t hat is m at ure or t hat is st art ing t o m at ure, you can find com m on, effect ive m et hods for achieving aim s and solving problem s in various cont ext s. The com m unit y of people who pract ice a craft usually invent j argon t hat helps t hem t alk about t heir craft . This j argon oft en refers t o pat t erns, or st andardized ways of achieving cert ain aim s. Writ ers docum ent t hese pat t erns, helping t o st andardize t he j argon. Writ ers also ensure t hat t he accum ulat ed wisdom of a craft is available t o fut ure generat ions of pract it ioners. Christ opher Alexander was one of t he first writ ers t o encapsulat e a craft 's best pract ices by docum ent ing it s pat t erns. His work relat es t o archit ect ure—of buildings, not soft ware. A Pat t ern Language: Towns, Buildings, Const ruct ion ( Alexander, I shikawa, and Silverst ein 1977) provides pat t erns for archit ect ing successful buildings and t owns. Alexander's writ ing is powerful and has influenced t he soft ware com m unit y, part ially because of t he way he looks at int ent . You m ight st at e t he int ent of archit ect ural pat t erns as " t o design buildings." But Alexander m akes it clear t hat t he int ent of archit ect ural pat t erns is t o serve and t o inspire t he people who will occupy buildings and t owns. Alexander's work showed t hat pat t erns are an excellent way t o capt ure and t o convey t he wisdom of a craft . He also est ablished t hat properly perceiving and docum ent ing t he int ent of a craft is a crit ical, philosophical, and elusive challenge. The soft ware com m unit y has resonat ed wit h Alexander's approach and has creat ed m any books t hat docum ent pat t erns of soft ware developm ent . These books record best pract ices for soft ware process, soft ware analysis, and high- level and class- level design. Table 1.1 list s books t hat record best pract ices in various aspect s of soft ware developm ent . This list of books is not com prehensive, and new books appear every year.
I f you are choosing a book about pat t erns t o read you should spend som e t im e reading reviews of available books and t ry t o select t he book t hat will help you t he m ost .
Why Design Patterns? A de sign pa t t e r n is a pat t ern—a way t o pursue an int ent —t hat uses classes and t heir m et hods in an obj ect - orient ed language. Developers oft en st art t hinking about design aft er learning a program m ing language and writ ing code for a while. You m ight not ice t hat som eone else's code seem s sim pler and works bet t er t han yours does, and you m ight wonder how t hat person achieves t his sim plicit y. Design pat t erns are a level up from code and t ypically show how t o achieve a goal, using one t o t en classes. Ot her people have figured out how t o program effect ively in obj ect - orient ed languages. I f you want t o becom e a powerful Java program m er, you should st udy design pat t erns, especially t hose in Design Pat t erns. Table 1.1. Books Conveying Software Development Wisdom in the Form of Patterns
PATTERN CATEGORY SOFTWARE PROCESS
OBJECT MODELING
AUTHORS /EDITORS Process Patterns: Building Large-Scale Scott W. Systems Using Object Technology Ambler More Process Patterns: Delivering Large-Scale Scott W. Systems Using Object Technology Ambler Analysis Patterns: Reusable Object Models Martin Fowler TITLE
Object Models: Strategies, Patterns and Applications
Pet er Coad Mark Mayfield David Nort h
ARCHITECTURE
CORBA Design Patterns
Thom as J. Mowbray Raphael C. Malveau
Core J2EE™ Patterns: Best Practices and Design Strategies
Deepak Alur John Crupi Dan Malks
Pattern-Oriented Software Architecture, Volume Frank 1: A System of Patterns Buschm ann Regine Meunier Hans Rohnert Pet er Som m erlad Michael St al
Pattern-Oriented Software Architecture, Volume Douglas
2: Patterns for Concurrent and Networked Objects
Schm idt Michael St al Hans Rohnert Frank Buschm ann
DESIGN
AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis
William J. Brown Raphael C. Malveau Hays W. McCorm ick I I I Thom as J. Mowbray
Applying UML and Patterns, Second Edition Concurrent Programming in Java™, Second Edition: Design Principles and Patterns Design Patterns
Craig Larman Doug Lea Erich Gam m a Richard Helm Ralph Johnson John Vlissides
Design Patterns for Object-Oriented Software Development Pattern Hatching: Design Patterns Applied SanFranciso™ Design Patterns
Wolfgang Pree John Vlissides Jam es Carey Brent Carlson Tim Graser
SMALLTALK ORIENTED
The Design Patterns Smalltalk Companion
Sherm an R. Alpert Kyle Brown Bobby Woolf
JAVA ORIENTED
COMPENDIA
Smalltalk Best Practice Patterns Java™ Design Patterns: A Tutorial Patterns in Java™, Volume 1 The Pattern Almanac 2000 Pattern Languages of Program Design
Kent Beck James W. Cooper Mark Grand Linda Rising Jam es O. Coplien Douglas C.
Schm idt
Pattern Languages of Program Design 2
John M. Vlissides Jam es O. Coplien Norm an Kert h
Pattern Languages of Program Design 3
Robert C. Mart in Dirk Riehle Frank Buschm ann
Pattern Languages of Program Design 4
Neil Harrison Brian Foot e Hans Rohnert
Design Pat t erns describes 23 design pat t erns—t hat is, 23 ways of pursuing an int ent , using classes and obj ect s in an obj ect - orient ed language. These are probably not absolut ely t he m ost useful 23 design pat t erns t o know. On t he ot her hand, t hese pat t erns are probably am ong t he 100 m ost useful pat t erns. Unfort unat ely, no set of crit eria est ablishes t he value of a pat t ern, and so t he ident it y of t he ot her 77 pat t erns in t he t op 100 is a m yst ery. Fort unat ely, t he aut hors of Design Pat t erns chose well, and t he pat t erns t hey docum ent are cert ainly wort h learning.
GoF You m ay have not ed t he pot ent ial confusion bet ween design pat t erns t he t opic and Design Pat t erns t he book. To dist inguish bet ween t he t opic and t he book t it le, m any speakers and som e writ ers refer t o t he book as t he " Gang of Four" book or t he " GoF" book, referring t o t he num ber of it s aut hors. I n print , t his dist inct ion is not so confusing. Accordingly, t his book avoids using t he t erm " GoF."
Why Java? This book gives it s exam ples in Java because Java is popular and im port ant and will probably be t he basis of fut ure generat ions of com put er languages. The popularit y of a language is recursive. Developers invest t heir learning cycles in t echnology t hat t hey believe will last for at least a few years. The m ore popular a t echnology becom es, t he m ore people want t o learn it , and t he m ore popular it becom es. This can lead t o h ype , or overexcit em ent about a t echnology's pot ent ial value. But Java is m ore t han hype. At a superficial level, Java is im port ant because it is popular, but Java is also popular because it is a st ride forward in com put er languages. Java is a consolida t ion la ngua ge , having absorbed t he st rengt hs and discarded t he weaknesses of it s predecessors. This consolidat ion has fueled Java's popularit y and helps ensure t hat fut ure languages will evolve from Java rat her t han depart radically from it . Your invest m ent in Java will alm ost surely yield value in any language t hat supplant s Java.
The pat t erns in Design Pat t erns apply t o Java because, like Sm allt alk and C+ + , Java follows a class/ inst ance paradigm . Java is m uch m ore sim ilar t o Sm allt alk and C+ + t han it is t o, say, Prolog or Self. Alt hough com pet ing paradigm s are im port ant , t he class/ inst ance paradigm appears t o be t he m ost pract ical next st ep in applied com put ing. This book uses Java because of Java's popularit y and because Java appears t o lie along t he evolut ionary pat h of languages t hat we will use for decades ahead.
Why UML? Where challenges have solut ions in code, t his book uses Java. But m any of t he challenges ask you t o draw a diagram of how classes, packages, and ot her elem ent s relat e. You can use any not at ion you like, but t his book uses Unifie d M ode ling La ngua ge ( UM L) not at ion. Even if you are fam iliar wit h UML, it is a good idea t o have a reference handy. Two good choices are The UML User Guide ( Booch, Rum baugh, and Jacobson 1999) , and UML Dist illed ( Fowler wit h Scot t 2000) . The bare m inim um of UML knowledge you need for t his book is provided in Appendix C, UML at a Glance, page 441.
Why a Workbook? No m at t er how m uch you read about doing som et hing, you won't feel as t hough you know it unt il you do it . This is t rue part ially because unt il you exercise t he knowledge you gain from a book, you won't encount er subt let ies, and you won't grapple wit h alt ernat ive approaches. You won't feel confident about design pat t erns unt il you apply t hem t o som e real challenges. The problem wit h learning t hrough experience is t hat you can do a lot of dam age as you learn. You can't apply pat t erns in product ion code before you are confident in your own skills. But you need t o st art applying pat t erns t o gain confidence. What a conundrum ! The solut ion is t o pract ice on exam ple problem s where m ist akes are valuable but painless. Each chapt er in t his w or k book begins wit h a short int roduct ion and t hen set s up a series of challenges for you t o solve. Aft er you com e up wit h a solut ion, you can com pare your solut ion t o one given in Appendix B, Solut ions, st art ing on page 359. The solut ion in t he book m ay t ake a different slant from your solut ion or m ay provide you wit h som e ot her insight . You probably can't go overboard in how hard you work t o com e up wit h answers t o t he challenges in t his book. I f you consult ot her books, work wit h a colleague, and writ e sam ple code t o check out your solut ion, t errific! You will never regret invest ing your t im e and energy in learning how t o apply design pat t erns. A danger lurks in t he solut ions t hat t his book provides. I f you flip t o t he solut ion im m ediat ely aft er reading a challenge, you will not gain m uch from t his book. The solut ions in t his book can do you m ore harm t han good if you don't first creat e your own solut ions.
The Organization of This Book There are m any ways t o organize and t o cat egorize pat t erns. You m ight organize t hem according t o sim ilarit ies in st ruct ure, or you m ight follow t he order in Design Pat t erns. But t he m ost im port ant aspect of any pat t ern is it s int ent , t hat is, t he pot ent ial value of applying t he pat t ern. This book organizes t he 23 pat t erns of Design Pat t erns according t o t heir int ent . Having decided t o organize pat t erns by int ent raises t he quest ion of how t o cat egorize int ent . This book adopt s t he not ion t hat t he int ent of a design pat t ern is usually easily
expressed as t he need t o go beyond t he ordinary facilit ies t hat are built int o Java. For exam ple, Java has plent iful support for defining t he int erfaces t hat a class im plem ent s. But if you want t o adapt a class's int erface t o m eet t he needs of a client , you need t o apply t he ADAPTER pat t ern. The int ent of t he ADAPTER pat t ern goes beyond t he int erface facilit ies built int o Java. This book places design pat t ern int ent in five cat egories, as follows: •
Interfaces
•
Responsibility
•
Construction
•
Operations
•
Extensions
These five cat egories account for five part s of t his book. Each part begins wit h a chapt er t hat discusses and present s challenges relat ed t o feat ures built int o Java. For exam ple, Part I , I nt erface Pat t erns, begins wit h a chapt er on ordinary Java int erfaces. That chapt er will challenge your underst anding of t he Java int erface const ruct , especially in com parison t o abst ract classes. The rem aining chapt ers of Part I address pat t erns whose prim ary int ent involves t he definit ion of an int erface—t he set of m et hods t hat a client can call from a service provider. Each of t hese pat t erns addresses a need t hat cannot be addressed solely wit h Java int erfaces. Table 1.2. Categorization of Patterns by Intent
INTENT PATTERNS INTERFACES ADAPTER, FACADE, COMPOSITE, BRIDGE RESPONSIBILITY SINGLETON, OBSERVER, MEDIATOR, PROXY, CHAIN OF RESPONSIBILITY, FLYWEIGHT CONSTRUCTION BUILDER, FACTORY METHOD, ABSTRACT FACTORY, PROTOTYPE, MEMENTO OPERATIONS TEMPLATE METHOD, STATE, STRATEGY, COMMAND, INTERPRETER EXTENSIONS DECORATOR, ITERATOR, VISITOR Cat egorizing pat t erns by int ent does not m ean t hat each pat t ern support only one t ype of int ent . A pat t ern t hat support s m ore t han one t ype of int ent appears as a full chapt er in t he first part t o which it applies and get s a brief m ent ion in subsequent sect ions. Table 1.2 shows t he cat egorizat ion behind t he organizat ion of t his book. I hope t hat you will quest ion t he cat egorizat ion in Table 1.2. Do you agree t hat SI NGLETON is about responsibilit y, not const ruct ion? Do you t hink t hat COMPOSI TE is an int erface pat t ern? Cat egorizing pat t erns is som ewhat subj ect ive. But I hope t hat you will agree t hat t hinking about t he int ent behind pat t erns and t hinking about how you will apply pat t erns are very useful exercises.
Welcome to Oozinoz! The challenges in t his book all cit e exam ples from Ooz inoz , a fict ional com pany t hat m anufact ures and sells fireworks and put s on fireworks displays. ( Oozinoz t akes it s nam e from t he sounds heard at Oozinoz exhibit ions.) The current code base at Oozinoz is
pret t y well designed, but m any challenges rem ain for you t o m ake t he code st ronger by applying design pat t erns.
Source Code Disclaimer The source code used in t his book is available at www.oozinoz.com . The code is free. You m ay use it as you wish, wit h t he sole rest rict ion t hat you m ay not claim t hat you wrot e it . On t he ot her hand, neit her I nor t he publisher of t his book w arrant t he code t o be useful for any part icular purpose. I f you use t he oozinoz code, I hope t hat you will t horoughly t est t hat it works properly wit h your applicat ion. And if you find a defect in m y code, please let m e know! I can be cont act ed at St eve.Met sker@acm .org.
Summary Pat t erns are dist illat ions of accum ulat ed wisdom , providing a st andard j argon and nam ing t he concept s t hat experienced pract it ioners apply. The pat t erns in Design Pat t erns are am ong t he m ost useful class- level pat t erns and are cert ainly wort h learning. This book com plem ent s Design Pat t erns providing challenges t o exercise your underst anding of t he pat t erns. This book uses Java in it s exam ples and challenges because of Java's popularit y and it s fut ure prospect s. By working t hrough t he challenges in t his book, you will learn t o recognize and t o apply a large port ion of t he accum ulat ed wisdom of t he soft ware com m unit y.
Part I: Interface Patterns Chapter 2. Introducing Interfaces Chapter 3. Adapter Chapter 4. Facade Chapter 5. Composite Chapter 6. Bridge
Chapter 2. Introducing Interfaces Speaking abst ract ly, a class's int e r fa ce is t he collect ion of m et hods and fields t hat a class perm it s obj ect s of ot her classes t o access. This int erface usually represent s a com m it m ent t hat t he m et hods will perform t he operat ion im plied by t heir nam es and as specified by code com m ent s and ot her docum ent at ion. A class's im ple m e n t a t ion is t he code t hat lies wit hin it s m et hods. Java elevat es t he not ion of int erface t o be a separat e const ruct , expressly separat ing int erface—what an obj ect m ust do—from im plem ent at ion—how an obj ect fulfills t his com m it m ent . Java int erfaces allow several classes t o provide t he sam e funct ionalit y, and t hey open t he possibilit y t hat a class can im plem ent m ore t han one int erface. The Java int erface const ruct is a powerful t ool wort h st udying in it s own right , but your design int ent will som et im es go beyond t he sim ple definit ion of an int erface. For exam ple, you m ight use an int erface t o adapt a class's int erface t o m eet a client 's needs, applying t he ADAPTER pat t ern. You m ight also creat e an int erface t o a collect ion of classes, applying t he FACADE pat t ern. I n t his case, you creat e a new int erface by creat ing a new class rat her t han a new int erface. I n t hese circum st ances and ot hers you can apply design pat t erns t o go beyond t he ordinary use of int erfaces.
Ordinary Interfaces Design Pat t erns ( Gam m a et al. 1990) frequent ly m ent ions t he use of abst ract classes but does not describe t he use of int erfaces. The reason is t hat t he languages C+ + and Sm allt alk, which Design Pat t erns uses for it s exam ples, do not have an int erface const ruct . This om ission has a m inor im pact on t he ut ilit y of t he book for Java developers, because Java int erfaces are quit e sim ilar t o abst ract classes.
CHALLENGE 2.1
Writ e down t hree differences bet ween abst ract classes and int erfaces in Java.
The basic definit ion of Java int erfaces is not com plex. However, you should be aware of a few subt le point s when using t hem . Consider t he definit ion of an Oozinoz int erface t hat rocket sim ulat ion classes m ust im plem ent . Engineers design m any different rocket s, including solid- and liquid- fueled rocket s, wit h com plet ely different ballist ics. Regardless of how a rocket is com posed, a sim ulat ion for t he rocket m ust provide figures for t he rocket 's expect ed t hrust and a poge e , or t he great est height t he rocket will achieve. Here is t he exact code, m inus t he code com m ent s, t hat Oozinoz uses t o define t he rocket sim ulat ion int erface:
package com. oozi noz. si mul at i on; i mpor t com. oozi noz. uni t s. *; i nt er f ace Rocket Si m { abst r act Lengt h apogee( ) ; publ i c For ce t hr ust ( ) ; } Classes t hat provide rocket sim ulat ions im plem ent t he RocketSim int erface. A rocket 's apogee and t hrust are quant it ies t hat com bine a m agnit ude wit h t he physical dim ensions of lengt h and force, respect ively. ( Chapt er 3, Adapt er, has m ore inform at ion about t he units package, on page 24.
CHALLENGE 2.2
Which of t he following st at em ent s are t rue?
A. Both methods of the RocketSim interface are abstract, although only apogee() declares this explicitly. B. Both methods of the interface are public, although only thrust() declares this explicitly. C. All interfaces are public, so RocketSim is public, although it does not declare this explicitly. D. It is possible to create another interface, say, RocketSimSolid, that
interface, say, RocketSimSolid, that extends RocketSim. E. Every interface must have at least one method. F. An interface can declare instance fields that an implementing class must also declare. G. Although you can't instantiate an interface, an interface definition can declare constructor methods that require an implementing class to provide constructors with given signatures.
Interfaces and Obligations A developer who creat es a class t hat im plem ent s RocketSim is responsible for writ ing apogee() and thrust() m et hods t hat ret urn m easures of a rocket 's perform ance. I n ot her words, t he developer m ust fulfill t he cont ract im plied by t he m et hod nam es and t he code com m ent s. Som et im es, t he m et hods t hat an int erface designat es do not carry an obligat ion t o perform a service for t he caller. I n som e cases, t he im plem ent ing class can even ignore t he call, im plem ent ing a m et hod wit h no body what soever.
CHALLENGE 2.3
Give an exam ple of an int erface wit h m et hods t hat do not im ply responsibilit y on t he part of t he im plem ent ing class t o t ake act ion on behalf of t he caller or t o ret urn a value.
I f you creat e an int erface t hat specifies a collect ion of not ificat ion m et hods, you should consider also supplying a st ub—a class t hat im plem ent s t he int erface wit h m et hods t hat do not hing. Developers can subclass t he st ub, overriding only t hose m et hods in t he int erface t hat are im port ant t o t heir applicat ion. The WindowAdapter class in java.awt.event is an exam ple of such a class, as Figure 2.1 shows. ( For a whirlwind int roduct ion t o UML, see Appendix C, UML at a Glance, on page 441.) The WindowAdapter class im plem ent s all t he m et hods in t he WindowListener int erface, but t he im plem ent at ions are all em pt y; t he m et hods cont ain no st at em ent s. Figure 2.1. The WindowAdapter class makes it easy to register for window events while ignoring any events you are not interested in.
CHALLENGE 2.4
What is t he value of a st ub class like WindowAdapter, com posed of m et hods t hat do not hing? ( Writ ing out your answer will give you pract ice at refining and art iculat ing your posit ion.)
Placing Constants in Interfaces I nt erfaces and classes can also cooperat e when it com es t o defining const ant s. A const a nt is a field t hat is st at ic and final. Consider t he definit ion of t he classificat ion of fireworks. I n t he Unit ed St at es, federal law defines t wo prim ary classificat ions, wit h each t ype of fir e w or k classified as eit her a consum er or a display t ype of firework. A display firework, any t ype of firework t hat is not a consum er firework, m ay be used only by licensed operat ors. I n federal law, t he consum er classificat ion defines fireworks t hat st at es m ay allow ordinary cit izens t o purchase and t o use. The consum er class is surprisingly large at t he federal level, including rocket s and aerial shells. St at e laws specify which consum er fireworks, if any, are legal t o purchase and t o operat e. Developers at Oozinoz use Classification const ant s t o t rack whet her a firework is federally m andat ed t o be sold only t o licensed operat ors ( see Figure 2.2) . Figure 2.2. A Firework object has a classification that reflects whether Oozinoz can sell the firework to unlicensed operators.
The nam es of t he Firework_1 and t he Classification_1 classes each end wit h an underscore and a version num ber because we will soon r e fa ct or t heir code int o new versions. When you refact or code, you should not norm ally include versioning inform at ion in class nam es. Rat her, you should use a configurat ion m anagem ent syst em t o keep t rack of versions of your code. However, t wo versions of t he Classification class appear sim ult aneously in t his book, and t he nam ing schem e helps t o different iat e t hese versions. The Classification_1 class set s up t wo const ant s t hat define a firework's classificat ion:
package com. oozi noz. f i r ewor ks; publ i c cl ass Cl assi f i cat i on_1 { publ i c st at i c f i nal Cl assi f i cat i on_1 CONSUMER = new Cl assi f i cat i on_1( ) ; publ i c st at i c f i nal Cl assi f i cat i on_1 DI SPLAY = new Cl assi f i cat i on_1( ) ;
pr i vat e Cl assi f i cat i on_1( ) { } } A t ypical m et hod t hat checks t he classificat ion const ant s looks like t his:
publ i c voi d secur eOr der ( Fi r ewor k_1 f / *, et c. */ ) { //. . . i f ( f . cl assi f i cat i on == Cl assi f i cat i on_1. DI SPLAY) { / / i ssue war ni ng } el se { / / pr oceed } } You can m ake t he Oozinoz code a lit t le cleaner if you m ove t he classificat ion const ant s t o an int erface and use t he Classification class solely for t ype checking. ( The nam e of t he Classification class does not end wit h a version num ber, indicat ing t hat we won't refact or t his class any furt her.) I f you m ove t he const ant s t o an int erface, you have t o relax t he visibilit y of t he Classification const ruct or:
package com. oozi noz. f i r ewor ks; publ i c cl ass Cl assi f i cat i on { pr ot ect ed Cl assi f i cat i on( ) { } } publ i c i nt er f ace Cl assi f i cat i onConst ant s { st at i c f i nal Cl assi f i cat i on CONSUMER = new Cl assi f i cat i on( ) ; st at i c f i nal Cl assi f i cat i on DI SPLAY = new Cl assi f i cat i on( ) ; } Figure 2.3 shows t he new relat ionship bet ween ClassificationConstants and Classification. I n t his arrangem ent , t he Classification class has no purpose ot her t han t o provide t ype checking for param et ers of t he Classification t ype. But t he ClassificationConstants int erface let s you m ake t he secureOrder( ) code m ore readable. Figure 2.3. The ClassificationConstants interface defines the two classifications of fireworks that U.S. federal law recognizes.
CHALLENGE 2.5
How can you change t he secureOrder() m et hod and it s class t o t ake advant age of t he ClassificationConstants int erface?
A Java int erface is essent ially a purely abst ract class, wit h t he provision t hat a class can im plem ent m ult iple int erfaces. I f you t hink of it in t his light , it is logical t hat a class " inherit s" t he const ant s defined in any int erface t he class im plem ent s.
Summary The basic purpose of a Java int erface is t o declare a set of m et hods t hat a class im plem ent s. This usually im plies t hat t he class provides t he services t hat t he m et hod nam es suggest . An except ion t o t his responsibilit y occurs when t he int erface let s an obj ect regist er for event not ificat ion. I n t his sit uat ion, t he client of t he int erface bears responsibilit y for calling t he int erface m et hods when t he event s im plied by t he m et hod nam es occur. When you creat e a regist rat ion int erface, it is useful t o pair t he int erface wit h an abst ract class t hat provides em pt y im plem ent at ions of t he int erface m et hods, sim plifying regist rat ion. I nt erfaces and classes can also collaborat e in t he use of const ant s, wit h int erfaces providing bet t er readabilit y.
BEYOND ORDINARY INTERFACES You can sim plify and st rengt hen your designs wit h appropriat e applicat ion of Java int erfaces. Som et im es, t hough, t he design of an int erface has t o go beyond t he ordinary definit ion and use of an int erface.
If you intend to • • • •
Adapt a class's interface to match the interface a client expects Provide a simple interface into a collection of classes Define an interface that applies to individual objects and groups of objects Decouple an abstraction from its implementation so that the two can vary independently
Apply the pattern Adapt er( Chapt er 3)
Facade( Chapt er 4)
Com posit e( Chapt er 5) Bridge( Chapt er 6)
The int ent of each design pat t ern is t o solve a problem in a cont ext . I nt erface- orient ed pat t erns address cont ext s in which you need t o define or t o redefine access t o t he m et hods of a class or a group of classes. For exam ple, when you have a class t hat perform s a service you need but wit h m et hod nam es t hat do not m at ch a client 's expect at ions, you can apply t he ADAPTER pat t ern.
Chapter 3. Adapter As a soft ware developer, you writ e classes whose m et hods are called by client s. You m ay be able t o cont rol t he int erface t hat t he client expect s, but oft en you will not be able t o do so. The m ost com m on exam ple of an expect at ion t hat you m ust frequent ly fulfill is t he im plem ent at ion of t he main() m et hod t hat t he Java int erpret er expect s. Services such as Swing also oft en include feat ures t hat require you t o support a predefined int erface. When you need t o im plem ent an expect ed int erface, you m ay find t hat an exist ing class perform s t he services a client needs but wit h different m et hod nam es. You can use t he exist ing class t o m eet t he client 's needs by applying t he ADAPTER pat t ern. The int ent of ADAPTER is t o provide t he int erface a client expect s, using t he services of a class wit h a different int erface.
Adapting in the Presence of Foresight When you need t o adapt your code t o m eet a client 's needs, you m ay find t hat t he client planned for such circum st ances. This is evident when t he client 's developers provide a Java int erface t hat defines t he services t he client code needs, as in t he exam ple shown in Figure 3.1. A client class in a package m akes calls t o a service() m et hod t hat is declared in a Java int erface. Suppose t hat you have found an exist ing class wit h a m et hod t hat can fulfill t he client 's needs, but t he nam e of t he m et hod is usefulMethod(). You can adapt t he exist ing class t o m eet t he client 's needs by writ ing a class t hat ext ends ExistingClass, im plem ent s ThoughtfulInterface, and overrides service() so t hat it delegat es it s request s t o usefulMethod(). Figure 3.1. When a developer of client code thoughtfully defines the client's needs, your mission is to adapt the interface to an existing class.
Suppose t hat you are working wit h a rocket sim ulat ion program at Oozinoz, and you want t o include an aerial shell as a " rocket ." An aerial shell is a firework t hat is fired from a m or t a r , or t ube, and explodes m idflight , ej ect ing and ignit ing a collect ion of st ars. A st a r is a com pressed pellet of a bright ly burning, explosive chem ical com pound. The m ain difference bet ween shells and rocket s is t hat shells have a lift ing charge and no subsequent t hrust , w hereas rocket s have a t hrust t hat is fairly cont inuous t hrough m idflight . The developer of t he rocket sim ulat ion has t hought fully provided an int erface t hat defines t he behavior t he sim ulat ion requires from a rocket :
publ i c i nt er f ace Rocket Si m { abst r act Lengt h apogee( ) ; publ i c For ce t hr ust ( ) ; } This int erface is now public, unlike t he version in Chapt er 2, I nt roducing I nt erfaces. The sidebar Unit s of Measure on page 24 describes how Oozinoz m odels physical quant it ies, such as lengt h and force. A key at t ribut e of a shell is it s m uzzle velocit y—t he speed at which t he shell leaves t he m ort ar it fires from . Given t he m uzzle velocit y, you can calculat e t he apogee of t he shell. ( To learn about t he chem ist ry and physics of fireworks, I recom m end reading The Chem ist ry of Fireworks ( Russell 2000) .) The m uzzle velocit y also let s you calculat e t he lift ing charge force, so you can m odel t hrust as an init ial spike t hat quickly drops t o zero.
CHALLENGE 3.1
Com plet e t he class diagram in Figure 3.2 t o show t he design of a ShellAsRocket class t hat let s a Shell obj ect part icipat e in a sim ulat ion as a RocketSim obj ect . Figure 3 2
You can adapt a shell's methods to meet the needs of a rocket
Figure 3.2. You can adapt a shell's methods to meet the needs of a rocket simulation.
Units of Measure The classes and int erfaces at Oozinoz t o prevent unit represent everyt hing from developers have creat ed a
in t he com.oozinoz.units package help developers conversion errors. Rat her t han using double t o inches of lengt h t o pounds of force, t he Oozinoz sophist icat ed obj ect m odel for m easurem ent s.
A m e a su r e is a com binat ion of a m agnit ude and a dim ension. A dim e nsion is an aggregat ion of a m easure's lengt h, m ass, and t im e exponent s. For exam ple, a volum e, such as one ft 3 , is t hree dim ensional in lengt h. An accelerat ion of 9.8 m et ers/ second 2 has a lengt h dim ension of 1 and a t im e dim ension of –2. The units package m odels com m only used dim ensions, as Figure 3.3 shows. Not e t hat t he subclasses of Measure are dim ensions, such as Length and Force, not unit s, such as inches and pounds. Figure 3.3. Oozinoz models units as instances of the Measure class, whose subclasses represent particular physical dimensions.
A unit is a st andard m easure—a st andard m agnit ude of a given dim ension. For exam ple, a cubic inch is a unit of volum e, and a pound is a unit of force. The UnitConstants int erface supplies unit s, and t he Measure hierarchy supplies dim ensions for t ype checking. For exam ple, t he UnitConstants int erface supplies a CUBIC_INCH const ant whose t ype is Volume. You can const ruct new inst ances of Measure by building from t he unit s available in t he UnitConstants int erface. For exam ple, suppose t hat t he cross sect ion of a rocket is 0.75 square inches.
Ar ea a = ( Ar ea) I NCH. t i mes( I NCH) . t i mes( . 75) ; The times() m et hod inst ant iat es whichever subclass of Measure reflect s t he dim ension of t he new unit . The ret urn t ype of t his m et hod is Measure, but you can cast t he result t o t he m ore specific class t hat represent s t he m easure's dim ension. To convert a m easure t o a quant it y of a specific unit , divide t he m easure by t he unit . For exam ple:
package com. oozi noz. uni t s; publ i c cl ass ShowConver si on i mpl ement s Uni t Const ant s { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Vol ume v = ( Vol ume) QUART. t i mes( 3) ; showVol ume( v) ; } publ i c st at i c voi d showVol ume( Vol ume v) { Syst em. out . pr i nt l n( "I see ") ; Syst em. out . pr i nt l n( "\ t " + v. di vi de( GALLON) + " gal l on( s) , ") ; Syst em. out . pr i nt l n( "\ t " + v. di vi de( CUBI C_I NCH) + " cubi c i nch( es) , ") ;
Syst em. out . pr i nt l n( "\ t " + v. di vi de( MI LLI LI TER) + " mi l l i l i t er ( s) . ") ; } } This program print s out :
I see: 0. 75 gal l on( s) , 173. 2 cubi c i nch( es) , 2839. 0 mi l l i l i t er ( s) . Many of t he exam ples ( and som e of t he challenges) t hat lie ahead use t he UnitConstants unit s t o creat e and t o apply various fireworks m easurem ent s.
Class and Object Adapters The designs in Figures 3.1 and 3.2 are class adapt ers, which adapt t hrough subclassing. When you need t o apply ADAPTER, you m ay not be able t o subclass t he class whose m et hods you need t o adapt . I n som e cases, you m ay need t o creat e an adapt er t hat adapt s inform at ion from m ore t han one obj ect . You m ay also find t hat t he int erface you need t o adapt t o is not a Java int erface but rat her an abst ract class t hat you m ust subclass. I n such cases, you need t o creat e an obj ect adapt er—an adapt er t hat uses delegat ion rat her t han subclassing. A good exam ple of a class t hat requires you t o writ e an obj ect adapt er is t he JTable class in javax.swing. This class creat es a GUI ( gr a phica l use r int e r fa ce ) t able com ponent filled wit h t he inform at ion t hat your adapt er feeds t o it . To display dat a from your dom ain, JTable provides const ruct ors t hat accept an inst ance of t he TableModel defined in javax.swing.table and shown in Figure 3.4. Figure 3.4. The JTable class is a Swing component that lifts data from an implementation of TableModel into a GUI table.
Many of t he m et hods in TableModel suggest t he possibilit y of a default im plem ent at ion. Happily, t he JD K ( Java Developm ent Kit ) supplies an abst ract class t hat provides default im plem ent at ions of all but t he m ost dom ain- specific m et hods in TableModel. Figure 3.5 shows t his class. Figure 3.5. The AbstractTableModel class provides defaults for all but a few of the methods in TableModel.
Suppose t hat you want t o show a few rocket s in a t able, using a Swing user int erface. As Figure 3.6 shows, you can creat e a RocketTable class t hat adapt s an array of rocket s t o t he int erface t hat TableModel and AbstractTableModel expect . Figure 3.6. The RocketTable class adapts the TableModel interface to the Rocket class from the Oozinoz domain.
The RocketTable class has t o subclass AbstractTableModel because AbstractTableModel is a class, not an int erface. Whenever t he int erface you are adapt ing t o is support ed wit h an abst ract class, you m ust use an obj ect adapt er. I n t his exam ple, t hough, t here is a second reason you cannot use a class adapt er. A RocketTable is not a kind or a subt ype of Rocket. When an adapt er class m ust draw on inform at ion from m ore t han one obj ect , you have t o im plem ent t he adapt er as an obj ect adapt er. When you creat e t he RocketTable class, you can easily display inform at ion about rocket s in a Swing JTable obj ect , as Figure 3.7 shows. To creat e t he applicat ion t hat Figure 3.7 shows, you have t o develop t he RocketTable class and before writ ing an applicat ion t hat shows t he t able:
package com. oozi noz. appl i cat i ons; i mpor t j avax. swi ng. t abl e. *; i mpor t com. oozi noz. f i r ewor ks. *; publ i c cl ass Rocket Tabl e ext ends Abst r act Tabl eModel { pr ot ect ed Rocket [ ] r ocket s; pr ot ect ed St r i ng[ ] col umnNames = new St r i ng[ ] { "Name", "Pr i ce", "Apogee" } ; publ i c Rocket Tabl e( Rocket [ ] r ocket s) { t hi s. r ocket s = r ocket s; }
publ i c i nt get Col umnCount ( ) { / / chal l enge! } publ i c St r i ng get Col umnName( i nt i ) { / / chal l enge! } publ i c i nt get RowCount ( ) { / / chal l enge! } publ i c Obj ect get Val ueAt ( i nt r ow, i nt col ) { / / chal l enge! } } Figure 3.7. This GUI displays an instance of JTable fueled with rocket data.
CHALLENGE 3.2
Fill in t he code for t he RocketTable m et hods t hat adapt an array of Rocket obj ect s t o serve as a TableModel.
To launch t he display t hat Figure 3.7 shows, you can creat e a couple of exam ple Rocket obj ect s, place t hem in an array, const ruct an inst ance of RocketTable from t he array, and use Swing classes t o display t he t able. The ShowRocketTable class provides an exam ple:
package com. oozi noz. appl i cat i ons; i mpor t j ava. awt . *; i mpor t j avax. swi ng. *; i mpor t j ava. awt . event . *; i mpor t com. oozi noz. f i r ewor ks. *;
i mpor t com. oozi noz. uni t s. *; publ i c cl ass ShowRocket Tabl e i mpl ement s Uni t Const ant s { //. . . . } The main() m et hod of ShowRocketTable set s up default t able font s, creat es a Swing t able, and displays it :
publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { set Font s( ) ; J Tabl e t = new J Tabl e( get Rocket Tabl e( ) ) ; t . set RowHei ght ( 36) ; J Scr ol l Pane j sp = new J Scr ol l Pane( t ) ; j sp. set Pr ef er r edSi ze( new j ava. awt . Di mensi on( 300, 100) ) ; di spl ay( j sp, " Rocket s") ; } The setFonts() m et hods est ablishes t he font s t o use for t able cells and colum n headers:
pr i vat e st at i c voi d set Font s( ) { Font f = new Font ( "Di al og", Font . PLAI N, 18) ; UI Manager . put ( "Tabl e. f ont ", f ) ; UI Manager . put ( "Tabl eHeader . f ont ", f ) ; } The getRocketTable() m et hod ret urns a RocketTable obj ect t hat fulfills t he role of a TableModel. Essent ially, t he RocketTable obj ect adapt s a ( row, colum n) request int o a ( rocket , at t ribut e) request :
pr i vat e st at i c Rocket Tabl e get Rocket Tabl e( ) { Rocket r 1 = new Rocket ( "Shoot er ", 3. 95, ( Lengt h) METER. t i mes( 50) ) ; Rocket r 2 = new Rocket ( "Or bi t ", 29. 95, ( Lengt h) METER. t i mes( 5000) ) ; r et ur n new Rocket Tabl e( new Rocket [ ] { r 1, r 2 } ) ; } The display() m et hod cont ains reusable code:
publ i c st at i c voi d di spl ay( Component c, St r i ng t i t l e) { J Fr ame f = new J Fr ame( t i t l e) ; f . get Cont ent Pane( ) . add( c) ; f . addWi ndowLi st ener ( new Wi ndowAdapt er ( ) { publ i c voi d wi ndowCl osi ng( Wi ndowEvent e) { Syst em. exi t ( 0) ; }
} ); f . pack( ) ; f . set Vi si bl e( t r ue) ; } I n Chapt er 4, Facade, you will port t his code t o a ut ilit y class. Wit h fewer t han 20 st at em ent s of it s own, t he ShowRocketTable class sit s above hundreds or t housands of st at em ent s t hat collaborat e t o produce a t able com ponent wit hin a GUI fram ework. The JTable class can handle nearly every aspect of displaying a t able, but it can't know in advance what dat a you will want t o present . To let you supply t he dat a it needs, t he JTable class set s you up t o apply ADAPTER. To use JTable, you im plem ent t he TableModel int erface t hat JTable expect s, along wit h a class t hat provides t he dat a you want t o display. I t 's easy t o use JTable because t his class is designed for adapt at ion. I n pract ice, when you need t o adapt an exist ing class t o m eet a client 's needs, you m ay find t hat t he client code designers have not foreseen t his possibilit y and have not provided an int erface t o adapt t o.
Unforeseen Adaptation Suppose t hat an exist ing applicat ion at Oozinoz issues warnings and print s out ot her m essages t o System.out. The com m only used expression System.out is an inst ance of PrintStream. Not ing t his and foreseeing t hat you m ight want t o redirect t he m essage out put , t he applicat ion's designers have built in a setOutput() m et hod. I t would be bet t er if t hese designers had defined t he int erface t hey required for displaying m essages. That would have let you provide an im plem ent at ion for j ust t he PrintStream m et hods t hat t he applicat ion uses. But at least t he setOutput() m et hod let s you supply a different inst ance of PrintStream as t he m essage channel. Suppose t hat you want t o redirect t he m essages t o appear in a JText-Area obj ect . I nspect ing t he applicat ion, you not e t hat t he only call it m akes t o t he PrintStream obj ect you supply is print(), wit h an Object argum ent . To redirect t hese print() calls t o a JTextArea obj ect , you m ight design t he MessageAdapter class as shown in Figure 3.8. Figure 3.8. A MessageAdapter object is a PrintStream object that forwards messages to a JTextArea object's append() method.
Suppose t hat you im plem ent t his design and pass a MessageAdapter obj ect t o t he applicat ion's setOutput() m et hod. Alt hough you m ay find t hat all your t est s pass, t his design is fragile.
CHALLENGE 3.3
Writ e down a com m ent explaining what m ight go wrong wit h t his approach t o adapt at ion.
You could t ry m aking MessageAdapter m ore robust by overriding every m et hod in PrintStream, but t his is difficult and relies on your abilit y t o t ranslat e every PrintStream behavior t o a m eaningful count erpart in JTextArea. A bet t er solut ion would specify t he int erface t hat t he applicat ion can use. I f you have access t o t he developers of t he client code, you can ask for support on t heir end.
CHALLENGE 3.4
You'd like t o narrow t he responsibilit y of t he MessageAdapter class wit h a specificat ion of t he calls t he client m ight m ake t o it . What request should you m ake?
Recognizing Adapter Adapt ers are healt hier when a Java int erface narrows t he responsibilit y t hat t he adapt er class bears t o t he client . The JTable class, for exam ple, specifies t he exact int erface it needs wit h t he TableModel int erface. By com parison, t he MessageAdapter class has no cont ract wit h t he applicat ion it plugs int o t hat t he applicat ion won't expand it s use of a PrintStream obj ect 's behavior. I n fact , alt hough t he word adapt er appears in t he nam e of t he MessageAdapter class, you m ight quest ion whet her MessageAdapter is an inst ance of t he ADAPTER pat t ern.
CHALLENGE 3.5
Provide an argum ent t hat MessageAdapter, as it appears in Figure 3.8, is or is not an exam ple of ADAPTER.
I n answering Challenge 2.4, you explained t he value of t he Window-Adapter class. The MouseAdapter class, as Figure 3.9 shows, is anot her exam ple of a class t hat st ubs out t he requirem ent s of a regist rat ion int erface. Figure 3.9. The MouseAdapter class stubs out the requirements of the MouseListener interface.
CHALLENGE 3.6
Provide an argum ent t hat you are applying t he ADAPTER pat t ern when you use t he MouseAdapter class. Alt ernat ively, argue t hat t his is not t he case.
Summary The JTable com ponent in Swing is a good exam ple of a class whose developers applied t he ADAPTER pat t ern. A JTable com ponent set s it self up as a client t hat needs t able inform at ion as defined by t he TableModel int erface. This m akes it easy for you t o writ e an adapt er t hat feeds t he t able dat a from dom ain obj ect s, such as inst ances of t he Rocket class. To use JTable, you need t o writ e an obj ect adapt er t hat delegat es calls t o inst ances of an exist ing class. Two aspect s of JTable prevent you from using a class adapt er. First , you will usually creat e your t able adapt er as a subclass of AbstractTableModel, so you can't also subclass your exist ing class. Second, t he JTable class requires a collect ion of obj ect s, and only an obj ect adapt er can adapt inform at ion from m ore t han one obj ect . A class adapt er produces a single obj ect t hat is t he adapt er. This avoids forwarding calls from an adapt er obj ect t o inst ance( s) of t he class t hat it adapt s. A class adapt er obj ect sim ply t ranslat es m et hod nam es, forwarding calls t o it self. The relat ive advant ages of class and obj ect adapt ers are sm all in com parison t o t he advant age of having a Java int erface t hat defines t he client 's needs. I n t he absence of such an int erface, your adapt er obj ect poses as an inst ance of t he client 's required t ype, w it h no cont ract defining t he client 's needs. I n a sit uat ion like t his you should, if possible, ask t he client developers t o build in an int erface t o which you can adapt .
Chapter 4. Facade A great advant age of obj ect - orient ed ( OO) program m ing is t hat it helps keep applicat ions from becom ing m onolit hic program s wit h hopelessly int erwoven pieces. An " applicat ion" in an OO syst em is a m inim al class t hat knit s t oget her t he behaviors from reusable t oolkit s of ot her classes. A t oolkit or subsyst em developer oft en creat es packages of welldesigned classes wit hout providing any applicat ions t hat t ie t he classes t oget her. The packages in t he JDK are generally like t his; t hey are t oolkit s from which you can weave an endless variet y of dom ain- specific applicat ions.
The reusabilit y of t oolkit s com es wit h a problem : The diverse applicabilit y of classes in an OO subsyst em m ay offer an oppressive variet y of opt ions. A developer who want s t o use t he t oolkit m ay not know where t o begin. This is especially a problem when a developer want s t o apply a norm al, no- frills, vanilla usage of t he classes in a package. The FACADE pat t ern addresses t his need. A facade is a class wit h a level of funct ionalit y t hat lies bet ween a t oolkit and a com plet e applicat ion, offering a vanilla usage of t he classes in a package or a subsyst em . The int ent of t he FACADE pat t ern is t o provide an int erface t hat m akes a subsyst em easy t o use.
Refactoring to Facade A facade provides an int erface, but it does not use a Java int erface. Rat her, a facade is a class wit h m et hods t hat m ake it easy t o use t he classes in a subsyst em . Consider an exam ple from t he early days at Oozinoz, when t here were not yet st andards for GUI developm ent . Suppose t hat you com e across an applicat ion t hat a developer has creat ed t o show t he flight pat h of an unexploded shell. Figure 4.1 shows t his class. As before, t he underscore in t he nam e of t he FlightPanel_1 class is a hint t hat t his class, from com.oozinoz.applications, needs refact oring. Figure 4.1. The FlightPanel_1 class displays the flight path of an unexploded shell.
Aerial shells are designed t o explode high in t he sky, wit h spect acular result s. But occasionally, a shell does not explode at all; it is a du d. I f a shell does not explode, it s ret urn t o Eart h becom es int erest ing. A shell, unlike a rocket , is unpowered, so if you ignore t he effect s of wind and air resist ance, t he flight pat h of a dud is a sim ple parabola. Figure 4.2 shows a screen shot of t he window t hat appears when you execut e FlightPanel_1.main(). Figure 4.2. The FlightPanel_1 application shows where a dud may land.
There is a problem wit h t he FlightPanel_1 class: I t int erm ingles t hree purposes. I t s prim ary purpose is t o act as a panel t hat displays a flight pat h. A second purpose of t his class is t o calculat e a parabolic flight pat h. The FlightPanel_1 class current ly perform s t his calculat ion in it s paintComponent() code:
publ i c voi d pai nt Component ( Gr aphi cs g) { super . pai nt Component ( g) ; / / pai nt t he backgr ound i nt nPoi nt = 101; doubl e w = get Wi dt h( ) - 1; doubl e h = get Hei ght ( ) - 1; i nt [ ] x = new i nt [ nPoi nt ] ; i nt [ ] y = new i nt [ nPoi nt ] ;
f or ( i nt i = 0; i < nPoi nt ; i ++) { / / t goes 0 t o 1 doubl e t = ( ( doubl e) i ) / ( nPoi nt - 1) ; / / x goes 0 t o w x[ i ] = ( i nt ) ( t * w) ; / / y i s h at t = 0 and t = 1, and / / y i s 0 at t = . 5 y[ i ] = ( i nt ) ( 4 * h * ( t - . 5) * ( t - . 5) ) ; } g. dr awPol yl i ne( x, y, nPoi nt ) ; } See t he sidebar Param et ric Equat ions on page 40 for an explanat ion of how t his code est ablishes t he x and y values of t he dud's pat h.
The t hird purpose t hat FlightPanel_1 fulfills is t o act as a com plet e applicat ion, wrapping t he flight pat h panel in a t it led border and displaying it . This code appears in t he main() rout ine:
publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Fl i ght Panel _1 f p = new Fl i ght Panel _1( ) ; f p. set Pr ef er r edSi ze( new Di mensi on( 300, 200) ) ; J Panel f p_t i t l ed = cr eat eTi t l edPanel ( "Fl i ght Pat h", f p) ; // J Fr ame f r ame = new J Fr ame( "Fl i ght Pat h f or Shel l Duds") ; f r ame. get Cont ent Pane( ) . add( f p_t i t l ed) ; f r ame. addWi ndowLi st ener ( new Wi ndowAdapt er ( ) { publ i c voi d wi ndowCl osi ng( Wi ndowEvent e) { Syst em. exi t ( 0) ; } } ); f r ame. pack( ) ; f r ame. set Vi si bl e( t r ue) ; }
Parametric Equations When you need t o plot a curve, it can be difficult t o describe t he y values as funct ions of x values. Pa r a m e t r ic e qu a t ion s let you define bot h x and y in t erm s of a t hird param et er t hat you int roduce. Specifically, you can define t hat t im e t goes from 0 t o 1 as t he curve is drawn, and you can define x and y as funct ions of t he param et er t . For exam ple, suppose t hat you want t he parabolic flight of a nonexploding shell t o st ret ch across t he widt h, w, of a Graphics obj ect . Then a param et ric equat ion for x is easy:
The y values for a parabola m ust vary wit h t he square of t he value of t . Y values increase going down t he screen. For a parabolic flight , y should be 0 at t im e t = 0.5:
Here, k represent s a const ant t hat we st ill need t o det erm ine. The equat ion provides for y t o be 0 at t = 0.5 and provides for y t o have t he sam e value at t = 0 and t = 1. At t hose t wo t im es, y should be h, so wit h a lit t le algebraic m anipulat ion, you can find t he com plet e equat ion for y:
Figure 4.2 shows t he equat ions for a parabola in act ion. Anot her advant age of param et ric equat ions is t hat t here is no problem drawing curves t hat have m ore t han one y value for a given x value. Consider drawing a circle. The equat ion for a circle wit h a radius of 1 is:
or
Handling t he fact t hat t wo y values em erge for every x value is com plicat ed. I t 's also difficult t o adj ust t hese values t o plot correct ly wit hin a Graphics obj ect 's height and widt h. Polar coordinat es m ake t he funct ion for a circle sim pler:
These form ulas are param et ric equat ions t hat show x and y as funct ions of a new param et er, t het a. Thet a represent s t he sweep of an arc t hat varies from 0 t o 2 pi as a circle is drawn. You can set t he radius of a circle so t hat it will fit wit hin t he height h and widt h w of a Graphics obj ect . A handful of param et ric equat ions suffice t o plot a circle wit hin t he bounds of a Graphics obj ect :
Translat ing t hese equat ions int o Java produces t he circle shown in Figure 4.3. Figure 4.3. Parametric equations simplify the modeling of curves where y is not a single-valued function of x.
The code t hat draws a circle is a fairly direct t ranslat ion of t he m at hem at ical form ulas. One subt let y is t hat t he code reduces t he height and widt h of t he Graphics obj ect because t he pixels are num bered from 0 t o height –1 and from 0 t o widt h –1:
package com. oozi noz. appl i cat i ons; i mpor t j avax. swi ng. *; i mpor t j ava. awt . *; i mpor t com. oozi noz. ui . Swi ngFacade; publ i c cl ass ShowCi r cl e ext ends J Panel { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { ShowCi r cl e sc = new ShowCi r cl e( ) ; sc. set Pr ef er r edSi ze( new Di mensi on( 300, 300) ) ; Swi ngFacade. l aunch( sc, " Ci r cl e") ; } pr ot ect ed voi d pai nt Component ( Gr aphi cs g) { super . pai nt Component ( g) ; i nt nPoi nt = 101; doubl e w = get Wi dt h( ) - 1; doubl e h = get Hei ght ( ) - 1; doubl e r = Mat h. mi n( w, h) / 2. 0; i nt [ ] x = new i nt [ nPoi nt ] ; i nt [ ] y = new i nt [ nPoi nt ] ; f or ( i nt i = 0; i < nPoi nt ; i ++) { doubl e t = ( ( doubl e) i ) / ( nPoi nt - 1) ; doubl e t het a = Mat h. PI * 2. 0 * t ; x[ i ] = ( i nt ) ( w / 2 + r * Mat h. cos( t het a) ) ;
y[ i ] = ( i nt ) ( h / 2 - r * Mat h. si n( t het a) ) ; } g. dr awPol yl i ne( x, y, nPoi nt ) ; } } Defining x and y funct ions in t erm s of t let s you divide t he t asks of det erm ining x values and y values. This is oft en sim pler t han defining y in t erm s of x and oft en facilit at es t he m apping of x and y ont o a Graphics obj ect 's coordinat es. Param et ric equat ions also sim plify t he plot t ing of curves where y is not a singlevalued funct ion of x.
CHALLENGE 4.1
Com plet e t he diagram in Figure 4.4 t o show t he code for FlightPanel_1 refact ored int o t hree classes: a FlightPath class, a reduced FlightPanel class t hat draws a flight pat h, and a SwingFacade class. Provide t he facade wit h t he ut ilit y m et hods t hat appeared in t he init ial FlightPanel_1 class. Also provide t he facade wit h a m et hod t o launch a fram e. Figure 4.4. This diagram shows the flight path application refactored into classes that each have one job.
Aft er refact oring, t he FlightPanel class is m uch sim pler:
package com. oozi noz. appl i cat i ons; i mpor t j avax. swi ng. *; i mpor t j ava. awt . *; i mpor t com. oozi noz. si mul at i on. Fl i ght Pat h; publ i c cl ass Fl i ght Panel ext ends J Panel { publ i c voi d pai nt Component ( Gr aphi cs g) { super . pai nt Component ( g) ; / / pai nt t he backgr ound i nt nPoi nt = 101; Fl i ght Pat h f p = new Fl i ght Pat h( get Wi dt h( ) - 1, get Hei ght ( ) - 1) ;
g. dr awPol yl i ne( f p. get X( nPoi nt ) , f p. get Y( nPoi nt ) , nPoi nt ) ; } } The FlightPath class provides t he parabolic flight pat h of a dud:
package com. oozi noz. si mul at i on; publ i c cl ass Fl i ght Pat h { pr ot ect ed doubl e di st ance; pr ot ect ed doubl e apogee; publ i c Fl i ght Pat h( doubl e di st ance, doubl e apogee) { t hi s. di st ance = di st ance; t hi s. apogee = apogee; } publ i c i nt [ ] get X( i nt nPoi nt ) { i nt [ ] x = new i nt [ nPoi nt ] ; f or ( i nt i = 0; i < nPoi nt ; i ++) { / / t goes 0 t o 1 doubl e t = ( ( doubl e) i ) / ( nPoi nt - 1) ; / / x goes 0 t o di st ance x[ i ] = ( i nt ) ( t * di st ance) ; } r et ur n x; } publ i c i nt [ ] get Y( i nt nPoi nt ) { i nt [ ] y = new i nt [ nPoi nt ] ; f or ( i nt i = 0; i < nPoi nt ; i ++) { / / t goes 0 t o 1 doubl e t = ( ( doubl e) i ) / ( nPoi nt - 1) ; / / y i s apogee at t = 0 and t = 1, / / and y i s 0 at t = . 5 y[ i ] = ( i nt ) ( 4 * apogee * ( t - . 5) * ( t - . 5) ) ; } r et ur n y; } } Most of t he code of t he main() rout ine of t he init ial FlightPanel class m igrat es t o st at ic m et hods in t he SwingFacade class:
package com. oozi noz. ui ;
i mpor t i mpor t i mpor t i mpor t publ i c {
j avax. swi ng. *; j avax. swi ng. bor der . *; j ava. awt . *; j ava. awt . event . *; cl ass Swi ngFacade
publ i c st at i c Ti t l edBor der cr eat eTi t l edBor der ( St r i ng t i t l e) { Ti t l edBor der t b = Bor der Fact or y. cr eat eTi t l edBor der ( Bor der Fact or y. cr eat eBevel Bor der ( Bevel Bor der . RAI SED) , t i t l e, Ti t l edBor der . LEFT, Ti t l edBor der . TOP) ; t b. set Ti t l eCol or ( Col or . bl ack) ; t b. set Ti t l eFont ( get St andar dFont ( ) ) ; r et ur n t b; } publ i c st at i c J Panel cr eat eTi t l edPanel ( St r i ng t i t l e, J Panel i n) { J Panel out = new J Panel ( ) ; out . add( i n) ; out . set Bor der ( cr eat eTi t l edBor der ( t i t l e) ) ; r et ur n out ; } publ i c st at i c Font get St andar dFont ( ) { r et ur n new Font ( "Di al og", Font . PLAI N, 18) ; } publ i c st at i c J Fr ame l aunch( Component c, St r i ng t i t l e) { J Fr ame f r ame = new J Fr ame( t i t l e) ; f r ame. get Cont ent Pane( ) . add( c) ; l i st en( f r ame) ; f r ame. pack( ) ; f r ame. set Vi si bl e( t r ue) ; r et ur n f r ame; } publ i c st at i c voi d l i st en( Fr ame f )
{ f . addWi ndowLi st ener ( new Wi ndowAdapt er ( ) { publ i c voi d wi ndowCl osi ng( Wi ndowEvent e) { Syst em. exi t ( 0) ; } } ); } } You m ay have found t hat refact oring FlightPanel_1 m ade t he applicat ion disappear, wit h no main() m et hod appearing anywhere. I t oft en happens t hat refact oring a m onolit hic applicat ion produces a facade and elim inat es t he presence of a main() m et hod on any of t he new classes. To achieve t he result s of t he original applicat ion in t his exam ple, you can creat e a st andalone class wit h j ust one m et hod:
package com. oozi noz. appl i cat i ons; i mpor t j ava. awt . Di mensi on; i mpor t j avax. swi ng. *; i mpor t com. oozi noz. ui . Swi ngFacade; publ i c cl ass ShowFl i ght Panel { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Fl i ght Panel f p = new Fl i ght Panel ( ) ; f p. set Pr ef er r edSi ze( new Di mensi on( 300, 200) ) ; J Panel p = Swi ngFacade. cr eat eTi t l edPanel ( "Fl i ght Pat h", f p) ; Swi ngFacade. l aunch( p, "Fl i ght Pat h f or Shel l Duds") ; } } Running t his class produces exact ly t he sam e result s as t he main() in t he original FlightPanel class. But now, you have a m ore reusable t oolkit and a Swing facade t hat provides st andard com ponent s and sim plifies launching Swing applicat ions.
Facades, Utilities, and Demos A facade class m ay have all st at ic m et hods, as is t he case wit h t he SwingFacade class provided in t he solut ions. Such a class is called a ut ilit y in UML ( Booch, Rum baugh, and Jacobson 1999) . A de m o is an exam ple t hat shows how t o use a class or a subsyst em . As such, dem os provide m uch of t he sam e value as facades.
CHALLENGE 4.2
Writ e down t wo differences bet ween a dem o and a facade.
The javax.swing package cont ains JOptionPane, a class t hat " m akes it easy t o pop up a st andard dialog box," according t o t he class com m ent .
For exam ple, t he following code displays and redisplays a dialog unt il t he user clicks t he Yes but t on ( see Figure 4.5) :
package com. oozi noz. appl i cat i ons; i mpor t j avax. swi ng. *; publ i c cl ass ShowOpt i onPane { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { i nt opt i on; do { opt i on = J Opt i onPane. showConf i r mDi al og( nul l , "Had enough?", " A St ubbor n Di al og", J Opt i onPane. YES_NO_OPTI ON) ; } whi l e ( opt i on == J Opt i onPane. NO_OPTI ON) ; } } Figure 4.5. The JOptionPane class makes it easy to display dialogs such as this one.
CHALLENGE 4.3
CHALLENGE 4.4
The JOptionPane does in fact m ake it easy t o display a dialog. St at e whet her t his class is a facade, a ut ilit y, or a dem o, and j ust ify your answer.
Few facades appear in t he Java class libraries. Why is t hat ?
Summary Ordinarily, you should refact or t he classes in a subsyst em unt il each class has a welldefined purpose. This will m ake your code easier t o m aint ain, but it can also m ake it difficult for a user of your subsyst em t o know where t o begin. To help t he developer who is using your code, you can supply dem os or facades t hat go wit h your subsyst em . A dem o is usually a st and- alone, nonreusable applicat ion t hat shows one way t o apply a
subsyst em . A facade is usually a configurable, reusable class wit h a higher- level int erface t hat m akes t he subsyst em easier t o use.
Chapter 5. Composite A com posit e is a group of obj ect s in which som e obj ect s m ay cont ain ot hers; t hus, one obj ect m ay represent groups, and anot her m ay represent an individual it em , or le a f. When you m odel a com posit e, t wo powerful concept s em erge. One im port ant m odeling idea is t o design groups so t hat t hey can cont ain eit her individual it em s or ot her groups. ( A com m on error is t o define groups so t hat t hey can cont ain only leaves.) A second powerful concept is t o define com m on behaviors for individual obj ect s and for com posit ions. You can bring t hese ideas t oget her by defining a com m on t ype for groups and it em s and by m odeling groups as cont aining a collect ion of obj ect s of t his t ype. This fulfills t he int ent of t he COMPOSI TE pat t ern: COMPOSI TE let s client s t reat individual obj ect s and com posit ions of obj ect s uniform ly.
An Ordinary Composite Figure 5.1 shows an ordinary com posit e st ruct ure. The Leaf and Composite classes share a com m on int erface t hat Component abst ract s, and a Composite obj ect ret ains a collect ion of ot her Composite and Leaf obj ect s. Not e t hat t he Component class in Figure 5.1 is an abst ract class wit h no concret e operat ions, so you could define it as an int erface t hat Leaf and Composite im plem ent . Figure 5.1. The key ideas of Composite are that composites can contain other composites— not just leaves—and that composite and leaf nodes share a common interface.
CHALLENGE 5.1
Give t wo reasons why t he int erface t hat Leaf and Composite share usually appears in an abst ract class rat her t han in an int erface.
Recursive Behavior in Composites
Engineers at Oozinoz perceive a nat ural com posit ion in t he processing m achines t hey use t o produce fireworks. The fact ory is com posed of bays; each bay has one or m ore m anufact uring lines; a line is a collect ion of m achines t hat collaborat ively produce m at erial t o m eet a schedule. The developers at Oozinoz have m odeled t his com posit ion from t he problem dom ain wit h t he class st ruct ure shown in Figure 5.2. As t he figure shows, t he getMachineCount() behavior applies t o bot h individual m achines and collect ions of m achines and ret urns t he num ber of m achines in any given com ponent . Figure 5.2. The getMachineCount() method is an appropriate behavior for both individual machines and composites.
CHALLENGE 5.2
Writ e t he code for t he getMachineCount() m et hods im plem ent ed by Machine and by MachineComposite.
Suppose t hat MachineComponent also needs t he following m et hods:
Method
Behavior isCompletelyUp() Indicates whether all the machines in a component are in an "up" state stopAll() Directs all the machines in a component to stop processing getOwners() Returns a set of process engineers responsible for the machines in a component getMaterial() Return all the in-process material in a machine component The definit ion and operat ion of each m et hod in MachineComponent is recursive. For exam ple, t he count of m achines in a com posit e is t he sum of t he count s of m achines in it s com ponent s.
CHALLENGE 5.3
For each m et hod declared by MachineComponent, give recursive definit ions for MachineComposite and nonrecursive definit ions for Machine.
Method
Class
getMachineCount() MachineComposite
Definition Return the
getMachineCount() MachineComposite
Return the sum of the counts for each component in components.
Machine
Return 1. isCompletelyUp() MachineComposite ?? Machine ?? stopAll() MachineComposite ?? Machine ?? getOwners() MachineComposite ?? Machine ?? getMaterial() MachineComposite ?? Machine ??
Trees in Graph Theory I n a com posit e st ruct ure, w e can say t hat a node t hat holds references t o ot her nodes is a t r e e . However, t his definit ion is a bit loose. To be m ore precise, we can apply a few t erm s from graph t heory t o obj ect m odeling. We can st art by drawing an obj ect m odel as a gr a ph —a collect ion of nodes and edges—wit h obj ect s as nodes and obj ect references as edges. Consider an obj ect m odel of an a ssa y, or analysis, of a bat ch of a chem ical. The Assay class has a batch at t ribut e of t ype Batch, and t he Batch class has a chemical at t ribut e of t ype Chemical. Suppose t hat a part icular Assay obj ect a has a batch at t ribut e t hat refers t o a Batch obj ect b. Suppose t oo t hat t he chemical at t ribut e of t he Batch obj ect b refers t o a Chemical c. Figure 5.3 shows t wo alt ernat ive diagram s for t his obj ect m odel. Figure 5.3. The two UML diagrams here show alternative representations of the same information: Object a refers to object b, and object b refers to object c.
There is a pa t h—a series of obj ect references—from a t o c because a refers t o b and b refers t o c. A cycle is a pat h in which a node appears t wice. There would be a cycle of references in t his obj ect m odel if t he c Chemical obj ect referred back t o t he a:Assay obj ect .
Obj ect m odels are direct ed graphs: An obj ect reference has a direct ion. Graph t heory usually applies t he t erm t ree t o refer t o undirect ed graphs. However, a direct ed graph m ay be called a t ree if it •
Has a root node that has no references to it
•
Has exactly one edge, or reference, to all other nodes
We can also express t he second rule by saying t hat each node except t he root has exact ly one parent —t he node t hat refers t o it . The obj ect m odel t hat Figure 5.3 depict s is a t ree. For larger obj ect m odels, it can be difficult t o t ell whet her t he m odel is a t ree. Figure 5.4 shows t he obj ect m odel of a fact ory, called plant, t hat is a MachineComposite obj ect . This plant cont ains a bay t hat has t hree m achines: m, n, and o. The obj ect m odel also shows t hat t he plant obj ect 's list of m achine com ponent s cont ains a direct reference t o m achine m. Figure 5.4. An object model of a small fireworks factory
The plant obj ect in Figure 5.4 does not cont ain a cycle, but not e t hat it is not a t ree, because t w o obj ect s refer t o t he Machine obj ect m. The bay obj ect m odel, viewed in isolat ion from t he plant obj ect , is a t ree. Met hods t hat work on com posit es m ay have defect s if t hey assum e t hat a com posit e is a t ree. Challenge 5.2 asked for a definit ion of a getMachineCount() operat ion. The Machine class im plem ent at ion of t his operat ion as given in t he solut ion t o t his challenge is arguably correct :
publ i c i nt get Machi neCount ( ) { r et ur n 1; } The MachineComposite class also correct ly im plem ent s getMachineCount(), ret urning t he sum of t he count s for each of a com posit e's com ponent s:
publ i c i nt get Machi neCount ( ) { i nt count = 0; I t er at or i = component s. i t er at or ( ) ; whi l e ( i . hasNext ( ) )
{ Machi neComponent mc = ( Machi neComponent ) i . next ( ) ; count += mc. get Machi neCount ( ) ; } r et ur n count ; } These m et hods are correct so long as MachineComponent obj ect s are t rees. I t can happen, t hough, t hat a com posit e t hat you suppose is a t ree suddenly becom es not a t ree. This is especially likely t o occur when users can edit t he com posit ion. Consider an exam ple t hat m ight occur at Oozinoz. The fireworks engineers at Oozinoz use a GUI applicat ion t o record and t o updat e t he com posit ion of m achinery in t he fact ory. One day, t hey report a defect regarding t he num ber of m achines t hat are report ed t o exist in t he fact ory. You reproduce t heir obj ect m odel wit h a plant() m et hod on an OozinozFactory class:
publ i c st at i c Machi neComposi t e pl ant ( ) { Machi neComposi t e pl ant = new Machi neComposi t e( 100) ; Machi neComposi t e bay = new Machi neComposi t e( 101) ;
Machi ne m = new Mi xer ( 102) ; Machi ne n = new Mi xer ( 103) ; Machi ne o = new Mi xer ( 104) ; bay. add( m) ; bay. add( n) ; bay. add( o) ;
pl ant . add( m) ; pl ant . add( bay) ; r et ur n pl ant ; } This code produces t he plant obj ect shown in Figure 5.4.
CHALLENGE 5.4
What does t he following program print out ?
package com. oozi noz. machi ne; publ i c cl ass ShowPl ant { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Machi neComponent c = Oozi nozFact or y. pl ant ( ) ; Syst em. out . pr i nt l n( c. get Machi neCount ( ) ) ; } }
The GUI applicat ion t hat Oozinoz uses t o let engineers build obj ect m odels of a fact ory's m achinery should check whet her a node already exist s in a com ponent t ree before adding it a second t im e. A sim ple way t o do t his is t o m aint ain a set of exist ing nodes. You m ay not , however, always have cont rol over how a com posit e is form ed. I n t his case, you can writ e an isTree() m et hod t o check whet her a com posit e is a t ree. An obj ect m odel is a t ree if an algorit hm can t raverse it s references wit hout encount ering t he sam e node t wice. You can im plem ent an isTree() m et hod on t he abst ract class MachineComponent so t hat it delegat es t o an isTree() m et hod t hat m aint ains a set of visit ed nodes. The MachineComponent class can leave t he im plem ent at ion of t he param et erized isTree(s:Set) m et hod abst ract . Figure 5.5 shows t he placem ent of t he isTree() m et hods. Figure 5.5. An isTree() method can detect whether a composite is in fact a tree.
The MachineComponent code delegat es an isTree() call t o it s abst ract isTree(s:Set) m et hod:
publ i c bool ean i sTr ee( ) { r et ur n i sTr ee( new HashSet ( ) ) ; } publ i c abst r act bool ean i sTr ee( Set s) ; The im plem ent at ion of isTree() for Machine reflect s t he fact t hat individual m achines are always t rees:
pr ot ect ed bool ean i sTr ee( Set vi si t ed) { vi si t ed. add( t hi s) ; r et ur n t r ue; } The MachineComposite im plem ent at ion of isTree() should add t he receiving obj ect ( this) t o t he visit ed set and t hen it erat e over t he com posit e's com ponent s. The m et hod
can ret urn false if any com ponent has been previously visit ed or if any com ponent is not it self a t ree. Ot herwise, t he m et hod can ret urn true.
CHALLENGE 5.5
Writ e t he code for MachineComposite.isTree(Set s).
Wit h som e care, you can guarant ee t hat an obj ect m odel is a t ree by refusing changes t hat would m ake isTree() false. On t he ot her hand, you m ay need t o allow com posit es t hat are not t rees, part icularly when t he problem dom ain t hat you are m odeling cont ains cycles.
Composites with Cycles The nont ree com posit e t hat Challenge 5.4 refers t o was an accident . For physical obj ect s, you m ay want t o disallow t he not ion t hat an obj ect is cont ained by m ore t han one ot her obj ect . Physical obj ect s also usually cannot appear in cyclic m odels, in which an obj ect ult im at ely cont ains it self. However, a problem dom ain can have nonphysical elem ent s where cycles of cont ainm ent m ake sense. This occurs frequent ly when m odeling process flows. Consider t he const ruct ion of aerial shells such as t he one t hat Figure 5.6 depict s. We launch a shell from a m ort ar by ignit ing t he lift ing charge of black powder t hat is seat ed beneat h t he core charge. The secondary fuses burn while t he shell in t he air, event ually reaching t he core. When t he shell core explodes it s st ars ignit e, creat ing t he visual effect s of aerial fireworks. Figure 5.6. An aerial shell uses two charges: one for initial lift and one to expel explosive stars when the shell is at its peak.
The process flow for m aking an aerial shell consist s of building an inner shell, having it inspect ed, and t hen eit her reworking it or finishing it s assem bly. To m ake t he inner shell, an operat or uses a shell assem bler t o place som e st ars in a hem ispherical casing, insert a
black powder core, at t ach m ore st ars on t op of t he core, and seal t his subassem bly wit h anot her hem ispherical casing. An inspect or verifies t hat t he inner shell m eet s safet y and qualit y st andards. I f it doesn't , t he operat or disassem bles t he inner shell and, grum bling, m akes it again. When an inner shell passes inspect ion, t he operat or finishes t he shell, using a fuser t o connect a lift ing charge t o t he inner shell wit h fusing. Finally, t he operat or m anually wraps t he com plet e aerial shell. As wit h m achine com posit es, Oozinoz engineers have a GUI t hat let s t hem describe t he com posit ion of a process. Figure 5.7 shows t he class st ruct ure t hat support s process m odeling. Figure 5.7. The process for manufacturing fireworks includes some steps that are alternations, or sequences, of other steps.
Figure 5.8 shows t he obj ect s t hat represent t he process flow for m aking an aerial shell. The make process is a sequence of t he buildInner st ep, t he inspect st ep, and t he reworkOrFinish subprocess. The reworkOrFinish subprocess t akes one of t wo alt ernat e pat hs. I t m ay require a disassemble st ep followed by t he make process, or it m ay require j ust t he finish st ep. Figure 5.8. When completed, this diagram will show an object model of the Oozinoz process for making aerial shells.
CHALLENGE 5.6
Figure 5.8 shows t he obj ect s in a m odel of t he shell assem bly process. A com plet e obj ect diagram would show links bet ween any obj ect s t hat refer t o each ot her. For exam ple, t he diagram shows t he references t hat t he make obj ect ret ains. Your challenge is t o fill in t he m issing links in t he diagram .
The getStepCount() operat ion in t he ProcessComponent hierarchy count s t he num ber of st eps in a process flow. This m et hod has t o be careful t o count each st ep only once and t o not ent er an infinit e loop when a process cont ains a cycle. The ProcessComponent class im plem ent s t he getStepCount() m et hod so t hat it relies on a com panion m et hod t hat passes along a set of visit ed nodes:
publ i c i nt get St epCount ( ) { r et ur n get St epCount ( new HashSet ( ) ) ; } publ i c abst r act i nt get St epCount ( Set vi si t ed) ; The ProcessComposite class t akes care in it s im plem ent at ion of t he getStepCount() m et hod not t o visit a previously visit ed node:
publ i c i nt get St epCount ( Set vi si t ed) { vi si t ed. add( t hi s) ; i nt count = 0; I t er at or i = subpr ocesses. i t er at or ( ) ; whi l e ( i . hasNext ( ) ) { Pr ocessComponent pc = ( Pr ocessComponent ) i . next ( ) ; i f ( ! vi si t ed. cont ai ns( pc) ) { count += pc. get St epCount ( vi si t ed) ; } } r et ur n count ; } The ProcessStep class im plem ent at ion of getStepCount() is sim ple:
publ i c i nt get St epCount ( Set vi si t ed) { vi si t ed. add( t hi s) ; r et ur n 1; } The com.oozinoz.process package cont ains a ShellProcess class t hat has a make() m et hod t hat ret urns t he make obj ect t hat Figure 5.8 depict s. ( The code for t his m et hod appears on page 320, alt hough t he point here is t hat t he make process cont ains four st eps.) The process package also has a TestProcess class t hat provides aut om at ed t est s of various t ypes of process graphs. For exam ple, t his class includes a m et hod t hat t est s t hat t he getStepCount() operat ion correct ly count s t he num ber of st eps in t he cyclic make process:
publ i c voi d t est Shel l ( ) { asser t Equal s( 4, Shel l Pr ocess. make( ) . get St epCount ( ) ) ; } This t est runs and passes wit hin t he JUnit t est ing fram ework. See www.j unit .org for m ore inform at ion about JUnit .
Consequences of Cycles Most operat ions on a com posit e, such as count ing it s num ber of leaf nodes, m ake sense even if t he com posit e is not a t ree. Usually, t he only difference t hat nont ree com posit es int roduce is t hat you have t o be careful t o not operat e on a given node t wice. However, som e operat ions becom e m eaningless if t he com posit e cont ains a cycle. Operat ions t hat don't m ake sense if a com posit e cont ains cycles include any behavior t hat depends on t he lengt h of a pat h in t he com posit e. For exam ple, t he height of a t ree is t he lengt h of t he longest pat h from t he root t o a leaf. This concept does not apply when cycles appear in a com posit e, as t here is no lim it t o t he lengt h of som e pat hs. I n t he fireworks process exam ple, t here is no " height " of t he process com posit e. This m irrors t he fact t hat t here is no lim it t o t he num ber of t im es an aerial shell m ay be reworked.
Anot her result of allowing com posit es t hat are not t rees is t hat you lose t he abilit y t o assum e t hat each node has a single parent . I f a com posit e is not a t ree, a node m ay have m ore t han one parent . For exam ple, t he process t hat Figure 5.8 m odels m ight have several com posit e st eps t hat use t he inspect st ep, giving t he inspect obj ect m ult iple parent s. There is no inherent problem in a node's having m ult iple parent s, but a problem m ay arise if you creat e a m odel t hat insist s ot herwise.
Summary COMPOSI TE cont ains t wo powerful, relat ed concept s. One concept is t hat a group can cont ain eit her individual it em s or ot her groups. Relat ed t o t his concept is t he idea t hat groups and individual it em s m ay share a com m on int erface. These ideas com e t oget her in obj ect m odeling, whereby you can creat e an abst ract class or a Java int erface t hat defines t he behaviors t hat are com m on t o groups and t o individual obj ect s. Modeling com posit es oft en leads t o recursive definit ion of m et hods on com posit e nodes. When recursion is present , a danger exist s of writ ing code t hat produces an infinit e loop. To avoid such problem s, you can t ake st eps t o guarant ee t hat your com posit es are always t rees. Alt ernat ively, you can allow cycles t o occur in a com posit e, but you have t o m odify your algorit hm s t o w at ch for recursion. You can handle cycles in com posit es by m aint aining a collect ion of observed nodes.
Chapter 6. Bridge The Bridge Pat t ern focuses on t he im plem ent at ion of an a bst r a ct ion. Design Pat t erns ( Gam m a et al. 1995) uses t he word abst ract ion t o refer t o a class t hat relies on a set of abst ract operat ions, where several im plem ent at ions of t he set of abst ract operat ions are possible. The ordinary way t o im plem ent an abst ract ion is t o creat e a class hierarchy, wit h an abst ract class at t he t op t hat defines t he abst ract operat ions; each subclass in t he hierarchy provides a different im plem ent at ion of t he set of abst ract operat ions. This approach becom es insufficient when you need t o subclass t he hierarchy for anot her reason. I t can also happen t hat t he abst ract operat ions need t o be defined and im plem ent ed in advance of abst ract ions t hat will use t hem . You can creat e a bridge by m oving t he set of abst ract operat ions t o an int erface, so t hat an abst ract ion will depend on an im plem ent at ion of t he int erface. The int ent of t he BRI DGE pat t ern is t o decouple an abst ract ion from t he im plem ent at ion of it s abst ract operat ions, so t hat t he abst ract ion and it s im plem ent at ion can vary independent ly.
A Classic Example of Bridge: Drivers A com m on use of BRI DGE occurs when an applicat ion uses a driver. A dr ive r is an obj ect t hat operat es a com put er syst em or an ext ernal device according t o a well- specified int erface. Applicat ions t hat use drivers are abst ract ions: The effect of running t he applicat ion depends on which driver is in place. Each driver is an inst ance of t he ADAPTER pat t ern, providing t he int erface a client expect s, using t he services of a class wit h a different int erface. An overall design t hat uses drivers is an inst ance of BRI DGE. The design separat es applicat ion developm ent from t he developm ent of t he drivers t hat im plem ent t he abst ract operat ions on which t he applicat ions rely. An everyday exam ple of applicat ions using drivers t o operat e com put er syst em s appears in dat abase access. Dat abase connect ivit y in Java usually depends on JD BC. ( " JDBC" is a t radem arked nam e, not an acronym .) A good resource t hat explains how t o apply JDBC is JDBC Dat abase Access wit h Java™ ( Ham ilt on, Cat t ell, and Fisher 1997) . I n short , JDBC is an a pplica t ion pr ogr a m m ing int e r fa ce ( API ) for execut ing st r uct ur e d que r y
la ngua ge ( SQL) st at em ent s. Classes t hat im plem ent t he int erface are JDBC drivers, and applicat ions t hat rely on t hese drivers are abst ract ions t hat can work wit h any dat abase for which a JDBC driver exist s. The JDBC archit ect ure decouples an abst ract ion from it s im plem ent at ion so t hat t he t wo can vary independent ly—an excellent exam ple of BRI DGE. To use a JDBC driver, you load it , connect t o a dat abase, and creat e a Statement obj ect :
Cl ass. f or Name( dr i ver Name) ; Connect i on c = Dr i ver Manager . get Connect i on( ur l , user , passwd) ; St at ement s = c. cr eat eSt at ement ( ) ; A discussion of how t he DriverManager class works is out side t he scope of a discussion on BRI DGE. The point here is t hat t he variable s is a Statement obj ect , capable of issuing SQL queries t hat ret urn result set s:
Resul t Set r = s. execut eQuer y( "sel ect name, apogee f r om f i r ewor k") ; whi l e( r . next ( ) ) { St r i ng name = r . get St r i ng( "name") ; i nt apogee = r . get I nt ( "apogee") ; Syst em. out . pr i nt l n( name + ", " + apogee) ; }
CHALLENGE 6.1
Figure 6.1 shows a UML sequence diagram t hat illust rat es t he m essage flow in a t ypical JDBC applicat ion. Fill in t he m issing t ype nam es and t he m issing m essage nam e in t his illust rat ion. Figure 6.1. This diagram shows most of the typical message flow in a JDBC application.
Figure 6.1 is a se que nce dia gr a m . You can use a cla ss dia gr a m t o show t he relat ionship of t he applicat ion t o t he driver it applies. Figure 6.2 shows t he general form of an abst ract ion and it s im plem ent at ion in an applicat ion of t he BRI DGE pat t ern. This figure brings out t he separat ion of an abst ract ion and it s im plem ent at ion. The effect of
calling t he operation() m et hod depends on which im plem ent at ion of t he Implementor int erface is in place. Figure 6.2. A Bridge structure moves the abstract operations that an abstraction relies on into a separate interface.
I t can be challenging t o see how t he classes in a JDBC applicat ion align wit h t he BRI DGE st ruct ure t hat Figure 6.2 shows. The quest ions t o answer are: Which classes are abst ract ions? Which classes are " im plem ent at ions" ? Consider t he JDBCAdapter class t hat Sun Microsyst em s packages in t he JDK as a dem onst rat ion of how t o display dat a from a dat abase t able. This class im plem ent s t he TableModel int erface as shown in Figure 6.3. Figure 6.3. The JDBCAdapter class adapts information in a database table to appear in Swing JTable component.
The JDBCAdapter class const ruct or est ablishes a dat abase connect ion and creat es a Statement obj ect . The adapt er passes queries t o t he Statement obj ect and m akes t he result s available in a GUI com ponent . The JDBCAdapter class assum es t hat a client will call executeQuery() before m aking calls t o ext ract dat a from t he query. The result s of not providing a query are som ewhat unpredict able. Not icing t his, suppose t hat you decide t o subclass JDBCAdapter wit h an OzJDBCAdapter class t hat t hrows a NoQuery except ion if a client forget s t o issue a query before asking for dat a.
CHALLENGE 6.2
Draw a diagram t hat shows t he relat ionships am ong t he classes JDBCAdapter, OzJDBCAdapter, NoQuery, and Statement.
The JDBC archit ect ure clearly divides t he roles of t he driver writ er and t he applicat ion writ er. I n som e cases, t his division will not exist in advance, even if you are using drivers. You m ay be able t o set up drivers as subclasses of an abst ract superclass, wit h each subclass driving a different subsyst em . I n such a case, you can be forced t o set up a BRI DGE when you need m ore flexibilit y.
Refactoring to Bridge Som et im es, you have t o refact or an abst ract class int o a BRI DGE t o gain m ore flexibilit y in t he abst ract ion. Consider t he MachineController hierarchy at Oozinoz. Ot her t han dat abase drivers, t he m ost com m on applicat ion of drivers is t o operat e ext ernal devices. These devices m ay be peripherals, such as print ers and pen plot t ers, or any m achine capable of com m unicat ing wit h com put ers. Applicat ions at Oozinoz cont rol m ost of t he m achines on t he fact ory floor wit h t he drivers, or cont rollers, t hat Figure 6.4 shows. Figure 6.4. Instances of classes in the MachineController hierarchy drive machines in the Oozinoz factory.
The MachineController class defines t he int erface for an obj ect t hat cont rols a m achine. Subclasses provide different im plem ent at ions of t he m et hods t hat drive a m achine. Most of t he m et hods t hat Machine-Controller declares are abst ract , but it does have concret e m et hods t hat depend on it s abst ract m et hods. For exam ple, t he inputFull() m et hod is concret e, but it s effect s depend on how subclasses im plem ent getQueueMax():
publ i c bool ean i nput Ful l ( ) {
r et ur n get QueueMax( ) >= get Queue( ) . si ze( ) ; } The MachineController class is an abst ract ion whose im plem ent at ion occurs in it s subclasses, and we can consider separat ing t he abst ract ion from it s im plem ent at ion. The m ot ivat ion t o separat e an abst ract ion from it s im plem ent at ion usually occurs when a new reason for subclassing em erges. For exam ple, suppose t hat you want t o different iat e norm al cont rollers from t est ing cont rollers. A t est ing cont roller has new m et hods t hat t est or st ress m achines, such as an overflow() m et hod t hat dispat ches m at erial t o a m achine unt il t he input queue overflows and t he m achines signals an alarm . Figure 6.5 shows a design t hat provides for t est ing classes. Figure 6.5. In this factoring, subclasses of MachineController exist for various types of machines and for various types of controllers.
I nt roducing a TestController class t urns out t o force you t o creat e t hree new classes, as t est cont rollers for st ar presses and shell assem blers im plem ent getQueueMax() different ly. This is a problem because you'd like t o be able t o add new classes individually. The problem arises because t he hierarchy is fact ored according t o t wo principles: Each m achine needs it s own cont roller, and different t ypes of cont rollers are used for norm al operat ion and for t est ing. You really need a class for every com binat ion of m achine t ype and cont roller t ype—a m aint enance night m are. You can solve t he problem by separat ing t he MachineController abst ract ion from it s im plem ent at ion. To refact or a hierarchy wit h an abst ract class at it s t op int o a bridge, do t he following. 1. Move t he abst ract operat ions in t he superclass int o an int erface. 2. Define im plem ent at ion classes t hat provide different im plem ent at ions of t he int erface. 3. Redefine t he rem aining operat ions in t he abst ract class as operat ions on an inst ance of t he new int erface.
CHALLENGE 6.3
Figure 6.6 shows t he MachineController hierarchy refact ored int o a bridge. Fill in t he m issing labels. Figure 6.6. This diagram, when complete, will show the results of refactoring MachineController to apply Bridge.
refactoring MachineController to apply Bridge.
Aft er t his t ype of refact oring, you can usually change t he declarat ion of t he abst ract superclass t o be a con cr e t e cla ss. Not e, however, t hat t he class is st ill an abst ract ion in t hat it s operat ions depend on t he im plem ent at ion of abst ract operat ions defined in t he int erface.
A Bridge Using the List Interface Drivers offer good exam ples of t he BRI DGE pat t ern, but you can apply BRI DGE in ot her sit uat ions, t oo. Whenever you separat e an abst ract ion—a class t hat relies on a set of abst ract operat ions—from t he im plem ent at ion of it s abst ract operat ions, you have applied BRI DGE. Consider t he Queue class in use at Oozinoz. When using a list t o m odel a conveyor, you have discovered t hat it is easy t o get confused about which end of t he list applies t o w hich end of a conveyor. To avoid t his confusion, Oozinoz developers alw ays define t he ends of t he conveyor as t he conveyor's t ail and head and define t hat t he belt m oves from t he t ail t oward t he head. Placing m at erial on t he t ail of a conveyor enqueues t he m at erial on t he conveyor; rem oving m at erial from t he head dequeues t he m at erial from t he conveyor. Figure 6.7 shows t he Queue class. The add() m et hod adds an obj ect t o t he t ail of a list , and a call t o remove(0) rem oves t he head of a list . Figure 6.7. The Queue class defines operations that rely on an abstract List object.
CHALLENGE 6.4
Writ e t he code for dequeue() and enqueue().
Summary A com m on exam ple of BRI DGE occurs in drivers. An applicat ion t hat uses a driver is an abst ract ion—t he choice of driver det erm ines what happens when t he applicat ion runs. The int erface bet ween applicat ions and drivers let s you vary eit her side independent ly. You can creat e new applicat ions t hat use t he drivers wit hout changing t he drivers. You can also add new drivers wit hout changing exist ing applicat ions. That is t he int ent of BRI DGE. You m ay encount er abst ract ions t hat are not decoupled from t heir im plem ent at ion but rat her are arranged as an abst ract class whose concret e subclasses provide various im plem ent at ions. I n such a case, you can apply BRI DGE if you want t o fact or t he hierarchy along anot her line. Moving t he im plem ent at ions out let s each im plem ent at ion becom e a new class t hat im plem ent s a st andard int erface. This let s you add new subclasses t o t he abst ract ion, regardless of which im plem ent at ion of t he int erface you will use. This m ove also let s you add new im plem ent at ions of t he int erface wit hout affect ing t he abst ract ion hierarchy. The BRI DGE pat t ern decouples an abst ract ion from it s im plem ent at ion so t hat t he t wo can vary independent ly.
Part II: Responsibility Patterns Chapter 7. Introducing Responsibility Chapter 8. Singleton Chapter 9. Observer Chapter 10. Mediator Chapter 11. Proxy Chapter 12. Chain of Responsibility Chapter 13. Flyweight
Chapter 7. Introducing Responsibility The responsibilit y of an obj ect is com parable t o t he responsibilit y of a represent at ive in t he Oozinoz call cent er. When you order firew orks from Oozinoz, t he person you speak t o is a represent at ive for t he com pany—a proxy. He or she perform s foreseeable t asks, usually by delegat ing t asks t o ot her syst em s and people. Som et im es, t he represent at ive will delegat e a request t o a single, cent ral aut horit y who will m ediat e t he sit uat ion or will escalat e problem s up a chain of responsibilit y. Like call cent er represent at ives, ordinary obj ect s have t he inform at ion and m et hods t hey need t o operat e independent ly. Som et im es, however, you need t o cent ralize responsibilit y, divert ing from t he norm al independent operat ion of obj ect s. Several design pat t erns address t his need. Ot her pat t erns let obj ect s escalat e request s and isolat e an obj ect from ot her obj ect s t hat depend on it . Responsibilit y- orient ed pat t erns provide t echniques for cent ralizing, escalat ing, and lim it ing ordinary obj ect responsibilit y.
Ordinary Responsibility You probably have a st rong sense of how at t ribut es and responsibilit ies should com e t oget her in a well- form ed class, alt hough it can be challenging t o explain your views.
CHALLENGE 7.1
The class st ruct ure shown in Figure 7.1 has at least t en quest ionable assignm ent s of responsibilit y. Circle as m any problem s as you can find; for four of t hese point s, writ e a st at em ent of what is wrong. Figure 7.1. What's wrong with this picture?
Looking at all t he oddit ies in Figure 7.1 m ay loosen up your t hinking about appropriat e obj ect m odeling. This is a good fram e of m ind t o be in when you set out t o define t erm s, such as class. The value of defining t erm s increases as it helps people com m unicat e and decreases as it becom es a goal in it self and a source of conflict . I n t his spirit , t ake t he following difficult challenge.
CHALLENGE 7.2
Define t he qualit ies of an effect ive, useful class.
One feat ure t hat m akes a class easier t o use is t hat it s m et hod nam es suggest , accurat ely, what t he m et hods do. However, you have seen som e except ions t o t his principle in challenges from earlier chapt ers.
CHALLENGE 7.3
Give an exam ple of when t he effect of calling a m et hod differs from t he behavior im plied by t he m et hod's nam e.
Est ablishing principles for t he proper assignm ent of responsibilit y in obj ect - orient ed syst em s seem s t o be an area ripe for progress in com put er science. A syst em in which every class and m et hod clearly defines it s responsibilit ies and bears t hem correct ly is a st rong syst em , probably st ronger t han m ost syst em s we encount er t oday. I t is com m on t o speak of classes and m et hods as bearing various responsibilit ies. I n pract ice, t his usually t ranslat es int o your bearing responsibilit y for t he solid design and proper funct ioning of your code. Fort unat ely, Java offers som e relief. You can lim it t he visibilit y of your classes, fields, and m et hods and t hereby lim it your responsibilit y t o developers who use your code.
Controlling Responsibility with Visibility Consider t he SolidRocket sim ulat ion class shown in Figure 7.2. This class's const ruct or requires four param et ers t hat est ablish a rocket 's t hrust . ( The figure shows only t he burn rat e param et er; t he ot her param et ers are t he specific im pulse, propellant densit y, and burn area of t he rocket .) Suppose t hat you own t he simulation package and have a habit of declaring inst ance variables wit h t he protected m odifier. This policy let s ot her classes in your package access a SolidRocket inst ance's fields wit hout having t o call get- m et hods. This is som ewhat risky, as you have t o underst and, for exam ple, t hat changing t he burn rat e in a sim ulat ion m eans t hat you have t o recalculat e a rocket 's t hrust . Figure 7.2. Protected visibility lets classes outside your package access your code in ways that may be inappropriate.
One day, you not ice t hat anot her developer has subclassed your SolidRocket class wit h a class she calls Burrow. I n t his class, she has added a setBurnRate() m et hod t hat updat es t he burn rat e at t ribut e. She needs it because she want s t o plot a graph showing several burn rat es and t he corresponding rocket t hrust . You don't have t his m et hod in Solid-Rocket. Her approach fails because you calculat e t hrust only in t he const ruct or, when you first receive t he burn rat e. This sit uat ion com es t o your at t ent ion when she calls t o ask why t he t hrust doesn't change aft er she updat es t he burn rat e.
CHALLENGE 7.4
Summary
I n t his sit uat ion, how could you prevent ot her developers from t aking advant age of protected fields by subclassing int o classes in your package?
As a Java developer, you t ake on responsibilit y for creat ing classes t hat form a logical collect ion of at t ribut es and associat ed behaviors. You are also responsible for ensuring t hat your m et hods perform t he services im plied by t heir nam es. You can lim it your responsibilit y wit h t he proper use of visibilit y, but t o be a developer im plies t hat som e of your code m ust be available t o ot her developers.
Beyond Ordinary Responsibility Like developers, obj ect s have cert ain responsibilit ies but in a different sense. Obj ect s cannot bear a m oral, et hical, or professional responsibilit y, but we do say t hat obj ect orient ed developm ent dist ribut es responsibilit y t o individual obj ect s. This is prim arily a way of charact erizing encapsulat ion, t he idea t hat an obj ect works on it s own dat a. Obj ect - orient ed developm ent ordinarily dist ribut es responsibilit y as far as it will go, m eaning t hat each obj ect does it s own work. Dist ribut ed responsibilit y is t he norm , but several design pat t erns oppose t his and m ove responsibilit y t o an int erm ediary or t o a part icular obj ect . A cent ral figure m ay absorb cert ain responsibilit ies, or you m ay also need policies for escalat ing request s t o ot her aut horit ies. And, alt hough obj ect s are norm ally highly responsible, you m ay want t o observe t heir behavior wit hout t heir knowing it . You can t hink of t he int ent of t he OBSERVER pat t ern and several ot her pat t erns as except ions t o t he ordinary rule of dist ribut ed responsibilit y.
If you intend to • • • • • •
Centralize responsibility in a single instance of a class
Apply the pattern Singlet on ( Chapt er 8)
Decouple an object from awareness of which other Observer ( Chapt er 9) objects depend on it Centralize responsibility in a class that oversees how a set of other objects interact Let an object act on behalf of another object
Mediat or ( Chapt er 10)
Proxy ( Chapt er 11)
Allow a request to escalate up a chainof objects Chain of Responsibilit y ( Chapt er 12) until one handles it Centralize responsibility in shared, finegrained objects
Flyweight ( Chapt er 13)
The int ent of each design pat t ern is t o solve a problem in a cont ext . Responsibilit yorient ed pat t erns address cont ext s in which you need t o deviat e from t he norm al rule t hat responsibilit y should be dist ribut ed as far as possible. For exam ple, when you need t o cent ralize responsibilit y in a single inst ance of a class, you can apply t he SI NGLETON pat t ern.
Chapter 8. Singleton Obj ect s can usually act responsibly j ust by perform ing t heir own work on t heir own at t ribut es, wit hout incurring obligat ions beyond self- consist ency. Som e obj ect s, t hough,
t ake on furt her responsibilit ies, such as m odeling real- world ent it ies, coordinat ing work, or m odeling t he overall st at e of a syst em . When a part icular obj ect in a syst em bears a responsibilit y on which ot her obj ect s rely, you need a way of finding t he responsible obj ect . For exam ple, you m ight need t o find an obj ect t hat represent s a part icular m achine or a cust om er obj ect t hat can const ruct it self from dat a in a dat abase or an obj ect t hat init iat es syst em m em ory recovery. When you need t o find a responsible obj ect , t he obj ect t hat you need will, in som e cases, be t he only inst ance of it s class. For exam ple, at Oozinoz, t here is at present only one fact ory and only one Factory obj ect . I n t his case, you need SI NGLETON. The int ent of t he SI NGLETON pat t ern is t o ensure t hat a class has only one inst ance and t o provide a global point of access t o it .
Singleton Mechanics The m echanics of SI NGLETON are m ore m em orable t han it s int ent . I t is easier t o explain how t o ensure t hat a class has only one inst ance t han it is t o say why you m ight want t his rest rict ion. You m ight cat egorize SI NGLETON as a " creat ional" pat t ern, as Design Pat t erns ( Gam m a et al. 1995) does. You should, of course, t hink of pat t erns in what ever way helps you rem em ber, recognize, and apply t hem . But t he int ent of t he SI NGLETON pat t ern im plies t hat a specific obj ect bears a responsibilit y on which ot her obj ect s rely. You have som e opt ions about how you creat e an obj ect t hat t akes on a unique role. But regardless of how you creat e a singlet on, you have t o ensure t hat ot her developers don't creat e new inst ances of t he class you int end t o lim it .
CHALLENGE 8.1
How can you prevent ot her developers from const ruct ing new inst ances of your class?
When you design a singlet on class, you need t o decide when t o inst ant iat e t he single obj ect t hat will represent t he class. One choice is t o creat e t his inst ance as a st at ic field in t he class. For exam ple, t he Runtime class in java.lang includes t he line:
pr i vat e st at i c Runt i me cur r ent Runt i me = new Runt i me( ) ; This class m akes it s unique inst ance available t hrough it s public, st at ic getRuntime() m et hod. Rat her t han creat ing a singlet on inst ance ahead of t im e, you m ight wait unt il t he inst ance is first needed, or la z y- init ia liz e it . For exam ple, a Factory class m ight m ake it s single inst ance available wit h:
publ i c st at i c Fact or y get Fact or y( ) { i f ( f act or y == nul l ) { f act or y = new Fact or y( ) ; // . . . } r et ur n f act or y; }
CHALLENGE 8.2
Why m ight you decide t o lazy- init ialize a singlet on inst ance rat her t han init ialize it in it s field declarat ion?
Singletons and Threads I f you want t o lazy- init ialize a singlet on in a m ult it hreaded environm ent , you have t o t ake care t o prevent m ult iple t hreads from init ializing t he singlet on. I n a m ult it hreaded environm ent , a m et hod is not guarant eed t o run t o com plet ion before a m et hod in anot her t hread st art s running. This opens t he possibilit y, for exam ple, t hat t wo t hreads will t ry t o init ialize a singlet on at roughly t he sam e t im e. Suppose t hat a m et hod finds t hat a singlet on is null. I f anot her t hread begins execut ing at t hat m om ent , it will also find t hat t he singlet on is null. Then bot h m et hods will proceed t o init ialize t he singlet on. To prevent t his sort of cont ent ion, you need a locking abilit y t o help coordinat e m et hods running in different t hreads. The Java language and class libraries include good support for m ult it hreaded developm ent . I n part icular, Java supplies every obj ect wit h a m on it or —a lockable aspect t hat represent s possession of t he obj ect by a t hread. To ensure t hat only one t hread init ializes a singlet on, you can synchronize t he init ializat ion on t he m onit or of a suit able obj ect . Ot her m et hods t hat require exclusive access t o t he singlet on can synchronize on t he sam e m onit or. Concurrent Program m ing in Java™ ( Lea 2000) suggest s synchronizing on t he m onit or t hat belongs t o t he class it self:
package com. oozi noz. machi ne; publ i c cl ass Fact or y_2 { pr i vat e st at i c Fact or y_2 f act or y; pr i vat e st at i c f i nal Obj ect cl assLock = Fact or y_2. cl ass; pr i vat e l ong wi pMoves; pr i vat e Fact or y_2( ) { wi pMoves = 0; } publ i c st at i c Fact or y_2 get Fact or y( ) { synchr oni zed ( cl assLock) { i f ( f act or y == nul l ) { f act or y = new Fact or y_2( ) ; } r et ur n f act or y; } } publ i c voi d r ecor dWi pMove( ) {
/ / chal l enge! } // . . . } The getFactory() code ensures t hat if a second t hread begins t o lazy- init ialize t he singlet on aft er anot her t hread has begun t he sam e init ializat ion, t he second t hread will wait t o obt ain t he classLock obj ect 's m onit or. When it obt ains t he m onit or, t he second t hread will find t hat t he singlet on is no longer null. The wipMoves variable records t he num ber of t im es t hat work in process ( W I P) advances. Every t im e a bin m oves ont o a new m achine, t he subsyst em t hat causes or records t he m ove m ust call t he fact ory singlet on's recordWipMove() m et hod.
CHALLENGE 8.3
Writ e t he code for t he recordWipMove() m et hod of t he Factory_2 class.
Recognizing Singleton Unique obj ect s are not uncom m on. I n fact , m ost obj ect s in an applicat ion bear a unique responsibilit y; why would you creat e t wo obj ect s wit h ident ical responsibilit ies? Sim ilarly, nearly every class has a unique role. Why would you develop t he sam e class t wice? On t he ot her hand, singlet on classes—classes t hat allow only a single inst ance—are relat ively rare. The fact t hat an obj ect or a class is unique does not im ply t hat t he SI NGLETON pat t ern is at work. Consider t he classes in Figure 8.1. Figure 8.1. Which classes appear to apply Singleton?
CHALLENGE 8.4
Summary
For each class in Figure 8.1, say whet her it appears t o be a singlet on class, and why.
Code t hat support s SI NGLETON ensures t hat a class has only one inst ance and provides a global point of access t o it . A com m on way t o achieve t his is t hrough lazy- init ializat ion of a singlet on obj ect , inst ant iat ing it only when t he singlet on is first needed. I n a m ult it hreaded environm ent , you m ust t ake care t o m anage t he collaborat ion of t hreads t hat m ay access a singlet on's m et hods and dat a at approxim at ely t he sam e t im e. Regardless of t he m echanics t hat you apply, t he value of SI NGLETON lies in cent ralizing aut horit y in a single obj ect .
Chapter 9. Observer Client s ordinarily gat her inform at ion from an int erest ing obj ect by calling it s m et hods. But when an int erest ing obj ect changes, a problem arises: How do client s t hat depend on t he obj ect 's inform at ion know t hat t he inform at ion has changed? You m ay encount er designs t hat m ake an obj ect responsible for inform ing client s when an int erest ing aspect of t he obj ect changes. The problem wit h t his is t hat t he knowledge of which at t ribut es about an obj ect are int erest ing lies wit h t he client . The int erest ing obj ect shouldn't accept responsibilit y for updat ing t he client . One solut ion is t o arrange for client s t o be inform ed when t he obj ect changes and leave it t o t he client s t o follow up wit h int errogat ions about t he obj ect 's new st at e. The int ent of t he OBSERVER pat t ern is t o define a one- t o- m any dependency such t hat when one obj ect changes st at e, all it s dependent s are not ified and updat ed aut om at ically.
A Classic Example: Observer in Swing The m ost com m on case in which client s depend on changing obj ect s occurs in graphical user int erfaces. Whenever a user clicks a but t on or adj ust s a slider, m any obj ect s in t he applicat ion m ay need t o react t o t he change. Java Swing ant icipat es t hat you will be int erest ed in knowing when a user changes a Swing com ponent , and t he OBSERVER pat t ern is evident t hroughout Swing. Swing refers t o int erest ed client s as " list eners" and let s you regist er as m any list eners as you like t o be not ified of a com ponent 's event s. Consider a t ypical Oozinoz applicat ion wit h a Swing GUI , such as t he one t hat Figure 9.1 shows. This applicat ion let s a fireworks engineer experim ent visually wit h param et ers t hat det erm ine t he relat ionship bet ween a rocket 's t hrust and t he surface area of it s fuel. Figure 9.1. The curves shown change in real time as the user adjusts the tPeak variable with the slider.
When a solid rocket engine ignit es, t he part of it s fuel t hat is exposed t o air burns, producing t hrust . From ignit ion t o m axim um burn rat e, t he burn area increases from t he
init ial ignit ion area t o t he full surface area of t he fuel. This m axim um rat e occurs at t im e t peak . As fuel burns off, t he surface area reduces again unt il t he fuel is consum ed. Suppose t hat t he burn rat e and t hrust equat ions are:
The applicat ion in Figure 9.1 shows how t peak affect s t he burn rat e and t hrust of a rocket . As a user m oves t he slider, t he value of t peak changes, and t he curves t ake on new shapes. Figure 9.2 shows t he prim ary classes t hat m ake up t he applicat ion. The ShowBallistics_1 class and t he BallisticsPanel_1 class are m em bers of t he com.oozinoz.applications package. The BallisticsFunction int erface is a m em ber of t he com.ooz-inoz.ballistics package. That package also cont ains a Ballistics ut ilit y class t hat provides t hat inst ances of BallisticsFunction t hat define t he burn rat e and t hrust curves. Figure 9.2. The ballistics application registers itself to receive slider events.
When t he ballist ics applicat ion init ializes t he slider, t he applicat ion regist ers it self t o receive slider event s. When t he slider changes, t he applicat ion updat es t he panels t hat show t he curves and updat es t he label t hat shows t he value of t peak .
CHALLENGE 9.1
Com plet e t he slider() and stateChanged() m et hods for ShowBallistics_1 so t hat t he ballist ics panels and t he t peak label reflect t he slider's value.
publ i c J Sl i der sl i der ( ) { i f ( sl i der == nul l ) { sl i der = new J Sl i der ( ) ; sl i der Max = sl i der . get Maxi mum( ) ; sl i der Mi n = sl i der . get Mi ni mum( ) ; sl i der . addChangeLi st ener ( ?? ) ; sl i der . set Val ue( sl i der . get Mi ni mum( ) ) ; } r et ur n sl i der ; } publ i c voi d st at eChanged( ChangeEvent e) { doubl e val = sl i der . get Val ue( ) ; doubl e t p = ( val - sl i der Mi n) / ( sl i der Max - sl i der Mi n) ; bur nPanel ( ) . ?? ( ?? ) ; t hr ust Panel ( ) . ?? ( ?? ) ; val ueLabel ( ) . ?? ( ?? ) ; } The ShowBallistics_1 class updat es t he burn panel, t hrust panel, and value label obj ect s t hat depend on t he slider's value. This is not uncom m on and not necessarily bad code, but not e: This code com plet ely undoes t he int ent of OBSERVER! Swing applies OBSERVER so t hat t he slider is not responsible for knowing which client s are int erest ed in it . The ShowBallistics_1 code regist ers a single dependent obj ect —it self—t hat dispat ches changes t o int erest ed obj ect s. This obj ect t akes on responsibilit y for knowing w hich client s depend on t he slider, inst ead of let t ing each dependent obj ect regist er it self. To be consist ent wit h OBSERVER, you can m ake a few changes in t he code t o let each int erest ed com ponent regist er it self t o receive t he slider's change event s.
CHALLENGE 9.2
Provide a new class diagram showing a design t hat let s each int erest ed obj ect regist er for slider event s. Be sure t o account for t he label t hat shows t he slider's value.
I n t his design, you can m ove t he calls t o addChangeListener() out of t he slider() m et hod and int o t he const ruct ors of t he dependent com ponent s:
publ i c Bal l i st i csPanel _2( Bal l i st i csFunct i on f unc, J Sl i der sl i der )
{ t hi s. f unc = f unc; t hi s. sl i der = sl i der ; sl i der . addChangeLi st ener ( t hi s) ; } When t he slider changes, t he BallisticsPanel_2 obj ect is not ified. The label recalculat es it s tPeak value and repaint s it self:
publ i c voi d st at eChanged( ChangeEvent e) { doubl e val = sl i der . get Val ue( ) ; doubl e max = sl i der . get Maxi mum( ) ; doubl e mi n = sl i der . get Mi ni mum( ) ; t Peak = ( val - mi n) / ( max - mi n) ; r epai nt ( ) ; } A new problem em erges in t his refact oring. The design adj ust s responsibilit ies so t hat each int erest ed obj ect regist ers for and react s t o changes in t he slider. The dist ribut ion of responsibilit y is good, but now each com ponent t hat list ens t o t he slider needs t o recalculat e t he value of tPeak. I n part icular, if you use a BallisticsLabel_2 class, as t he solut ion t o Challenge 9.2 does, it s stateChanged() m et hod will be nearly ident ical t o t he stateChanged() m et hod of BallisticsPanel_2. To consolidat e t his duplicat ed code, we can refact or again, ext ract ing an underlying dom ain obj ect from t he current design.
Model/View/Controller As applicat ions and syst em s grow, it is im port ant t o divide and redivide responsibilit y so t hat classes and packages st ay sm all enough t o m aint ain. The phrase m odel/ view/ cont roller ( MVC) refers t o separat ing an int erest ing obj ect —t he m odel—from GUI elem ent s t hat port ray it —t he view and t he cont roller. Java support s t his separat ion of responsibilit y wit h it s OBSERVER m echanics. The init ial versions of t he ShowBallistics applicat ion com bine int elligence about an applicat ion GUI wit h inform at ion about ballist ics. You can refact or t his code, following MVC t o divide t his applicat ion's responsibilit ies. I n t his refact oring, t he revised ShowBallistics class should ret ain t he views and cont rollers in it s GUI elem ent s. The creat ors of MVC envisioned t hat t he look of a com ponent —it s " view" —m ight be separable from it s feel—it s " cont roller." I n pract ice, t he appearance of a GUI com ponent and it s support for user int eract ion are t ight ly coupled, and Swing does not divide views from cont rollers. The value of MVC is t o push t he " m odel" out of an applicat ion int o it s own dom ain. When you divide GUI obj ect s from business obj ect s, you can creat e layers of code. A la ye r is a group of classes wit h sim ilar responsibilit ies, oft en collect ed in a single Java package. Higher layers, such as a GUI layer, usually depend only on classes in equal or lower layers. Layering usually includes a clear definit ion of t he int erfaces bet ween layers, such as a GUI and t he business, or dom ain, obj ect s it represent s. This opens t he possibilit y of arranging for different layers t o execut e on different com put ers. A layer t hat execut es on a com put er is a t ie r in an n - t ie r syst em . You can reorganize t he responsibilit ies of t he ShowBallistics code, achieving a layered syst em , as Figure 9.3 shows. Figure 9.3. By creating an observable Tpeak class you can separate a business logic layer from a GUI layer.
The design t hat Figure 9.3 illust rat es creat es a Tpeak class t o m odel t he t peak value, t he crit ical value in t he ballist ics equat ions t hat t he applicat ion displays. The BallisticsPanel and BallisticsLabel classes depend on Tpeak. Rat her t han m aking a Tpeak obj ect responsible for updat ing GUI elem ent s, t he design applies OBSERVER so t hat int erest ed obj ect s can regist er for not ificat ion when Tpeak changes. The Java class libraries support s t his, providing an Observable class and an Observer int erface in t he java.util package. The Tpeak class can subclass Observable and can updat e it s observers when it s value changes.
publ i c voi d set Val ue( doubl e val ue) { t hi s. val ue = val ue; set Changed( ) ; not i f yObser ver s( ) ; } Not e t hat you have t o call setChanged() so t hat t he Observable code will broadcast t he change. ( I t is quest ionable whet her a design t hat requires t wo st eps t o not ify observers is ideal.) The notifyObservers() m et hods calls t he update() m et hod of each regist ered observer. The update() m et hod is a requirem ent for im plem ent ers of t he Observer int erface, as Figure 9.4 shows. Figure 9.4. A BallisticsLabel is an Observer; it can register its interest in an Observable object so that the label's update() method is called when the Observable object changes.
A BallisticsLabel obj ect need not ret ain a reference t o t he Tpeak obj ect it observes. Rat her, t he BallisticsLabel const ruct or can regist er for updat es w hen t he Tpeak obj ect changes. The label's update() m et hod will receive t he Tpeak obj ect as it s Observable argum ent . The m et hod can cast t he argum ent t o Tpeak, ext ract t he new value, change t he label's t ext , and repaint .
CHALLENGE 9.3
Writ e t he com plet e code for BallisticsLabel.java.
The new design of t he ballist ics applicat ion separat es a business obj ect from t he GUI elem ent s t hat represent it . Two requirem ent s m ust be m et for t his design t o work.
1. Implementations of Observer must register their interest and must update themselves appropriately, often including repainting themselves. 2. Subclasses of Observable must remember to notify observers when their values change. These t wo st eps set up m ost of t he wiring you need across layers in t he ballist ics applicat ion. You also need t o arrange for a Tpeak obj ect t o change when t he applicat ion's slider changes. You can achieve t his wit hout writ ing a class, by inst ant iat ing an anonym ous subclass of ChangeListener.
CHALLENGE 9.4
Suppose t hat tPeak is an inst ance of Tpeak and an at t ribut e of t he ShowBallistics class. Com plet e t he code for ShowBallistics.slider() so t hat t he slider's changes updat e tPeak.
publ i c J Sl i der sl i der ( ) { i f ( sl i der == nul l ) { sl i der = new J Sl i der ( ) ; sl i der Max = sl i der . get Maxi mum( ) ; sl i der Mi n = sl i der . get Mi ni mum( ) ; sl i der . addChangeLi st ener ( new ChangeLi st ener ( ) {
{ ?? } ); sl i der . set Val ue( sl i der . get Mi ni mum( ) ) ; } r et ur n sl i der ; } When you apply MVC, t he flow of event s m ay seem indirect . Slider m ovem ent in t he ballist ics applicat ion causes a ChangeListener t o updat e a Tpeak obj ect . I n t urn, a change in t he Tpeak obj ect not ifies t he applicat ion's label and panels, and t hese obj ect s repaint t hem selves. Change propagat es from t he GUI layer t o t he business layer and back up t o t he GUI layer.
CHALLENGE 9.5
Fill in t he m essages in Figure 9.5. Figure 9.5. MVC causes calls to pass from a GUI layer into a business layer and back into the GUI layer.
The payback for a layered design is in t he value of t he int erface and in t he independence t hat you get bet ween t he layers. The layering of code is a layering of responsibilit y, which m akes t he code easier t o m aint ain. For exam ple, in t he ballist ics exam ple, you can add a second GUI , perhaps for a handheld device, wit hout having t o change classes in t he business obj ect layer. I n t he business obj ect layer, you m ight add a new source of change t hat updat es a Tpeak obj ect . I n t his case, t he OBSERVER m echanics you have in place will aut om at ically updat e obj ect s in t he GUI layer.
Anot her advant age of layering code is t hat layering support s a m ove t o an n- t ier archit ect ure, wit h layers execut ing as t iers on different com put ers. This m inim izes t he am ount of code t hat m ust execut e on a user's deskt op. I t also let s you m ake changes in business classes wit hout updat ing soft ware on user m achines, great ly sim plifying deploym ent . I n short , OBSERVER support s MVC. MVC support s n- t ier com put ing, which brings m any pract ical advant ages t o soft ware developm ent and deploym ent .
Maintaining an Observable Object You m ay not always be able t o m ake t he class you want t o observe a subclass of Observable. I n part icular, your class m ay already be a subclass of som et hing ot her t han Object. I n t his case, you can provide your class wit h an Observable obj ect and have your class forward key m et hod calls t o it . The Component class in java.awt uses t his approach, alt hough it uses a PropertyChangeSupport obj ect inst ead of an Observable obj ect . The PropertyChangeSupport class is quit e sim ilar t o t he Observable class but is part of t he java.beans package. The JavaBeans API exist s t o support t he creat ion of reusable com ponent s. This API has found it s great est applicabilit y t o GUI com ponent s, but you can cert ainly apply it elsewhere. The Component class uses a PropertyChangeSupport obj ect t o let int erest ed observers regist er for not ificat ion of changes in t he propert ies of labels, panels, and ot her GUI com ponent s. Figure 9.6 shows t he relat ionship bet ween t he Component class from java.awt and t he PropertyChangeSupport class. Figure 9.6. A Component object maintains a PropertyChangeSupport object that maintains a collection of listeners.
The Component class duplicat es part of t he int erface of t he Prop-ertyChangeSupport class. These m et hods in Component each forward t he m essage call t o an inst ance of t he PropertyChangeSupport class.
CHALLENGE 9.6
Com plet e t he class diagram in Figure 9.7 t o show Tpeak using a PropertyChangeSupport obj ect t o m anage list eners. Figure 9.7. A Tpeak business object can delegate calls that affect listeners to a PropertyChangeSupport object.
Whet her you use Observer, PropertyChangeSupport, or anot her class t o est ablish t he OBSERVER pat t ern, t he point is t o define a one- t o- m any dependency am ong obj ect s. When one obj ect changes st at e, all it s dependent s are not ified and updat ed aut om at ically. This lim it s t he responsibilit y and eases m aint enance of bot h int erest ing obj ect s and t heir int erest ed observers.
Summary The OBSERVER pat t ern appears m ost frequent ly in GUI applicat ions and is a fundam ent al aspect of Java's Swing archit ect ure. Wit h Swing, you never have t o change or subclass a com ponent class j ust t o com m unicat e it s event s t o an int erest ed obj ect . For sm all applicat ions, a com m on pract ice is t o regist er a single obj ect t o receive all t he event s in a GUI . Doing so has no inherent problem , but you should recognize t hat it reverses t he dist ribut ion of responsibilit y t hat OBSERVER int ends. For a large GUI , consider let t ing each int erest ed obj ect regist er for event s rat her t han int roducing a m ediat or. OBSERVER let s you delineat e t he responsibilit y bet ween business obj ect s and a GUI , which allows you t o est ablish an MVC design. MVC let s you creat e loosely coupled layers t han can change independent ly and t hat m ay execut e on different m achines. When you decide t o apply OBSERVER, you can writ e t he regist rat ion and list ening m echanism s yourself, but Java includes t wo packages wit h classes t hat have t hese m echanics. You can use t he Observable class and t he Observer int erface in java.util. Alt ernat ively, you can use t he PropertyChangeSupport class and PropertyChangeListener in java.beans. The m echanics of t hese class/ int erface pairs are sim ilar. I n part icular, you can use eit her an Observable obj ect or a PropertyChangeSupport obj ect when your int erest ing class needs t o im plem ent OBSERVER t hrough delegat ion inst ead of subclassing.
The value of OBSERVER rem ains t he sam e whet her you effect it t hrough subclassing or delegat ion and regardless of t he classes you use t o support it . OBSERVER let s client s know when an int erest ing obj ect changes, at t he sam e t im e m inim izing t he responsibilit y of t he int erest ing obj ect .
Chapter 10. Mediator Ordinary obj ect - orient ed developm ent dist ribut es responsibilit y as far as it will go, wit h each obj ect doing it s own work independent ly. The OBSERVER pat t ern support s t his dist ribut ion by m inim izing t he responsibilit y of an obj ect t hat ot her obj ect s find int erest ing. The SI NGLETON pat t ern resist s t he dist ribut ion of responsibilit y t o let you cent ralize respon- sibilit y in part icular obj ect s t hat client s locat e and reuse. The MEDI ATOR pat t ern t oo cent ralizes responsibilit y but for a part icular set of obj ect s rat her t han for all t he client s in a syst em . Providing a cent ral aut horit y for a group of obj ect s is useful when t he int eract ions t he obj ect s gravit at e t oward t he com plex condit ion in which every obj ect is aware of every ot her obj ect in t he group. Cent ralizat ion of responsibilit y is also useful when t he logic surrounding t he int eract ions of t he relat ed obj ect s is independent of t he ot her behavior of t he obj ect s. The MEDI ATOR pat t ern defines an obj ect t hat encapsulat es how a set of obj ect s int eract . This prom ot es loose coupling, keeping t he obj ect s from referring t o one anot her explicit ly, and let s you vary t heir int eract ion independent ly.
A Classic Example: GUI Mediators You will probably m ost oft en encount er t he MEDI ATOR pat t ern when you develop an applicat ion wit h a GUI . Such applicat ions t end t o becom e t hick, gat hering code t hat you can refact or int o ot her classes. The FlightPanel class in Chapt er 4, Facade, init ially perform ed t hree roles. Before you refact ored it , t his class act ed as a display panel, as a com plet e GUI applicat ion, and as a flight pat h calculat or. Aft er refact oring, t he applicat ion t hat launches t he flight panel display becam e sim ple, cont aining j ust a few lines of code. Large applicat ions, however, can rem ain com plex aft er t his t ype of refact oring, even when t hey cont ain j ust t he logic t hat creat es com ponent s and t hat arranges for t he com ponent s' int eract ion. Consider t he applicat ion in Figure 10.1. Oozinoz st ores chem ical bat ches in rubber t ubs supplied by t he Dubdub Rubber Tub com pany. Machines at Oozinoz read bar codes on t he t ubs t o keep t rack of where t ubs are in t he fact ory. Som et im es, t hough, a m anual override is necessary, part icularly when hum ans m ove a t ub inst ead of wait ing for a robot t o t ransfer it . I n t his case, t he applicat ion in Figure 10.1 let s a user specify t he m achine at which a t ub is locat ed. The user t ypes in t he t ub I D, chooses a m achine from a list , and clicks Ok! t o m ake t he associat ion. Figure 10.2 shows t he applicat ion class. Figure 10.1. This application lets its user manually update the location of a tub of chemicals.
Figure 10.2. The PlaceATub_1 class has a mix of component-building and event-handling methods.
About half of t he m et hods in PlaceATub_1 exist t o lazy- init ialize variables t hat cont ain t he applicat ion's com ponent s. The textField() m et hod is t ypical:
publ i c J Text Fi el d t ext Fi el d( ) {
i f ( t ext Fi el d == nul l ) { t ext Fi el d = new J Text Fi el d( ) ; t ext Fi el d. set Font ( f ont ( ) ) ; t ext Fi el d. addAct i onLi st ener ( t hi s) ; t ext Fi el d. addFocusLi st ener ( t hi s) ; } r et ur n t ext Fi el d; } Most of t he rem aining m et hods in PlaceATub cont ain logic t hat handles t he applicat ion's event s. The focusGained() and focusLost() m et hods, for exam ple, m anage t he effect s of t he user's t abbing in and out of t he t ext field:
publ i c voi d f ocusGai ned( FocusEvent e) { t ext Fi el d. sel ect Al l ( ) ; } publ i c voi d f ocusLost ( FocusEvent e) { t ext Fi el dChanged( ) ; } The textFieldChanged() m et hod updat es t he affect ed m achine and t ub obj ect s. Not e t hat t he MEDI ATOR pat t ern is at work in t his class. Com ponent s don't updat e one anot her direct ly; rat her, t hey issue event s t hat a class can regist er for and react t o. A PlaceATub_1 obj ect encapsulat es how t he com ponent s in a GUI int eract . The m echanics of Swing nudge you int o using a m ediat or, alt hough not hing in Swing suggest s t hat an applicat ion m ust be it s own m ediat or. I nst ead of m ingling com ponent - creat ion m et hods wit h event - handling m et hods in one class, you can m ove all event handling int o a separat e m ediat or class. This refact oring result s in a pair of sim pler classes, wit h t wo specializat ions: com ponent creat ion and event handling.
CHALLENGE 10.1
Com plet e t he diagram in Figure 10.3 t o show a refact oring of PlaceATub_1, int roducing a new PlaceATubMediator class t o receive t he event s of t he PlaceATub GUI . Figure 10.3. Separate the component-building and event-handling parts of the application.
The refact oring consolidat es t he m ediat or int o a separat e class, let t ing you develop it and focus on it separat ely. Now when t he PlaceATub applicat ion runs, com ponent s pass event s t o a PlaceATubMediator obj ect . This obj ect m ay call back com ponent s in t he GUI : for exam ple, t o select all t he t ext in t he t ext field when t he user t abs int o it . The m ediat or m ay also call dom ain obj ect s: for exam ple, calling a Tub obj ect 's setMachine() m et hod when t he user clicks t he Ok! but t on.
CHALLENGE 10.2
Draw a diagram t hat shows what happens when t he user clicks t he Ok! but t on. Show t he but t on, t he m ediat or, t he affect ed t ub, and t he sequence of m essages t hat pass am ong t hese obj ect s.
Java Swing com ponent s apply t he MEDI ATOR pat t ern as a m at t er of course, not ifying a m ediat or when event s occur rat her t han t aking responsibilit y for updat ing ot her com ponent s direct ly. Swing applicat ions give rise t o perhaps t he m ost com m on applicat ion of t he MEDI ATOR pat t ern, but you m ay want t o int roduce a m ediat or in ot her cases as well. Whenever t he int eract ion of a set of obj ect s is com plex, you can cent ralize t he responsibilit y for t his int eract ion in a m ediat or obj ect t hat st ands out side t he int eract ing group. This prom ot es loose coupling—a reduct ion in t he responsibilit y t hat t he int eract ing obj ect s bear t o one anot her. Managing t he int eract ion of obj ect s in an independent class also has t he advant age of sim plifying and st andardizing t he int eract ion rules. One exam ple of t he value of a m ediat or occurs when you need t o m anage relat ional int egrit y.
Relational Integrity An obj ect m odel is relat ionally consist ent if every t im e obj ect a point s t o obj ect b, obj ect b point s t o obj ect a. For a m ore rigorous definit ion, consider t wo classes: Alpha and Beta. Let A represent t he set of obj ect s in t he m odel t hat are inst ances of class Alpha, and let B represent t he set of obj ect s t hat are inst ances of class Beta. Let a and b denot e m em bers of A and B, and let t he ordered pair ( a, b) denot e t hat obj ect a A has a reference t o obj ect b B. This reference m ay be eit her a direct reference or one of a set of references, as when obj ect a has a List obj ect t hat includes b. The Cart esian product A x B is t he set of all possible ordered pairs ( a, b) wit h a A and b B. The set s A and B allow t he t wo Cart esian product s A x B and B x A. An obj ect m odel relat ion on A and B is t he subset of A x B t hat exist s in an obj ect m odel. Let AB denot e t his subset , and let BA denot e t he subset of B x A t hat exist s in t he m odel. Any binary relat ion R
A x B has an inverse R–1
B x A defined by:
The inverse of AB provides t he set of references t hat m ust occur from inst ances of B t o inst ances of A if t he obj ect m odel is consist ent . I n ot her words, inst ances of classes Alpha and Beta are relat ionally consist ent if and only if BA is t he inverse of AB.
Relational Integrity Mediators Part of t he st rengt h of t he obj ect - orient ed paradigm is t hat it let s you easily m ap connect ions bet ween Java obj ect s and obj ect s in t he real world. However, a Java obj ect m odel's abilit y t o reflect t he real world has at least t wo fundam ent al deficit s. First , obj ect s in t he real world vary wit h t im e, but no support for t his is built int o Java. For exam ple, assignm ent st at em ent s oblit erat e any previous value inst ead of rem em bering it , as a hum an would. Second, in t he real world, relat ions are as im port ant as obj ect s, but relat ions receive lit t le support in t oday's obj ect - orient ed languages, including Java. For exam ple, t here is no built - in support for t he fact t hat if St ar Press 2402 is in bay 1, bay 1 m ust cont ain St ar Press 2402. I n fact , it is quit e possible for such relat ions t o go awry in Java, which in t urn invit es t he applicat ion of t he MEDI ATOR pat t ern. Table 10.1. Modeling a Relationship
TUB T305 T308 T377 T379 T389 T001 T002
MACHINE StarPress-2402 StarPress-2402 ShellAssembler-2301 ShellAssembler-2301 ShellAssembler-2301 Fuser-2101 Fuser-2101
Consider t he rubber t ubs of chem icals at Oozinoz. Tubs are always assigned t o a part icular m achine. You can m odel t his relat ionship wit h a t able, as Table 10.1 shows. Recording relat ional inform at ion in a t able let s you preserve relat ional int egrit y. I n Table 10.1, keeping t he Tub colum n unique guarant ees t hat no t ub can appear on t wo m achines at once. Table 10.1 shows t he r e la t ion of t ubs and m achines—t he way in which t hey st and wit h regard t o each ot her. Mat hem at ically, a relat ion is a subset of all ordered pairs of obj ect s, so t here is a relat ion of t ubs t o m achines and a relat ion of m achines t o t ubs. See t he sidebar Relat ional I nt egrit y on page 108 for a m ore st rict definit ion of relat ional consist ency in an obj ect m odel. When you record t ub and m achine relat ional inform at ion in a t able, you can guarant ee t hat each t ub is on only one m achine by enforcing t he rest rict ion t hat each t ub occurs only once in t he Tub colum n. The m ost com m on way t o do t his is t o m ake t he Tub colum n t he prim ary key of t he t able in a relat ional dat abase. Wit h t his m odel, as in realit y, a t ub cannot appear on t wo m achines at once. I n an obj ect m odel, such a guarant ee is m ore difficult t o m ake, because t he relat ion inform at ion is dist ribut ed t o t he m odeled obj ect s, as Figure 10.4 shows. Figure 10.4. An object model distributes information about relations.
The arrowheads in Figure 10.4 em phasize t hat t ubs know about m achines and t hat m achines know about t ubs. I n a t ypical im plem ent at ion, each Tub obj ect would have a Machine at t ribut e t o indicat e it s current locat ion, and each Machine obj ect would have a list of Tub obj ect s represent ing t ubs locat ed at t he m odeled m achine. The dist ribut ion of t he t ub/ m achine relat ionship m akes t he m anagem ent of t he relat ionship m ore difficult and m akes t his m anagem ent a candidat e for applicat ion of t he MEDI ATOR pat t ern. Consider a defect t hat occurred at Oozinoz when a developer began m odeling a new m achine t hat included a bar code reader for ident ifying t ubs. Aft er scanning a t ub for it s I D, t he developer set t he locat ion of t ub b t o m achine m wit h t he following code:
/ / t el l t ub about machi ne, and machi ne about t ub t . set Machi ne( m) ; m. addTub( t ) ;
CHALLENGE 10.3
Suppose t hat t he obj ect t represent s t ub T308 and t hat t he obj ect m represent s t he m achine Fuser-2101. Com plet e t he obj ect diagram in Figure 10.5, showing t he effect s of t he code t hat updat es t he t ub's locat ion. What defect does t his reveal? Figure 10.5. When completed, this diagram will show the flaw in a developer's code that updates a tub's location.
The easiest way t o guarant ee relat ional consist ency is t o pull t he relat ional inform at ion back int o a single t able m anaged by a m ediat ing obj ect . I nst ead of having t ubs know about m achines and m achines know about t ubs, you can give all t hese obj ect s a reference t o a m ediat or t hat keeps a single t able of t ubs and m achines. The java.util package provides t he Map int erface, along wit h several im plem ent ing classes, t o st ore key/ value pairs. Figure 10.6 shows a class diagram wit h a m ediat or in place. The Mediator class uses a Map obj ect t o st ore t he t ub/ m achine relat ionship:
package com. oozi noz. machi ne; i mpor t j ava. ut i l . *; i mpor t com. oozi noz. chemi cal . Tub; publ i c cl ass Medi at or { pr ot ect ed Map t ubToMachi ne = new HashMap( ) ; publ i c Machi ne get Machi ne( Tub t ) { r et ur n ( Machi ne) t ubToMachi ne. get ( t ) ; } publ i c Set get Tubs( Machi ne m) { Set set = new HashSet ( ) ; I t er at or i = t ubToMachi ne. ent r ySet ( ) . i t er at or ( ) ; whi l e ( i . hasNext ( ) ) { Map. Ent r y e = ( Map. Ent r y) i . next ( ) ; i f ( e. get Val ue( ) . equal s( m) ) { set . add( e. get Key( ) ) ; } }
r et ur n set ; } publ i c voi d set ( Tub t , Machi ne m) { t ubToMachi ne. put ( t , m) ; } } Figure 10.6. The Mediator pattern lets you define an object that encapsulates how tubs and machines interact.
CHALLENGE 10.4
Writ e t he code for t he Tub m et hods getMachine() and setMachine().
When you have an obj ect m odel t hat is not t ied t o a relat ional dat abase, you can use m ediat ors t o sust ain t he relat ional int egrit y of your m odel. Move t he logic t hat get s and set s relat ions bet ween obj ect s int o m ediat or classes. Such classes can specialize in m aint aining relat ional int egrit y and can apply t ablelike dat a st ruct ures t hat nat urally guarant ee int egrit y.
CHALLENGE 10.5
Summary
Wit h respect t o fact oring logic out of a class, MEDI ATOR is sim ilar t o ot her pat t erns. List t wo pat t erns t hat m ay involve m oving an aspect of behavior out of an exist ing class or hierarchy.
The MEDI ATOR pat t ern prom ot es loose coupling, keeping relat ed obj ect s from referring t o one anot her explicit ly. MEDI ATOR shows up m ost oft en in GUI applicat ion developm ent , in which you don't want t o m anage t he com plexit y of individual widget s' updat ing one anot her. The archit ect ure of Swing guides you in t his direct ion, encouraging you t o define obj ect s t hat regist er for GUI event s. I f you are developing wit h Swing, you are probably applying MEDI ATOR. Swing nudges you int o using MEDI ATOR, but Swing does not force you t o m ove t his m ediat ion out side an applicat ion class. Doing so can sim plify your code. You can let a m ediat or class concent rat e on t he int eract ion bet ween obj ect s and let an applicat ion class concent rat e on com ponent const ruct ion. There are ot her cases in w hich you can benefit from int roducing a m ediat or obj ect . For exam ple, you m ight need a m ediat or t o cent ralize responsibilit y for m aint aining t he relat ional int egrit y in an obj ect m odel. You can apply MEDI ATOR whenever you need t o define an obj ect t hat encapsulat es how a set of obj ect s int eract .
Chapter 11. Proxy An ordinary obj ect does it s own work in support of t he public int erface t hat it advert ises. I t can happen, t hough, t hat a legit im at e obj ect cannot live up t o t his ordinary responsibilit y. This occurs m ost frequent ly when an obj ect t akes a long t im e t o load or w hen t he obj ect is running on anot her com put er. I n t hese cases, a proxy obj ect can t ake t he responsibilit y t hat a client expect s and forward t he request appropriat ely t o an underlying t arget obj ect . The int ent of t he PROXY pat t ern is t o provide a surrogat e, or placeholder, for anot her obj ect t o cont rol access t o it .
A Classic Example: Image Proxies While conduct ing plant t ours, t he Oozinoz public relat ions m anager want s t o pause at one point t o show im ages of previous exhibit ions. Your design t eam decides t o sat isfy t his requirem ent wit h a PC- based Java applicat ion wit h a Swing GUI . You m ight convert t o a browser- based t hin- client design in t he fut ure, but in any case, you can rely on Java's ext ensive support for loading, m anipulat ing, and displaying im ages. As you st art const ruct ing t he applicat ion, realize t hat you will want t o load som e im ages only on dem and. You m ight m ake clickable, t hum bnail versions of larger im ages, but in t he first pass, you decide t o show prebuilt Absent and Loading… im ages. Figure 11.1 shows t hree screen shot s of t he syst em you int end t o const ruct . ( The code t hat displays t he windows t hat Figure 11.1 shows is in t he ShowProxy class in t he com.oozinoz.applications package. The underlying code t hat support s t he applicat ion is in t he com.oozinoz.imaging package.) Figure 11.1. Three screen shots show a mini-application before, during, and after loading a large image. (Photo courtesy of Corbis, Inc.)
The user int erface displays one of t hree im ages: one indicat ing t hat loading has not begun, one indicat ing t hat t he real im age is loading, or t he real im age. When t he applicat ion st art s, it shows Absent , a JPEG im age t hat you have built in an im ageprocessing t ool. When t he user clicks Load, t he im age changes alm ost inst ant ly t o anot her prebuilt im age, Loading… . Aft er a few m om ent s, t he desired im age appears. An easy way t o display a im age saved in, say, a JPEG file is t o use an ImageIcon obj ect as an argum ent t o a " label" t hat will show t he im age:
I mageI con i = new I mageI con( "Fest . j pg") ; J Label l = new J Label ( i ) ; I n t he applicat ion t hat you are building, you want t o pass int o JLabel a proxy t hat will forward paint ing request s t o Absent , Loading…, or t he desired im age. The m essage flow m ight look like t he sequence diagram in Figure 11.2. Figure 11.2. An ImageIconProxy object forwards paint() requests to the current ImageIcon object.
When t he user clicks Load, your code will cause t he ImageIconProxy obj ect t o change it s current im age t o be t he Loading… im age. The proxy will also begin loading t he desired im age. When t he desired im age is com plet ely loaded, t he ImageIconProxy obj ect will change it s current im age t o be t he desired im age. To set up a proxy, you can creat e a subclass of ImageIcon, as Figure 11.3 shows. The code for ImageIconProxy defines t wo st at ic variables t hat cont ain t he Absent and Loading… im ages:
st at i c f i nal I mageI con ABSENT = new I mageI con( "absent . j pg") ; st at i c f i nal I mageI con LOADI NG = new I mageI con( "l oadi ng. j pg") ; Figure 11.3. An ImageIconProxy object can stand in for an ImageIcon object because an ImageIconProxy object is an ImageIcon object.
The const ruct or for ImageIconProxy accept s t he nam e of an im age file t o event ually load. When an ImageIconProxy obj ect 's load() m et hod is called, it set s t he im age t o LOADING and st art s a separat e t hread t o load t he im age. Using a separat e t hread keeps t he applicat ion from hanging while t he im ages loads. The load() m et hod accept s a JFrame obj ect t hat t he run() m et hod calls back once t he desired im age is loaded. The alm ost - com plet e code for ImageIconProxy.java is:
pacakage com. oozi noz. i magi ng; i mpor t j ava. awt . *; i mpor t j avax. swi ng. *; publ i c cl ass I mageI conPr oxy ext ends I mageI con i mpl ement s Runnabl e { st at i c f i nal I mageI con ABSENT = new I mageI con( "absent . j pg") ; st at i c f i nal I mageI con LOADI NG = new I mageI con( "l oadi ng. j pg") ; I mageI con cur r ent = ABSENT; pr ot ect ed St r i ng f i l ename; pr ot ect ed J Fr ame cal l backFr ame; publ i c I mageI conPr oxy( St r i ng f i l ename) { super ( ABSENT. get I mage( ) ) ; t hi s. f i l ename = f i l ename; } publ i c voi d l oad( J Fr ame cal l backFr ame) { t hi s. cal l backFr ame = cal l backFr ame; cur r ent = LOADI NG; cal l backFr ame. r epai nt ( ) ; new Thr ead( t hi s) . st ar t ( ) ; } publ i c voi d r un( ) { cur r ent = new I mageI con( f i l ename) ; cal l backFr ame. pack( ) ; } publ i c i nt get I conHei ght ( ) { / / chal l enge! } publ i c i nt get I conWi dt h( ) {
/ / chal l enge! } publ i c synchr oni zed voi d pai nt I con( Component c, Gr aphi cs g, i nt x, i nt y) { / / chal l enge! } }
CHALLENGE 11.1
An ImageIconProxy obj ect accept s t hree im age display calls t hat it m ust pass on t o t he current im age. Writ e t he code for getIconHeight(), getIconWidth(), and paintIcon() for t he ImageIconProxy class.
Suppose t hat you get t he code working for t his sm all dem onst rat ion applicat ion. Before you build t he real applicat ion, which has m ore t han j ust a Load but t on, you hold a design review, and t he fragilit y of your design com es t o light . As wit h m any proxies, t here is a dangerous dependency bet ween ImageIcon and ImageIconProxy. The current design assum es t hat you have correct ly ascert ained, or guessed, t he right m et hods t o override. Even if you have, t he ImageIcon class can change in a fut ure release and break your code. You and t he design review t eam decide t o rework your approach.
Image Proxies Reconsidered At t his point , you m ight ask whet her design pat t erns have helped you. You fait hfully im plem ent ed a pat t ern, and now you're looking at t earing it out . I n fact , t his is nat ural and healt hy, alt hough it appears m ore oft en in applied developm ent t han in books. An aut hor, wit h t he help of his or her reviewers, can ret hink and replace an inferior design before any reader sees it . I n pract ice, a design pat t ern can help you get an applicat ion running and can facilit at e t he discussion of your design. I n t he ImageIconProxy exam ple at Oozinoz, t he pat t ern has served it s purpose, even t hough it is m uch sim pler t o achieve t he effect you desire wit hout a lit eral im plem ent at ion of a proxy. The ImageIcon class operat es on an Image obj ect . Rat her t han forwarding paint ing request s t o a separat e ImageIcon obj ect , it is easier t o operat e on t he Image obj ect t hat ImageIcon wraps. Figure 11.4 shows an ImageIconLoader class, from com.oozinoz.imaging, t hat has j ust t wo m et hods beyond it s const ruct or: load() and run(). Figure 11.4. The ImageIconLoader class works by switching the Image object that it holds.
The load() m et hod in t his revised class st ill receives a JFrame obj ect t o call back aft er t he desired im age is loaded. When load() execut es, it calls setImage() wit h t he im age held by LOADING, repaint s t he fram e, and st art s a separat e t hread for it self. The run() m et hod, execut ing in a separat e t hread, creat es a new ImageIcon obj ect for t he file nam ed in t he const ruct or, calls setImage() wit h t he im age held by t his obj ect , and repacks t he fram e. The alm ost - com plet e code for ImageIconLoader.java is:
i mpor t j ava. awt . *; i mpor t j avax. swi ng. *; publ i c cl ass I mageI conLoader ext ends I mageI con i mpl ement s Runnabl e { st at i c f i nal I mageI con ABSENT = new I mageI con( "absent . j pg") ; st at i c f i nal I mageI con LOADI NG = new I mageI con( "l oadi ng. j pg") ; pr ot ect ed St r i ng f i l ename; pr ot ect ed J Fr ame cal l backFr ame; publ i c I mageI conLoader ( St r i ng f i l ename) { super ( ABSENT. get I mage( ) ) ; t hi s. f i l ename = f i l ename; } publ i c voi d l oad( J Fr ame cal l backFr ame) { / / chal l enge! } publ i c voi d r un( ) { / / chal l enge!
} }
CHALLENGE 11.2
Fill in t he code for load() and run() in ImageIconLoader.
The revised code is less coupled t o t he design of ImageIcon, relying prim arily on getImage() and setImage() rat her t han on t he m echanics of which m et hods t o forward. I n fact , no forwarding exist s: ImageIconLoader is a proxy in spirit but not in st ruct ure.
Remote Proxies The idea of a proxy is t hat one obj ect int elligent ly forwards calls t o anot her obj ect . This can becom e com plex and fragile, as in t he case of ImageIcon, where it is difficult t o know which calls t o forward. Even if you forward every call, your code m ay break if t he class of t he proxied obj ect changes. When you encount er t he PROXY pat t ern in a design, you should quest ion whet her t he benefit j ust ifies t he accom panying fragilit y of code. There are cases in which applying PROXY is well j ust ified, as when an obj ect and it s proxy are act ive on different com put ers. I f t he obj ect w hose m et hod you w ant t o call is running on anot her com put er, you m ust find a way t o com m unicat e wit h t he rem ot e obj ect ot her t han calling it direct ly. You could open a socket on t he rem ot e m achine and devise a prot ocol t o pass m essages t o t he rem ot e obj ect . I deally, such a schem e would let you pass m essages in alm ost t he sam e way as if t he obj ect were local. You should be able t o call m et hods on a proxy obj ect t hat forwards t he calls t o t he real obj ect on t he rem ot e m achine. I n fact , such schem es have been realized, not ably in t he com m on obj ect request broker archit ect ure ( CORBA) and in Java's r e m ot e m e t h od invoca t ion ( RM I ) . RMI m akes it about as easy as possible for a client t o obt ain a proxy obj ect t hat forwards calls t o a desired obj ect t hat is act ive on anot her com put er. I t is well wort h learning about RMI , as RMI is part of t he underpinning of t he Ent e r pr ise Ja va Be a ns ( EJB) specificat ion, an im port ant em erging indust ry st andard. Regardless of how indust ry st andards evolve, t he role of PROXY in dist ribut ed com put ing will cont inue int o t he foreseeable fut ure, and RMI provides a good exam ple of t his pat t ern in act ion. To experim ent wit h RMI , you will need a good reference on t his t opic, such as Java™ Ent erprise in a Nut shell ( Flanagan et al. 1999) . The following exam ple is not a t ut orial on RMI but m erely point s out t he presence and value of PROXY wit hin RMI applicat ions. Suppose t hat you decide t o explore t he workings of RMI , m aking an obj ect 's m et hods available t o a Java program running on anot her com put er. The init ial developm ent st ep is t o creat e an int erface for t he class t hat you want t o provide rem ot e access t o. As an experim ent al proj ect , suppose t hat you creat e a Rocket int erface t hat is independent of exist ing code at Oozinoz:
package com. oozi noz. r emot e; i mpor t j ava. r mi . *; publ i c i nt er f ace Rocket ext ends Remot e { voi d boost ( doubl e f act or ) t hr ows Remot eExcept i on; doubl e get Apogee( ) t hr ows Remot eExcept i on; doubl e get Pr i ce( ) t hr ows Remot eExcept i on; }
The Rocket int erface ext ends Remote, and t he m et hods in t he int erface all declare t hat t hey t hrow RemoteException. The reasons for t hese aspect s of t he int erface lie out side t he scope of t his book, but any book t hat t eaches RMI should cover t hem . Your RMI source should also explain t hat , t o act as a server, t he im plem ent at ion of your rem ot e int erface can subclass UnicastRemoteObject, as Figure 11.5 shows. Figure 11.5. To use RMI, you can define the interface you want for messages that pass between computers and create a subclass of UnicastRemoteObject that implements it.
Your plan is for RocketImpl obj ect s t o be act ive on a server and t o be available t hrough a proxy t hat is act ive on a client . The code for RocketImpl is sim ple:
package com. oozi noz. r emot e; i mpor t j ava. r mi . *; i mpor t j ava. r mi . ser ver . Uni cast Remot eObj ect ; publ i c cl ass Rocket I mpl ext ends Uni cast Remot eObj ect i mpl ement s Rocket { pr ot ect ed doubl e pr i ce; pr ot ect ed doubl e apogee; publ i c Rocket I mpl ( doubl e pr i ce, doubl e apogee) t hr ows Remot eExcept i on {
t hi s. pr i ce = pr i ce; t hi s. apogee = apogee; } publ i c voi d boost ( doubl e f act or ) { apogee *= f act or ; } publ i c doubl e get Apogee( ) { r et ur n apogee; } publ i c doubl e get Pr i ce( ) { r et ur n pr i ce; } } An inst ance of RocketImpl can be act ive on one m achine and can be accessed by a Java program running on anot her m achine. For t his t o work, a client needs a proxy for t he RocketImpl obj ect . This proxy needs t o im plem ent t he Rocket int erface and m ust have t he addit ional feat ures required t o com m unicat e wit h a rem ot e obj ect . A great benefit of RMI is t hat it aut om at es t he const ruct ion of t his proxy. To generat e t he proxy, place t he RocketImpl.java file and t he Rocket.java int erface file below t he direct ory in which you will run t he RMI regist ry:
c: \ r mi > di r com\ oozi noz\ r emot e Rocket . j ava Rocket I mpl . j ava To creat e t he RocketImpl st ub t hat facilit at es rem ot e com m unicat ion, run t he RMI com piler t hat com es wit h t he JDK:
c: \ r mi > r mi c - v1. 2 com. oozi noz. r emot e. Rocket I mpl Not e t hat t he rmic execut able t akes a class nam e, not t he file nam e, as an argum ent . Earlier versions of t he JDK const ruct ed separat e files for use on t he client and t he server m achines. As of version 1.2, t he RMI com piler creat es a single st ub file t hat bot h t he client and t he server m achines need. The rmic com m and forces t he com pilat ion of t he classes it needs and creat es a RocketImpl_Stub class:
c: \ r mi > di r com\ oozi noz\ r emot e Rocket I mpl . j ava Rocket . j ava Rocket I mpl . cl ass Rocket . cl ass Rocket I mpl _St ub. cl ass To m ake an obj ect act ive, you m ust regist er it wit h an RMI regist ry running on t he server. The rmiregistry execut able com es as part of t he JDK. When you run t he regist ry, specify t he port t hat t he regist ry will list en t o:
c: \ r mi > r mi r egi st r y 5000
Wit h t he regist ry running on t he server m achine, you can creat e and regist er a RocketImpl obj ect :
package com. oozi noz. r emot e; i mpor t j ava. r mi . *; publ i c cl ass Regi st er Rocket { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { t ry { / / chal l enge! Nami ng. r ebi nd( "r mi : / / l ocal host : 5000/ Bi ggi e", bi ggi e) ; Syst em. out . pr i nt l n( "Regi st er ed bi ggi e") ; } cat ch ( Except i on e) { e. pr i nt St ackTr ace( ) ; } } } I f you copy t his code t o t he sam e direct ory t hat t he ot her files reside in, you can com pile and run it . The program displays a confirm at ion t hat t he rocket is regist ered:
c: \ r mi > j avac com\ oozi noz\ r emot e\ Regi st er Rocket . j ava c: \ r mi > j ava com. oozi noz. r emot e. Regi st er Rocket Regi st er ed bi ggi e You need t o replace t he //challenge! line in t he RegisterRocket class wit h code t hat creat es a biggie obj ect t hat m odels a rocket . The rem aining code in t he main() m et hod regist ers t his obj ect . A descript ion of t he m echanics of t he Naming class is out side t he scope of t his discussion. However, you should have enough inform at ion t o creat e t he biggie obj ect t hat t his code regist ers.
CHALLENGE 11.3
Replace t he //challenge! line wit h a declarat ion and inst ant iat ion of t he biggie obj ect . Define biggie t o m odel a rocket wit h a price of $29.95 and an apogee of 820 m et ers.
Running t he RegisterRocket program m akes a RocketImpl obj ect , specifically biggie, available on a server. A client t hat runs on anot her m achine can access biggie if t he client has access t o t he Rocket int erface and t he RocketImpl_Stub class. I f you are working on a single m achine, you can st ill t est out RMI , accessing t he server on localhost rat her t han anot her host :
package com. oozi noz. r emot e; i mpor t j ava. r mi . *; publ i c cl ass ShowRocket Cl i ent { publ i c st at i c voi d mai n( St r i ng[ ] ar gs)
{ t ry { Obj ect o = Nami ng. l ookup( "r mi : / / l ocal host : 5000/ Bi ggi e") ; Rocket bi ggi e = ( Rocket ) o; Syst em. out . pr i nt l n( bi ggi e. get Apogee( ) ) ; } cat ch ( Except i on e) { Syst em. out . pr i nt l n( "Except i on whi l e l ooki ng up a r ocket : ") ; e. pr i nt St ackTr ace( ) ; } } } When t his program runs, it looks up an obj ect wit h t he regist ered nam e of " Biggie." The class t hat is serving t his nam e is RocketImpl, and t he obj ect o t hat lookup() ret urns will be an inst ance of RocketImpl_Stub class. The RocketImpl_Stub class im plem ent s t he Rocket int erface, so it is legal t o cast t he obj ect o as an inst ance of t he Rocket int erface. The Rocket_Impl class subclasses a RemoteStub class t hat let s t he obj ect com m unicat e wit h a server. To see t he ShowRocketClient program in act ion, copy t he Rocket and RocketImpl_Stub classes t o a client area:
d: \ cl i ent > di r com\ oozi noz\ r emot e Rocket . cl ass Rocket I mpl _St ub. cl ass ShowRocket Cl i ent . j ava Running t he ShowRocketClient program print s out t he apogee of a " Biggie" rocket :
d: \ cl i ent > j avac com\ oozi noz\ r emot e\ ShowRocket Cl i ent . j ava d: \ cl i ent > j ava com. oozi noz. r emot e. ShowRocket Cl i ent 820. 0 Through a proxy, t he getApogee() call is forwarded t o an im plem ent at ion of t he Rocket int erface t hat is act ive on a server.
CHALLENGE 11.4
Figure 11.6 shows t he getApogee() call being forwarded. The right m ost obj ect appears in a bold out line, indicat ing t hat it is act ive out side t he ShowRocketClient program . Fill in t he class nam es of t he unlabeled obj ect s in t his figure. Figure 11.6. When completed, this diagram will show the flow of messages in an RMI-based distributed application.
The benefit of RMI is t hat it let s client program s int eract wit h a local obj ect t hat is a proxy for a rem ot e obj ect . You define t he int erface for t he obj ect t hat you want client s and servers t o share. RMI supplies t he com m unicat ion m echanics and isolat es bot h server and client from t he knowledge t hat t wo im plem ent at ions of Rocket are collaborat ing t o provide nearly seam less int erprocess com m unicat ion.
Summary I m plem ent at ions of t he PROXY pat t ern est ablish a placeholder obj ect t hat m anages access t o a t arget obj ect . A proxy obj ect can isolat e client s from shift s in st at e of a desired obj ect , as when loading an im age endures a discernible durat ion. The problem wit h PROXY is t hat by it s nat ure, it relies on a t ight coupling bet ween t he placeholder and t he proxied obj ect . Nowhere is t his coupling m ore j ust ified t han in rem ot e com put ing. Here, t he PROXY pat t ern represent s t he ideal form of int ercom put er com m unicat ion. Rat her t han relying on anot her prot ocol, dist ribut ed com put ing schem es, such as RMI , est ablish com m unicat ion wit h rem ot e obj ect s as norm al- looking m et hod calls. This let s a client com m unicat e wit h a rem ot e obj ect t hrough a proxy as if t he rem ot e obj ect were local. The role of PROXY in dist ribut ed com put ing appears t o be a perm anent advance in obj ect - orient ed com put ing.
Chapter 12. Chain of Responsibility Obj ect - orient ed developers st rive t o keep obj ect s loosely coupled, keeping t he responsibilit y bet ween obj ect s specific and m inim al. This let s you int roduce change m ore easily and wit h less risk of int roducing defect s. To a degree, decoupling occurs nat urally in Java. Client s see only an obj ect 's visible int erface and rem ain isolat ed from t he det ails of t he obj ect 's im plem ent at ion. This arrangem ent , however, leaves in place t he fundam ent al coupling t hat t he client knows w hich obj ect has t he m et hod t he client needs t o call. An opport unit y t o loosen t he rest rict ion t hat a client m ust know which obj ect t o use occurs when you can arrange t he obj ect s in chains. I n such a case, you can provide t hese obj ect s wit h an operat ion t hat each obj ect eit her perform s or passes along t he chain. The int ent of t he CHAI N OF RESPONSI BI LI TY pat t ern is t o avoid coupling t he sender of a request t o it s receiver by giving m ore t han one obj ect a chance t o handle t he request . To
apply t his pat t ern, chain t he receiving obj ect s and pass t he request along t he chain unt il an obj ect handles it .
Varieties of Lookup I n CHAI N OF RESPONSI BI LI TY, an obj ect m odel t akes on t he j ob of finding which obj ect can sat isfy a client 's request . This approach goes beyond t he t wo lookup m echanism s t hat are built int o Java: except ion handling and m et hod lookup. When an except ion is t hrown, t he Java int erpret er looks back up t he call st ack t o find a m et hod called from a block enclosed in a try/catch st at em ent . I f t his try/catch st at em ent does not cat ch an except ion of t he t ype t hrown, t he Java int erpret er keeps looking. This can propagat e up t o t he main() m et hod. I f t he except ion is not handled t here, t he Java int erpret er print s out an error m essage and st ack t race and t hen exit s. The m ore com m on case of lookup in Java is m e t h od look u p—t he algorit hm for deciding which definit ion of a m et hod t o use when a client calls an obj ect 's m et hod. For exam ple, if you call an obj ect 's toString() m et hod, Java will use t he m et hod's im plem ent at ion t hat appears lowest in t he obj ect 's class hierarchy. When t he com piler cannot m ake t his det erm inat ion in advance, t he Java int erpret er looks up t he right m et hod t o invoke, at runt im e.
CHALLENGE 12.1
How does t he CHAI N OF RESPONSI BI LI TY pat t ern differ from ordinary m et hod lookup?
Refactoring to Chain of Responsibility The Oozinoz applicat ion suit e includes a visualizat ion t hat shows m achines on t he fact ory floor. The visualizat ion includes t he abilit y t o select m ult iple it em s and t o pop up a m enu of various request s. One it em on t he pop- up m enu displays a list of t he engineers who are responsible for t he select ed equipm ent . The sim ulat ed it em s include m achines, m achine com posit es, t ools, and t ool cart s. Tools are always assigned t o t ool cart s, and t ool cart s have a responsible engineer. I n det erm ining who is responsible for t he m achine, m achines m ay have a responsible engineer or m ay defer t o t he line t hey are part of or t o t he fact ory it self. Figure 12.1 shows t he classes involved in finding t he engineers who are responsible for select ed equipm ent . Figure 12.1. Items in a simulation include machines, machine composites, tools, and tool carts.
The m enu code t hat finds t he engineer responsible for a given m achine uses a series of if st at em ent s and instanceof operat ors. These t rait s are signs t hat refact oring can im prove t he code.
package com. oozi noz. appl i cat i ons; i mpor t com. oozi noz. machi ne. *; publ i c cl ass Ambi t i ousMenu { publ i c Engi neer get Responsi bl e( Si mul at edI t em i t em) { i f ( i t em i nst anceof Tool ) { Tool t = ( Tool ) i t em; r et ur n t . get Tool Car t ( ) . get Responsi bl e( ) ; } i f ( i t em i nst anceof Machi neComponent ) { Machi neComponent c = ( Machi neComponent ) i t em;
i f ( c. get Responsi bl e( ) == nul l ) { i f ( c. get Par ent ( ) ! = nul l ) { r et ur n c. get Par ent ( ) . get Responsi bl e( ) ; } } } r et ur n nul l ; } } You can clean up t his code by applying t he CHAI N OF RESPONSI BI LI TY pat t ern.
CHALLENGE 12.2
Redraw t he diagram in Figure 12.1, m oving getResponsible() t o SimulatedItem and adding t his behavior t o Tool.
The client code is a lot sim pler if it can ask any select able it em for it s responsible engineer:
publ i c Engi neer get Responsi bl e( Si mul at edI t em i t em) { r et ur n i t em. get Responsi bl e( ) ; } The im plem ent at ion of getResponsible() for each it em is sim pler, t oo.
CHALLENGE 12.3
Writ e getResponsible() for:
A. MachineComponent B. Tool C. ToolCart
Anchoring a Chain When you writ e t he getResponsible() m et hod for MachineComponent, you have t o consider t hat a MachineComponent obj ect 's parent m ight be null. Alt ernat ively, you can t ight en up your obj ect m odel, insist ing t hat MachineComponent obj ect s have a non- null parent . To achieve t his, add a parent argum ent t o t he const ruct or for MachineComponent. ( You m ay want t o t hrow an except ion if t he supplied obj ect is null.) Also consider t hat an obj ect will be t he root —a dist inguished obj ect t hat has no parent . A reasonable approach is t o creat e a MachineRoot class as a subclass of MachineComposite ( not MachineComponent) . Then you can guarant ee t hat a MachineComponent obj ect always has a responsible engineer if • •
The constructor(s) for MachineRoot require an Engineer object The constructor(s) for MachineComponent require a non-null parent object that is itself a MachineComponent
CHALLENGE 12.4
Fill in t he const ruct ors in Figure 12.2 t o support a design t hat ensures t hat every MachineComponent obj ect has a responsible engineer. Figure 12.2. How can constructors ensure that every MachineComponent object has a responsible engineer?
By anchoring a chain of responsibilit y, you st rengt hen t he obj ect m odel and sim plify t he code. Now you can im plem ent t he getResponsible() m et hod of MachineComponent as:
publ i c Engi neer get Responsi bl e( ) { i f ( r esponsi bl e ! = nul l ) { r et ur n r esponsi bl e; } r et ur n par ent . get Responsi bl e( ) ; }
Chain of Responsibility without Composite
CHAI N OF RESPONSI BI LI TY requires a st rat egy for ordering t he search for an obj ect t hat can handle a request . Usually, t he order t o follow will depend on an underlying aspect in t he m odeled dom ain. This frequent ly occurs when t here is com posit ion, as in t he Oozinoz m achine hierarchy. However, t his pat t ern can apply out side obj ect m odels t hat are com posit es.
CHALLENGE 12.5
Cit e an exam ple when t he CHAI N OF RESPONSI BI LI TY pat t ern m ight occur if t he chained obj ect s do not form a com posit e.
Summary When you apply t he CHAI N OF RESPONSI BI LI TY pat t ern, you relieve a client from having t o know which obj ect in a collect ion support s a given behavior. By set t ing up t he search for responsibilit y t o occur across obj ect s, you decouple t he client from any specific obj ect in t he chain. The CHAI N OF RESPONSI BI LI TY pat t ern occurs occasionally when an arbit rary chain of obj ect s can apply a series of different st rat egies t o t ackling a problem , such as parsing a user's input . More frequent ly t his pat t ern occurs in aggregat ions, in which a cont ainm ent hierarchy provides a nat ural ordering for a chain of obj ect s.CHAI N OF RESPONSI BI LI TY leads t o sim pler code in bot h t he hierarchy and t he client , which is t he m ain advant age of t his pat t ern.
Chapter 13. Flyweight The flyweight pat t ern addresses sharing, relying on an obj ect 's abilit y t o be responsible t o m ore t han a single client . An ordinary obj ect doesn't have t o worry m uch about shared responsibilit y. Oft en, only one client will hold a reference t o an obj ect at any one t im e. When t he obj ect 's st at e changes, it 's because t he client changed it , and t he obj ect does not have any responsibilit y t o inform any ot her client s. Som et im es, t hough, you will want t o arrange for m ult iple client s t o share access t o an obj ect . One incent ive for sharing an obj ect am ong m ult iple client s occurs when you m ust m anage t housands or t ens of t housands of sm all obj ect s, such as t he charact ers in an online version of a book. I n such a case, you m ay have a perform ance incent ive t o share t hese fine- grained obj ect s am ong m any client s. A book needs only one A obj ect , alt hough it needs a way t o m odel where different As appear. I n any applicat ion having a large num ber of sm all obj ect s, you m ay need t o provide a way for client s t o safely share t he com m on elem ent s of t hese obj ect s. The int ent of t he FLYWEI GHT pat t ern is t o use sharing t o support large num bers of fine- grained obj ect s efficient ly.
Recognizing Flyweight To provide for t he sharing of flyweight obj ect s, you need a class wit h a m et hod ot her t han a const ruct or t hat ret urns t he obj ect t o be shared. This let s you cont rol flyweight creat ion, ensuring t hat only one inst ance of any part icular flyweight exist s. There is an exam ple of a flyweight fact ory in a Swing class t hat is a fact ory for Swing com ponent s. This class com m ent says, " wherever possible, t his fact ory will hand out references t o shared ... inst ances." We used t his class in Chapt er 4, Facade, and in Chapt er 9, Observer.
CHALLENGE 13.1
Nam e a Swing class t hat provides an exam ple of FLYWEI GHT.
Immutability When a client changes t he st at e of an obj ect , t he st at e changes for every client t hat has access t o t he obj ect . This is no problem when t here is only a single client , which is t he m ost ordinary case. When m ult iple client s will share access t o an obj ect , t he easiest and m ost com m on way t o keep client s from affect ing one anot her is t o rest rict client s from int roducing any st at e changes in t he shared obj ect . You can achieve t his by m aking an obj ect im m u t a ble so t hat once creat ed, t he obj ect cannot change. The m ost com m on im m ut able obj ect s in Java are inst ances of t he String class. Once you creat e a st ring, neit her you nor any client wit h access t o t he st ring can change it s charact ers.
CHALLENGE 13.2
Eit her j ust ify why t he creat ors of Java m ade String obj ect s im m ut able or argue t hat t his was an unw ise rest rict ion.
When you have large num bers of sim ilar obj ect s, you m ay want t o arrange for shared access t o t hese obj ect s, but t hey m ay not be im m ut able. I n t his case, a prelim inary st ep in applying t he FLYWEI GHT pat t ern is t o ext ract t he im m ut able part of an obj ect so t hat t his part can be shared.
Extracting the Immutable Part of a Flyweight Around Oozinoz, chem icals are as prevalent as charact ers in a docum ent . The purchasing, engineering, m anufact uring, and safet y depart m ent s are all concerned wit h direct ing t he flow of t housands of chem icals t hrough t he fact ory. Bat ches of chem icals are oft en m odeled wit h inst ances of t he Substance_1 class, as shown in Figure 13.1. Figure 13.1. A Substance_1 object models a physical batch of chemical material.
The Substance_1 class has get- m et hods for it s at t ribut es and also has a getMoles() m et hod t hat ret urns t he num ber of m oles—a count of m olecules—in t he subst ance. For exam ple, 32 gram s of sulfur cont ains 1 m ole of sulfur at om s, as t he at om ic weight of sulfur is 32. The getMoles() m et hod of t he Substance_1 class ret urns t he num ber of m oles t hat a part icular Substance_1 inst ance represent s. The Substance_1 class uses t he at om ic weight of a chem ical when it calculat es t he num ber of m oles of a m odeled quant it y of subst ance. For exam ple, 10 gram s of sulfur cont ains 10/ 32 m oles:
publ i c doubl e get Mol es( ) { r et ur n gr ams / at omi cWei ght ; } The m ola lit y of a subst ance com bines inform at ion about t he m ass of a part icular bat ch and t he im m ut able at om ic weight of t he subst ance. To support t he calculat ion of m olalit y, developers at Oozinoz have placed m ass and at om ic weight t oget her in t he Substance_1 class. The m olalit y of subst ances, com bined wit h an underst anding of chem ical react ions, det erm ines t he am ount of each subst ance t hat m ust appear in a chem ical m ixt ure. ( For exam ple, t wo m oles of hydrogen and one m ole of oxygen can m ake a m ole of H2 O—wat er.) At Oozinoz, no m ixt ure is m ore com m on t han t he black powder t hat is part of m ost fireworks. Figure 13.2 shows an obj ect diagram of a bat ch of black powder. Figure 13.2. A batch of black powder contains saltpeter, sulfur, and carbon.
Given t he proliferat ion of chem icals at Oozinoz, you m ight decide t o m odel chem icals wit h shared, flyweight obj ect s. As a first st ep, suppose t hat you decide t o refact or t he Substance_1 class, ext ract ing it s im m ut able part int o a Chemical_1 class.
CHALLENGE 13.3
Com plet e t he class diagram in Figure 13.3 t o show a refact ored Substance class and a new, im m ut able Chemical_1 class. I nclude a getMoles() m et hod in t he Chemical_1 class, so t hat classes ot her t han Substance can check a chem ical bat ch's m olalit y. Figure 13.3. Complete this diagram to extract the immutable aspects of Substance into the Chemical_1 class.
Sharing Flyweights Ext ract ing t he im m ut able part of an obj ect is half t he bat t le in applying t he FLYWEI GHT pat t ern. The rem aining work includes creat ing a flyweight fact ory t hat inst ant iat es flyweight s and t hat arranges for client s t o share t hem . You also have t o ensure t hat
client s will use your fact ory inst ead of const ruct ing inst ances of t he flyweight class t hem selves. To m ake chem icals flyweight s, you need a fact ory, perhaps a ChemicalFactory_1 class, wit h a st at ic m et hod t hat ret urns a chem ical given it s nam e. You m ight st ore chem icals in a hash m ap, creat ing known chem icals as part of t he fact ory's init ializat ion. Figure 13.4 shows a design for a ChemicalFactory_1. The code for ChemicalFactory_1 can use a st at ic init ializer t o st ore Chemical_1 obj ect s in a hash m ap: Figure 13.4. The ChemicalFactory_1 class is a flyweight factory that returns Chemical objects.
package com. oozi noz. chemi cal ; i mpor t j ava. ut i l . *; publ i c cl ass Chemi cal Fact or y_1 { pr i vat e st at i c Map chemi cal s = new HashMap( ) ; st at i c { chemi cal s. put ( "car bon", new Chemi cal _1( "Car bon", "C", 12) ) ; chemi cal s. put ( "sul f ur ", new Chemi cal _1( "Sul f ur ", "S", 32) ) ; chemi cal s. put ( "sal t pet er ", new Chemi cal _1( "Sal t pet er ", "KN03", 101) ) ; //. . . } publ i c st at i c Chemi cal get Chemi cal ( St r i ng name) { r et ur n ( Chemi cal ) chemi cal s. get ( name. t oLower Case( ) ) ;
} } Having creat ed a fact ory for chem icals, you now have t o t ake st eps t o ensure t hat ot her developers use t his fact ory inst ead of inst ant iat ing t he Chemical_1 class t hem selves. A sim ple approach is t o rely on t he visibilit y of t he Chemical_1 class.
CHALLENGE 13.4
How can you use t he visibilit y of t he Chemical_1 class t o discourage ot her developers from inst ant iat ing Chemical_1 obj ect s?
Visibilit y m odifiers do not supply t he com plet e cont rol over inst ant iat ion t hat you m ight want . You m ight like t o ensure t hat ChemicalFactory is absolut ely t he only class t hat can creat e new Chemical inst ances. To achieve t his level of cont rol, you can apply an inner class, defining t he Chemical class wit hin ChemicalFactory. Java™ in a Nut shell ( Flanagan 1999b) has a good descript ion of t he various t ypes of inner classes t hat Java provides. For ChemicalFactory, you can declare Chemical as a st at ic m em ber class. This indicat es t hat Chemical need not reference any inst ance variables in ChemicalFactory. You m ight want t o nam e t he inner class ChemicalImpl and use t he nam e Chemical for an int erface. This let s client s refer t o Chemical obj ect s rat her t han ChemicalFactory.Chemical obj ect s. Client s will never reference t he inner class direct ly, so you can m ake it privat e, ensuring t hat only ChemicalFactory has access t o it :
package com. oozi noz. chemi cal ; i mpor t j ava. ut i l . *; publ i c cl ass Chemi cal Fact or y { pr i vat e st at i c Map chemi cal s = new HashMap( ) ; st at i c { chemi cal s. put ( "car bon", new Chemi cal I mpl ( "Car bon", "C", 12) ) ; / / . . . t hese put s ar e al l her e, but el i ded f or space } pr i vat e st at i c cl ass Chemi cal I mpl i mpl ement s Chemi cal { ?? ( decl ar e i nst ance var i abl es) ?? pr i vat e Chemi cal I mpl ( {
??
?? } publ i c doubl e get Mol es( doubl e gr ams) { r et ur n ?? }
)
publ i c St r i ng get Name( ) { r et ur n name; } publ i c St r i ng get Symbol ( ) { r et ur n symbol ; } publ i c doubl e get At omi cWei ght ( ) { r et ur n at omi cWei ght ; } } publ i c st at i c Chemi cal get Chemi cal ( St r i ng name) { r et ur n ?? } }
CHALLENGE 13.5
Com plet e t he Java code for a ChemicalFactory version t hat defines ChemicalImpl as a privat e, st at ic m em ber class. The inner class im plem ent s a Chemical int erface, and t he getChemical() m et hod of ChemicalFactory ret urns an obj ect of t ype Chemical.
Summary The FLYWEI GHT pat t ern let s you share access t o obj ect s, such as charact ers, chem icals, and borders, t hat m ay appear in large quant it ies. The flyweight obj ect s m ust be im m ut able, a feat ure you can est ablish by ext ract ing t he im m ut able part of t he class t hat you want t o share. To ensure t hat your flyweight obj ect s are shared, you have t o provide a fact ory in w hich client s can find flyweight s, and you have t o enforce t he use of t his fact ory. Visibilit y m odifiers give you som e cont rol over how ot her developers access your code. I nner classes t ake t his furt her, let t ing you guarant ee t hat a class is accessible by only it s cont aining class. By ensuring t hat client s use your flyweight fact ory properly, you can provide safe, shared access t o what would ot herwise be a m ult it ude of fine- grained obj ect s.
Part III: Construction Patterns Chapter 14. Introducing Construction Chapter 15. Builder Chapter 16. Factory Method
Chapter 17. Abstract Factory Chapter 18. Prototype Chapter 19. Memento
Chapter 14. Introducing Construction When you creat e a Java class, you norm ally provide for t he creat ion of obj ect s of your class by supplying class const ruct ors. I n m any regards, const ruct ors are like any ot her m et hods, but Java has num erous rules t hat specifically govern t he use of const ruct ors. I n part icular, const ruct ors have special abilit ies t o collaborat e wit h one anot her.
Ordinary Construction Java const ruct ors are special m et hods. I n m any respect s, including visibilit y m odifiers and except ion handling, const ruct ors are like ordinary m et hods. On t he ot her hand, a significant num ber of synt act ic and sem ant ic rules govern t he use and behavior of const ruct ors. These ext ra rules em erge because classes are significant ly different from ot her obj ect s in Java. I n Sm allt alk, by com parison, classes are obj ect s t hat are responsible for providing const ruct ors. There is no special synt ax for const ruct ors, a sim plificat ion t hat helps keep Sm allt alk synt ax sm all.
CHALLENGE 14.1
List t hree or four rules t hat govern t he use and behavior of const ruct ors in Java.
Sm allt alk's sim ple synt ax for const ruct ors is not proof per se of Sm allt alk's superiorit y. I n fact , Sm allt alk's organizat ion of t he class of a class of a class is m ind- num bingly difficult t o com prehend for m ost developers. The point of com paring Java t o ot her languages is not t o det erm ine superiorit y but rat her for us t o deepen our underst anding of t he design choices built int o Java and how t hey affect us as developers. One aspect of Java const ruct ors t hat is im port ant t o underst and is t he collaborat ion t hat you can orchest rat e am ong const ruct ors.
Superclass Collaboration Event ually, a const ruct or always collaborat es wit h one of it s superclass's const ruct ors. I f a class has no declared const ruct or, Java supplies a default one equivalent t o a const ruct or wit h no argum ent s and no st at em ent s. I f t he first st at em ent in a const ruct or is anyt hing ot her t han a specific invocat ion of anot her const ruct or, Java insert s a call t o super(), t he superclass's const ruct or wit h no argum ent s. This causes a com pilat ion error if t he superclass does not provide a const ruct or wit h no argum ent s. I n Figure 14.1, for exam ple t he classes capt ure t he idea t hat a fount ain is one t ype of firework. ( A fount a in is a ground- based firework t hat em it s a spray of sparks.) Suppose t hat at an early st age of developm ent , we have no const ruct ors defined. I n t his case, Java supplies a default const ruct or—one t hat accept s no argum ent s—for bot h classes. I t is legal t o inst ant iat e t hese obj ect s wit h such lines as:
Fount ai n f = new Fount ai n( ) ; Figure 14.1. In certain conditions, Java will provide default constructors and default constructor collaboration.
A problem arises if we add a const ruct or t o t he superclass. Suppose t hat we add a name at t ribut e t o Firework and a const ruct or t hat accept s it :
publ i c Fi r ewor k( St r i ng name) { t hi s. name = name; } Aft er t his change t o Firework, t he Fountain class will suddenly fail t o com pile. The Fountain class defines no const ruct or, so Java will supply a default const ruct or. This const ruct or m akes a default call t o super(), t he superclass's default const ruct or. However, t his const ruct or no longer exist s. Java supplies a default const ruct or only if t he class has no ot her const ruct or. By creat ing a const ruct or for Firework, we elim inat e t his class's default const ruct or and t hereby break t he Fountain class.
CHALLENGE 14.2
Writ e a const ruct or for t he Fountain class t hat allows it t o successfully com pile.
Java will not always insert a call t o super() in a const ruct or. A const ruct or can avoid t his default behavior by explicit ly invoking a superclass const ruct or or by invoking anot her const ruct or of t he current class wit h a call t o this(). When a class has several const ruct ors, t hey usually apply calls t o this() t o collaborat e wit h one anot her.
Collaboration within a Class The const ruct ors of a well- designed class usually int eract . Consider what happens when we need t o add a classificat ion at t ribut e t o t he Firework class so t hat each firework is classified as a consum er firework or as a display- t ype firework. Figure 14.2 shows t he Firework class picking up firework classificat ion const ant s from t he ClassificationConstants int erface and adding a Classification at t ribut e. Figure 14.2. The Firework class "inherits" classification constants by implementing the ClassificationConstants interface.
CHALLENGE 14.3
Suppose t hat you want t o use const ruct ors t o est ablish t he default classificat ion of a firework as DISPLAY. Writ e const ruct ors for Firework t hat ensure t his, and m ake t hem collaborat e so t hat t he one- param et er const ruct or uses t he t w oparam et er const ruct or.
Changing t he const ruct ors for t he Firework class forces us t o consider changing t he const ruct ors for it s subclass, Fountain.
CHALLENGE 14.4
Suppose t hat you want t he Fountain class t o abide by t he decision t hat t he default classificat ion of a firework is DISPLAY. I t is bet t er not have t his decision appear t wice in t he code. Writ e t he const ruct ors for Fountain so t hat t hey collaborat e effect ively wit h t he superclass const ruct ors, including observance of t he superclass's ideas about default s.
Summary Ordinarily, you will furnish classes t hat you develop wit h const ruct ors t o provide a m eans for inst ant iat ing t he class. These const ruct ors m ay form a collaborat ing suit e, and every call t o a const ruct or will ult im at ely also invoke a superclass const ruct or.
Beyond Ordinary Construction Java's const ruct or feat ures provide m any alt ernat ives when you design a new class. However, const ruct ors are effect ive only if t he user of your class knows which class t o inst ant iat e and knows t he required fields for inst ant iat ing an obj ect . For exam ple, t he choice of which user int erface com ponent t o com pose m ay depend on whet her t he program is running on a handheld device or on a larger display. I t can also happen t hat a developer knows which class t o inst ant iat e but does not have all t he necessary init ial values or has t hem in t he wrong form at . For exam ple, t he developer m ay need t o creat e an obj ect from a dorm ant or a t ext ual version of an obj ect . I n such circum st ances, you need t o go beyond t he use of ordinary Java const ruct ors and apply a design pat t ern. The following principles describe t he int ent of pat t erns t hat facilit at e const ruct ion.
If you intend to •
Gather the information for an object gradually before requesting its construction
Apply the pattern Builder ( Chapt er 15)
•
Defer the decision of which class to instantiate
Fact ory Met hod ( Chapt er 16)
•
Construct a family of objects that share a trait
Abst ract Fact ory ( Chapt er 17)
•
Specify an object to create by giving an example
Prot ot ype ( Chapt er 18)
•
Reconstruct an object from a dormant version that contains just the object's internal state
Mem ent o ( Chapt er 19)
The int ent of each design pat t ern is t o solve a problem in a cont ext . Const ruct ionorient ed pat t erns are designs t hat let a client const ruct a new obj ect t hrough a m eans ot her t han calling a class const ruct or. For exam ple, when you find t he init ial values for an obj ect gradually, you m ay want t o follow t he BUI LDER pat t ern.
Chapter 15. Builder The builder pat t ern m oves t he const ruct ion logic for an obj ect out side t he class t o inst ant iat e. Making t his m ove m ight be useful for several reasons. You m ight sim ply want t o reduce t he size of a class t hat has m any m et hods. You m ay also want t o allow st epby- st ep const ruct ion of a t arget obj ect . This occurs when you acquire t he param et ers for a const ruct or gradually, as happens wit h parsers and m ay happen wit h a user int erface.
Building from a Parser I n addit ion t o m anufact uring fireworks, Oozinoz put s on fireworks displays. Travel agencies e- m ail t heir reservat ion request s in t he form
Dat e, November 5, Headcount , 250, Ci t y, Spr i ngf i el d, Dol l ar sPer Head, 9. 95, HasSi t e, No As you m ight guess, t his prot ocol originat ed in t he days before XML ( Ex t e nsible M a r k up La ngua ge ) , but it has t hus far proved sufficient . The request t ells when a pot ent ial
cust om er want s t o see a display and in what cit y. The request also specifies t he m inim um headcount t hat t he cust om er will guarant ee and t he am ount of m oney per guest t hat t he cust om er will pay. The cust om er in t his exam ple want s t o put on a show for 250 guest s and is w illing t o pay $9.95 per guest , or a t ot al of $2,487.50. The t ravel agent has also indicat ed t hat t he cust om er does not have a sit e in m ind for t he display. The t ask at hand is t o parse t he t ext ual request and creat e a Reservation obj ect t hat represent s it . We m ight approach t his t ask by creat ing an em pt y Reservation obj ect and set t ing it s param et ers as our parser encount ers t hem . This causes t he problem t hat a given Reservation obj ect m ay or m ay not represent a valid request . For exam ple, we m ight finish reading t he t ext of a request and t hen realize t hat it lacks a dat e. To ensure t hat Reservation obj ect s are always valid request s, we can use a ReservationBuilder class. The ReservationBuilder obj ect can st ore a reservat ion request 's at t ribut es as a parser finds t hem and t hen build a Reservation obj ect , verifying it s validit y. Figure 15.1 shows t he classes we need for t his design. Figure 15.1. A builder class offloads construction logic from a domain class and can accept initialization parameters gradually, as a parser discovers them.
The classes t hat Figure 15.1 shows appear in t he com.oozinoz.reservation package. The ReservationBuilder class is abst ract , as is it s build() m et hod. We will creat e concret e subclasses of ReservationBuilder short ly. First , t hough, not e t hat t he const ruct or for Reservation is prot ect ed.
CHALLENGE 15.1
The Reservation const ruct or has prot ect ed visibilit y. Explain eit her why t his is ideal or why it
15.1
visibilit y. Explain eit her why t his is ideal or why it would be bet t er t o give t his const ruct or privat e, public, or package prot ect ion.
Building under Constraints You want t o ensure t hat invalid Reservation obj ect s are never inst ant iat ed. Specifically, suppose t hat every reservat ion m ust have a non- null dat e and cit y. Furt her, suppose t hat a business rule st at es t hat Oozinoz will not perform for fewer t han 25 people or for less t han $495.95. To support t his rule, you can creat e a ReservationConstants int erface:
publ i c i nt er f ace Reser vat i onConst ant s { publ i c st at i c f i nal i nt MI NHEAD = 25; publ i c st at i c f i nal doubl e MI NTOTAL = 495. 95; } A reservat ion for t oo sm all an audience or t hat generat es t oo lit t le incom e is invalid. To avoid creat ing an inst ance of Reservation when a request is invalid, you m ight place business logic checks and except ion t hrowing in t he const ruct or for Reservation or in an init() m et hod t hat t his const ruct or calls. But t his logic is fairly independent of t he norm al funct ion of a Reservation obj ect once it is creat ed. I nt roducing a builder will m ake t he Reservation class sim pler, leaving it wit h m et hods t hat concent rat e on behavior ot her t han const ruct ion. Using a builder also creat es an opport unit y t o validat e t he param et ers of a Reservation obj ect wit h different react ions t o invalid param et ers. Finally, by m oving t he const ruct ion j ob t o a ReservationBuilder subclass, you allow const ruct ion t o occur gradually, as t he parser finds a reservat ion's at t ribut es. Figure 15.2 shows concret e ReservationBuilder subclasses t hat differ in how forgiving t hey are of invalid param et ers. Figure 15.2. Builders can be forgiving or unforgiving as they try to create a valid object from a given set of parameters.
Code t hat uses a builder will look som et hing like t he following:
package com. oozi noz. appl i cat i ons; i mpor t com. oozi noz. r eser vat i on. *; publ i c cl ass ShowUnf or gi vi ng { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { St r i ng sampl e = "Dat e, November 5, Headcount , 250, " + "Ci t y, Spr i ngf i el d, Dol l ar sPer Head, 9. 95, " + "HasSi t e, No"; Reser vat i onBui l der b = new Unf or gi vi ngBui l der ( ) ; new Reser vat i onPar ser ( b) . par se( sampl e) ; t ry { Reser vat i on r = b. bui l d( ) ; Syst em. out . pr i nt l n( r ) ; } cat ch ( Bui l der Except i on e)
{ Syst em. out . pr i nt l n( e. get Message( ) ) ; } } } Running t his program print s out a Reservation obj ect :
Dat e: Nov 5, 2001, Headcount : 250, Ci t y: Spr i ngf i el d, Dol l ar s/ Head: 9. 95, Has Si t e: f al se Given a reservat ion request st ring, t he code inst ant iat es a builder and a parser and asks t he parser t o parse t he st ring. As it reads t he st ring, t he parser passes t he reservat ion at t ribut es t o t he builder, using t he builder's set m et hods. ( The parser is a bit off t he t opic of BUI LDER, but it s code is available at oozinoz.com .) Aft er parsing, t he code asks t he builder t o build a valid reservat ion. This exam ple j ust print s out an except ion's m essage t ext rat her t han t aking a m ore significant act ion as you would in a real applicat ion.
CHALLENGE 15.2
The build() m et hod of t he UnforgivingBuilder class t hrows a BuilderException if t he dat e or cit y is null, if t he headcount is t oo low, or if t he t ot al cost of t he proposed reservat ion is t oo low. Writ e t he code for t he build() m et hod according t o t hese specificat ions.
Building a Counteroffer The UnforgivingBuilder class will rej ect request s t hat are anyt hing less t han fully form ed. Suppose t hat t he business decides t hat t he soft ware should m ake reasonable changes t o request s t hat are m issing cert ain det ails about t he reservat ion. Specifically, suppose t hat an analyst asks you t o set t he headcount for an event t o t he m inim um if t his at t ribut e is m issing. Sim ilarly, if t he dollars/ head value is m issing, you are t o set it t o be high enough so t hat t he t ot al t ake is above t he m inim um . I f you m ake eit her of t hese changes, you should m ark t he reservat ion as a count eroffer t hat t he reservat ion syst em will confirm wit h t he cust om er. To m eet t his request , suppose t hat you add a Boolean counteroffer argum ent m et hod t o t he Reservation const ruct or and develop a new ForgivingBuilder class. As you build a reservat ion in ForgivingBuilder, do t he following. 1. I f t he reservat ion request specifies no headcount and no dollars/ head, set t he headcount t o t he m inim um and set dollars/ head t o t he m inim um t ot al divided by t he headcount . 2. I f t here is no headcount but t here is a dollars/ head value, set t he headcount t o be at least t he m inim um at t endance and at least enough t o generat e enough m oney for t he event . 3. I f t here is a headcount but no dollars/ head value, set t he dollars/ head value t o be high enough t o generat e t he m inim um t ake. As before, your code should t hrow an except ion if t he reservat ion fails t o specify a cit y or a dat e, as t here is no way t o guess t hese values.
CHALLENGE 15.3
Writ e t he code for t he build() m et hod of t he ForgivingBuilder class.
The classes ForgivingBuilder and UnforgivingBuilder let you guarant ee t hat Reservation obj ect s are always valid. Your design also gives you flexibilit y about what act ion t o t ake when t here is a problem in const ruct ing a reservat ion.
Summary The BUI LDER pat t ern separat es t he const ruct ion of a com plex obj ect from it s represent at ion. This has t he im m ediat e effect of m aking a com plex t arget class sim pler. I t let s a builder class focus on t he proper const ruct ion of an obj ect , leaving t he t arget class t o focus on t he operat ion of a valid inst ance. This is especially useful when you want t o ensure t he validit y of an obj ect before inst ant iat ing it and don't want t he associat ed logic t o appear in t he t arget class's const ruct ors. A builder also accom m odat es st ep- by- st ep const ruct ion, which occurs when you creat e an obj ect by parsing t ext and m ay occur when you gat her an obj ect 's param et ers from a user int erface.
Chapter 16. Factory Method As a class developer, you will ordinarily provide class const ruct ors t o let users of your class inst ant iat e it . Som et im es, however, a client t hat needs an obj ect does not or should not know which of several possible classes t o inst ant iat e. The FACTORY METHOD pat t ern let s a class developer define t he int erface for creat ing an obj ect while ret aining cont rol of which class t o inst ant iat e.
Recognizing Factory Method You m ight t hink any m et hod t hat creat es and ret urns a new obj ect is a " fact ory" m et hod. I n obj ect - orient ed program m ing, however, m et hods t hat ret urn new obj ect s are com m on, and not every such m et hod is an inst ance of t he FACTORY METHOD pat t ern.
CHALLENGE 16.1
Nam e t wo com m only used Java m et hods t hat ret urn a new obj ect .
The fact t hat a m et hod creat es a new obj ect does not in it self m ean t hat it is an exam ple of t he FACTORY METHOD pat t ern. The FACTORY METHOD pat t ern requires t hat an operat ion t hat creat es an obj ect also isolat es it s client from knowing which class t o inst ant iat e. I n FACTORY METHOD, you will find several classes t hat im plem ent t he sam e operat ion, ret urning t he sam e, abst ract t ype but int ernally inst ant iat ing different classes t hat im plem ent t he t ype. To sum m arize, t he signs t hat FACTORY METHOD is at work are t hat an operat ion: •
Creates a new object
•
Returns a type that is an abstract class or an interface
•
Is implemented by several classes
Table 16.1 shows a few m et hods from t he Java class libraries; t hese m et hods have all or som e of t he charact erist ics of t he FACTORY METHOD pat t ern. For exam ple, t he
BorderFactory class has a variet y of m et hods t hat creat e and ret urn new obj ect s. The class is a fact ory but is not an exam ple of t he FACTORY METHOD pat t ern. Table 16.1. Characteristics of Factory Method
METHOD BorderFactory. createEtchedBorder() Arrays.asList()
CREATES AN OBJECT RETURNS ABSTRACT CLASS OR INTERFACE IMPLEMENTED BY SEVERAL CLASSES INSTANCE OF FACTORY METHOD x x x x
toString() iterator()
CHALLENGE 16.2
x x
x x
x
Explain how t he int ent of FACTORY METHOD is different from t he int ent of t he BorderFactory class.
You m ight feel t hat a m et hod like Arrays.asList(), which inst ant iat es an obj ect and has an int erface ret urn t ype, is an exam ple of FACTORY METHOD. Aft er all, such m et hods do isolat e a client from knowing which class t o inst ant iat e. But t he spirit of FACTORY METHOD is t hat t he obj ect creat or m akes a choice about which of several possible classes t o inst ant iat e for t he client . The Arrays.asList() m et hod inst ant iat es t he sam e t ype of obj ect for every client , so t his m et hod does not fulfill t he int ent of FACTORY METHOD. Usually, you will find t hat several classes in a hierarchy im plem ent t he FACTORY METHOD operat ion. Then t he decision of which class t o inst ant iat e depends on t he class of t he obj ect t hat receives t he call t o creat e. I n ot her words, FACTORY METHOD let s subclasses decide which class t o inst ant iat e. Many classes, including t hose in hierarchies, im plem ent t he toString() m et hod. But t he toString() m et hod always ret urns a String obj ect . The classes t hat im plem ent toString() do not det erm ine which class t o inst ant iat e, so t his m et hod is not an exam ple of FACTORY METHOD.
A Classic Example of Factory Method: Iterators The I TERATOR pat t ern ( see Chapt er 28, I t erat or) provides a way t o access t he elem ent s of collect ion sequent ially. But iterator() m et hods t hem selves are usually good exam ples of t he FACTORY METHOD pat t ern. An iterator() m et hod isolat es it s caller from knowing which class t o inst ant iat e. The Java JDK version 1.2 release int roduced t he Collection int erface, which includes t he iterator() operat ion. All collect ions im plem ent t his operat ion. An iterator() m et hod creat es an obj ect t hat ret urns a sequence of t he elem ent s of a collect ion. For exam ple, t he following code creat es and print s out t he elem ent s of a list :
package com. oozi noz. appl i cat i ons; i mpor t j ava. ut i l . *; publ i c cl ass ShowI t er at or
{ publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Li st l i st = Ar r ays. asLi st ( new St r i ng[ ] { "f ount ai n", "r ocket ", "spar kl er " } ) ; I t er at or i = l i st . i t er at or ( ) ; whi l e ( i . hasNext ( ) ) { Syst em. out . pr i nt l n( i . next ( ) ) ; } } }
CHALLENGE 16.3
What class is t he Iterator obj ect in t his code?
The point of t he FACTORY METHOD pat t ern, which t he iterator() m et hod exem plifies well, is t hat client s of t he m et hod need not know which class t o inst ant iat e.
Taking Control of Which Class to Instantiate As a client of, say, a List, you know when you need an it erat or, but you don't know and don't want t o know which class t o inst ant iat e t o creat e t he it erat or. I n t his case, t he service provider—t he developers of t he List class—know which class t o inst ant iat e when a client needs an it erat or. This sit uat ion, in which t he service provider has m ore knowledge t han t he client about which class t o inst ant iat e, also occurs frequent ly in applicat ion code. Suppose t hat Oozinoz want s t o st art let t ing cust om ers buy fireworks on credit . Early in t he design of t he credit aut horizat ion syst em , you accept responsibilit y for developing a CreditCheckOnline class t hat checks t o see whet her a cust om er can m aint ain a cert ain am ount of credit w it h Oozinoz. As you begin developm ent , you realize t hat occasionally t he credit agency will be offline. The analyst on t he proj ect det erm ines t hat in t his sit uat ion, t he business want s you t o bring up a dialog for t he call cent er represent at ive and t o m ake a credit decision based on a few quest ions. To handle t his, you creat e a CreditCheckOffline class and get it working t o specificat ion. I nit ially, you design t he classes as shown in Figure 16.1. The creditLimit() m et hod accept s a cust om er's ident ificat ion num ber and ret urns t hat cust om er's credit lim it . Figure 16.1. You can instantiate one of these classes to check a customer's credit, depending on whether the online credit agency is up.
Wit h t he classes in Figure 16.1 you can provide credit lim it inform at ion whet her or not t he credit agency is online. The problem now is t hat t he user of your classes needs t o know which class t o inst ant iat e. But you are t he one who knows whet her t he agency is up! I n t his scenario, you need t o com m it t o t he int erface for creat ing an obj ect but keep cont rol of which class t o inst ant iat e. One solut ion is t o change bot h classes t o im plem ent a st andard int erface and t o creat e a fact ory m et hod t hat ret urns an obj ect of t hat t ype. Specifically, you m ight • • •
Make CreditCheck a Java interface that includes the creditLimit() method Change both credit check classes to declare that they implement the CreditCheck interface Create a CreditCheckFactory class that provides a createCreditCheck() method that returns an object whose type is CreditCheck
When you im plem ent createCreditCheck(), you will use your knowledge of t he credit agency's st at us t o decide which class t o inst ant iat e.
CHALLENGE 16.4
Draw a class diagram t hat est ablishes a way t o creat e a credit - checking obj ect while ret aining cont rol of which class t o inst ant iat e.
By applying FACTORY METHOD, you let t he user of your services call t he createCreditCheck() m et hod t o get a credit - checking obj ect t hat works regardless of whet her t he credit agency is online.
CHALLENGE 16.5
Assum e t hat t he CreditCheckFactory class has an isAgencyUp() m et hod t hat t ells whet her t he credit agency is available, and writ e t he code for createCreditCheck().
Factory Method in Parallel Hierarchies The FACTORY METHOD pat t ern oft en appears when you use parallel hierarchies t o m odel a problem dom ain. A pa r a lle l hie r a r chy is a pair of class hierarchies in which each class in one hierarchy has a corresponding class in t he ot her hierarchy. Parallel hierarchies usually em erge when you decide t o m ove a subset of behavior out of an exist ing hierarchy. Consider t he const ruct ion of aerial shells, as illust rat ed in Chapt er 5, Com posit e. To build t hese shells, Oozinoz uses t he m achines m odeled in Figure 16.2. Figure 16.2. The Machine hierarchy contains logic for controlling physical machines and for planning.
To m ake a shell, we m ix chem icals in a m ixer and pass t hem t o a st ar press, which ext rudes individual st ars. We pack st ars int o a shell around a core of black powder and place t his over a lift ing charge, using a shell assem bler. We use a fuser t o insert t he fusing t hat will ignit e bot h t he lift ing charge and t he shell core.
Suppose t hat you need t he getAvailable() m et hod t o forecast when a m achine will com plet e it s current processing and be available for m ore work. This m et hod m ay require several support ing privat e m et hods, adding up t o quit e a bit of logic t o add t o each of our m achine classes. Rat her t han adding t he planning logic t o t he Machine hierarchy, you m ight prefer t o have a separat e MachinePlanner hierarchy. You need a separat e planner class for m ost m achine t ypes, but m ixers and fusers are always available for addit ional work. For t hese classes, you can use a BasicPlanner class.
CHALLENGE 16.6
Fill in t he diagram of a Machine/ MachinePlanner parallel hierarchy in Figure 16.3. Figure 16.3. Slim down the Machine hierarchy by moving planning logic to a parallel hierarchy.
CHALLENGE 16.7
Suppose t hat you m ake t he Machine class abst ract and add an abst ract m et hod createPlanner(). Writ e a createPlanner() m et hod for t he Fuser and StarPress classes.
Summary The int ent of t he FACTORY METHOD pat t ern is t o define t he int erface for creat ing a new obj ect so t hat a service provider decides which class t o inst ant iat e inst ead of client s. This pat t ern occurs in t he Java class libraries and is com m on in applicat ion code.
You can also apply FACTORY METHOD when you want t o int roduce a parallel class hierarchy. This can help keep a set of classes from becom ing bloat ed wit h m any different aspect s of behavior. FACTORY METHOD let s you connect parallel hierarchies by let t ing subclasses in one hierarchy det erm ine which class t o inst ant iat e in t he corresponding hierarchy.
Chapter 17. Abstract Factory Som et im es, you want t o provide for obj ect creat ion while ret aining cont rol of which class t o inst ant iat e. I n such circum st ances, you can apply t he FACTORY METHOD pat t ern wit h a m et hod t hat uses an out side fact or t o det erm ine which class t o inst ant iat e. The out side fact or can be anyt hing. I n Chapt er 16, Fact ory Met hod, t his fact or was t he st at us of an online credit agency. Som et im es, t he fact or t hat cont rols which obj ect t o inst ant iat e can be t hem at ic, running across several classes. The ABSTRACT FACTORY pat t ern addresses t his sit uat ion. The int ent of t his pat t ern is t o provide for t he creat ion of a fam ily of relat ed, or dependent , obj ect s.
Abstract Factories for Families of Objects To answer t he challenges in Chapt er 16, you designed a pair of classes t o im plem ent t he CreditCheck int erface. Your design isolat es ot her developers from knowing whet her t he credit agency is up and running. Suppose t hat in support ing t he verificat ion of cust om er inform at ion, you add a couple of classes t hat check t he st at us of a cust om er's billing and shipping addresses. Your package of checking services looks like Figure 17.1. Figure 17.1. Classes in this package check a customer's credit, shipping address, and billing address.
Now suppose t hat a requirem ent s analyst t ells you t hat Oozinoz want s t o st art servicing cust om ers in Canada. To do business in Canada, you will use a different credit agency and different dat a sources t o est ablish at t ribut es of cust om er addresses, such as whet her t hey incur t ariffs or are resident ial. When a cust om er calls, t he call cent er applicat ion needs a fam ily of obj ect s t o perform a variet y of checks. The fam ily t o use depends on whet her t he call is from Canada or from t he Unit ed St at es. You can apply t he ABSTRACT FACTORY pat t ern t o provide for t he creat ion of t hese obj ect fam ilies. Suppose t hat you decide t o m aint ain t hree packages. Package com.ooz-inoz.check will cont ain t hree check int erfaces and an abst ract fact ory class. This class will have t hree create m et hods t hat creat e t he t hree kinds of check obj ect s. You will also put CreditCheckOffline in t his package, as you can use t his class for offline checks regardless of a call's origin. Figure 17.2 shows t he new cont ent s of com.oozinoz.check. Figure 17.2. The revised package contains primarily interfaces and an abstract factory class.
To im plem ent t he int erfaces in com.oozinoz.check wit h concret e classes, you can int roduce t wo new packages: com.oozinoz.check.canada and com.oozinoz.check.us. Each of t hese packages can cont ain a concret e version of t he fact ory class and classes t o im plem ent each of t he int erfaces in com.oozinoz.check.
CHALLENGE 17.1
Com plet e t he diagram in Figure 17.3, which shows t he classes in com.oozinoz.check.canada and t heir relat ion t o classes and int erfaces in com.oozinoz.check. Figure 17.3. Show the classes in the canada package and their relationships to classes in com.oozinoz.check.
The concret e fact ory classes for Canadian and U.S. calls are fairly sim ple. The classes ret urn t he Canadian or U.S. versions of t he check int erfaces, wit h t he except ion t hat bot h concret e fact ories ret urn a CreditCheck-Offline obj ect if t he local credit agency is offline. As in t he previous chapt er, t he CheckFactory class has an isAgencyUp() m et hod t hat t ells whet her t he credit agency is available.
CHALLENGE 17.2
Com plet e t he code for CheckFactoryCanada.java:
package com. oozi noz. check. canada; i mpor t com. oozi noz. check. *; publ i c cl ass CheckFact or yCanada ext ends CheckFact or y { / / ?? } At t his point , you have a solid design t hat applies t he ABSTRACT FACTORY pat t ern t o provide for t he creat ion of fam ilies of obj ect s t hat conduct checks of different kinds of cust om er inform at ion. You have one fam ily for U.S. checks and one for Canada checks. A final st ep is t o m ake your fact ories readily accessible.
CHALLENGE 17.3
Your syst em needs only one fact ory obj ect for Canada checks and one for Unit ed St at es checks. Writ e CheckFactory.java, including st at ic variables t hat m ake it easy for an applicat ion developer t o access t hese fact ories:
package com. oozi noz. check; i mpor t ?? i mpor t ?? publ i c abst r act cl ass CheckFact or y { publ i c st at i c f i nal CheckFact or y US = ?? publ i c st at i c f i nal CheckFact or y ?? publ i c abst r act ?? cr eat eBi l l i ngCheck ?? publ i c abst r act ?? cr eat eCr edi t Check ?? publ i c abst r act ?? cr eat eShi ppi ngCheck ?? }
Packages and Abstract Factories Speaking loosely, a package is usually a " fam ily" of classes, and an abst ract fact ory produces a " fam ily" of obj ect s. I n t he previous exam ple, you used separat e packages t o support abst ract fact ories for Canada and t he Unit ed St at es, wit h a t hird package t hat provides com m on int erfaces for t he obj ect s t he fact ories produce.
CHALLENGE 17.4
Writ e down an argum ent support ing t he decision t o place each fact ory and it s relat ed classes in a separat e package. Alt ernat ively, argue t hat anot her approach is superior.
Abstract Factories for Look-and-Feel Different operat ing syst em s have different st andards about t he look- and- feel—t hat is, t he appearance and behavior—of user int erface com ponent s. Java Swing com es wit h a plat form - independent look- and- feel, as well as t he abilit y t o m im ic t he look- and- feel st andards for Microsoft Windows and Mot if. I n applicat ion developm ent , you can go well beyond t hese st andards in specifying a look- and- feel for your applicat ion suit e. User environm ent s t hat m aint ain consist ent st andards look m ore professional and are easier t o use t han syst em s t hat do not adhere t o a st andard. For exam ple, you m ight want t o st andardize but t on sizes, font s, background and foreground colors, border widt hs, and m any ot her com ponent at t ribut es. Wit hout a st andard, user environm ent s t end t o t ake on an unpolished, crazy- quilt look t hat is difficult t o navigat e. To provide a st andard look- and- feel, you can est ablish a package of st andard com ponent classes. Figure 17.4 shows a user environm ent package for Oozinoz. Figure 17.4. Oozinoz consistently uses subclasses of Swing components to achieve a sharp, uniform look.
The current policy at Oozinoz requires developers t o use t hese classes rat her t han corresponding Swing com ponent s. This policy helps t o st andardize t he look- and- feel of Oozinoz applicat ions. The problem m ay arise, however, t hat t he business needs different look- and- feels in different sit uat ions.
CHALLENGE 17.5
Writ e down t wo reasons why Oozinoz m ight want t o provide different look- and- feels in user environm ent s.
To allow for different look- and- feel st andards t o exist sim ult aneously, you can creat e an abst ract class t hat ret urns t he st andard com ponent s. Concret e subclasses can t hen ret urn fam ilies of com ponent s t hat are appropriat e for a specific kind of user environm ent .
CHALLENGE 17.6
Draw a diagram of an abst ract class t hat will creat e st andard Oozinoz com ponent s.
Concret e subclasses of your abst ract class will work as fact ories. Each subclass will produce com ponent s t hat are appropriat e t o a specific t ype of display, such as handheld devices or full screens. For exam ple, if you creat e HandheldKit as a subclass of your abst ract fact ory, you will creat e HandheldKit.createButton() so t hat it creat es and ret urns a sm all but t on wit h a sm all font size for it s label. Each class t hat im plem ent s createButton() will ret urn an OzButton obj ect wit h at t ribut es t hat are consist ent wit h a look- and- feel. Design Pat t erns ( Gam m a et al. 1995) says t hat t he int ent for ABSTRACT FACTORY is t hat client s can creat e fam ilies of obj ect s " wit hout specifying t heir concret e classes." A lit eral reading of t his m ight exclude t he hierarchy of user int erface com ponent fact ories because client s will in fact know t he exact class for each t ype of obj ect . However, t he point of t he ABSTRACT FACTORY pat t ern is t hat t he fact ory classes isolat e client s from knowing how t o creat e obj ect s. Wit h t he user int erface hierarchy, client s know which class t o inst ant iat e but do not know how t o init ialize each obj ect . For exam ple, client s will rely on t he HandheldKit class t o know how t o init ialize each com ponent t o be a reasonable size for a handheld device. Because t he user int erface hierarchy of fact ory classes isolat es client s from knowing how t o creat e obj ect s, t his hierarchy follows t he int ent of ABSTRACT FACTORY.
Summary The ABSTRACT FACTORY pat t ern let s you arrange for a client t o creat e obj ect s t hat are part of a fam ily of relat ed, or dependent , obj ect s. The t hem e, or com m on t rait of t he fam ily, such as which count ry t he classes pert ain t o, m ay run across m any classes. I n t his case, you m ay m aint ain parallel packages, perhaps placing each fam ily in it s own package. For GUI fact ories, you m ay not need m ore t han one package if different " classes" of GUI com ponent s differ only in such at t ribut es as t ext size and cursor t ype. However you organize your code, ABSTRACT FACTORY let s you provide a client wit h a fact ory t hat produces obj ect s t hat are relat ed by a com m on t hem e.
Chapter 18. Prototype As a class developer, you ordinarily furnish your class wit h const ruct ors t o let client applicat ions inst ant iat e it . The const ruct ion- orient ed pat t erns covered so far—BUI LDER, FACTORY METHOD, and ABSTRACT FACTORY—isolat e t he user of an obj ect from t his const ruct or call. These pat t erns est ablish m et hods t hat inst ant iat e an appropriat e class on a client 's behalf. The PROTOTYPE pat t ern also conceals obj ect creat ion from client s but uses a different approach. I nst ead of bringing fort h new, uninit ialized inst ances of a class, t he int ent of t he PROTOTYPE pat t ern is t o provide new obj ect s by copying an exam ple.
Prototypes as Factories Suppose t hat you are using t he ABSTRACT FACTORY pat t ern at Oozinoz t o provide user int erfaces for several cont ext s. Figure 18.1 shows t he user int erface fact ories you have in place. Figure 18.1. Three abstract factories, or kits, produce different user interface look-andfeels.
The users at Oozinoz are t hrilled wit h t he product ivit y t hat result s from applying t he right user int erface in t he right cont ext . The problem you face is t hat t hey want several m ore user int erface kit s, and it 's becom ing burdensom e t o creat e a new class for each cont ext your users envision.
To halt t he proliferat ion of user int erface fact ory classes, you can replace t hem all wit h a single UIKit class t hat has prot ot ypical obj ect s in it s inst ance variables. This class's creat ion m et hods will ret urn copies of t hese prot ot ypical obj ect s. You haven't figured out yet how your copy m et hods will work or how you will set up your prot ot ypical fact ories, but having a sket ch of t he class will give you a t arget t o work t oward.
CHALLENGE 18.1
Draw a diagram of a UIKit class, showing inst ance variables for prot ot ypical but t on and t ext area obj ect s and showing t he creat ion m et hods t hat will ret urn copies of t hese obj ect s.
Suppose t hat in t he declarat ions of inst ance variables in UIKit, you init ialize each obj ect as follows:
pr ot ect ed OzBut t on but t on = new OzBut t on( ) ; pr ot ect ed OzText Ar ea t ext Ar ea = new OzText Ar ea( ) ; //. . . At present , t he various user int erfaces differ in t he font and t he cursor you use for each com ponent . For full- screen com ponent s, you use t he default cursor and t he following font :
Font f = new Font ( "Di al og", Font . I TALI C, 18) ; For handheld screen com ponent s you use t he follow ing cursor and font :
Cur sor c = new Cur sor ( Cur sor . HAND_CURSOR) ; Font f = new Font ( "Di al og", Font . PLAI N, 8) ; To creat e a fact ory for full- screen or handheld com ponent s, you creat e a UIKit obj ect and set t he cursor and font of t his obj ect 's inst ance variables. A convenient place t o do t his work is in UIKit st at ic m et hods t hat ret urn inst ances of t he appropriat ely t uned fact ories.
CHALLENGE 18.2
Writ e t he code for UIKit.handheld(), a st at ic m et hod t hat ret urns a fact ory for handheld display com ponent s.
Prototyping with Clones To com plet e t he fact ory design, you have t o est ablish how t he prot ot ypical inst ance variables in a UIKit obj ect will copy t hem selves. You consider using clone(), a m et hod t hat every class inherit s from Object. However, t his m et hod m ust be used w it h caut ion; it does not provide foolproof copying of an obj ect .
CHALLENGE 18.3
Consult t he source code for Object.clone() and writ e down what t his m et hod does.
Consider copying an OzTextArea obj ect . When you invest igat e t his class, you see t hat it is a subclass of JPanel, as Figure 18.2 shows. Figure 18.2. The OzTextArea class inherits a large number of methods and variables from its superclasses.
Subclassing from JPanel let s OzTextArea obj ect s uniform ly wrap t heir cont ent s in a scrolling panel and a st andard border. This m akes t ext panels in t he GUI m ore appealing, but it also has t he effect of giving OzTextArea obj ect s a huge num ber of inst ance variables, including t hose t hat OzTextArea declares and all t hose t hat OzTextArea inherit s from it s superclasses. A GUI obj ect can easily have m ore t han 100 inst ance variables. These at t ribut es oft en include dependencies on ot her obj ect s and on operat ing syst em resources t hat you cannot safely copy references t o. Rat her t han using Object.clone() t o creat e a new GUI obj ect , it is safer t o inst ant iat e t he class you want and t o set t he new obj ect 's inst ance variables explicit ly.
CHALLENGE 18.4
Writ e OzTextArea.clone() so t hat it copies a t ext area wit hout relying on a superclass im plem ent at ion of clone().
Aft er arranging for your prot ot ypical GUI obj ect s t o copy t hem selves, your design is com plet e. You have replaced t he com plet e ComponentKit hierarchy wit h a single class, UIKit. As wit h any abst ract fact ory, you can creat e m et hods t hat build screens t hat are independent of t he look- and- feel of a specific com ponent set . For exam ple, t he following class const ruct s and displays a panel wit h cross- sales inst ruct ions on a handheld device:
package com. oozi noz. ui ; i mpor t j ava. awt . *; i mpor t j avax. swi ng. *; publ i c cl ass ShowKi t { publ i c st at i c J Panel cr ossSal es( UI Ki t k) { J Panel p = new J Panel ( ) ; p. set Layout ( new Bor der Layout ( ) ) ; p. add( k. cr eat eBut t on( "Cl ear ") , "Sout h") ; OzText Ar ea t = k. cr eat eText Ar ea( ) ; t . append( " 1) Consul t t he r ecommendat i on l i st . \ n") ; t . append( " 2) Est abl i sh cust omer i nt er est . \ n") ; t . append( " 3) Cl ose t he sal e. \ n") ; p. add( t , "Cent er ") ; r et ur n p; } publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { UI Ki t k = UI Ki t . handhel d( ) ; J Panel p = ShowKi t . cr ossSal es( k) ; Swi ngFacade. l aunch( p, " Oozi noz Cr oss Sal es") ; } } This code launches a panel t hat is t oo t iny t o show in t his book. I n pract ice, such an applicat ion will also set up an act ion list ener t o react t o a user's screen clicks.
Using
Object.clone()
The Oozinoz applicat ion suit e includes a visualizat ion t hat shows users how m at erial is flowing t hrough m achines on t he fact ory floor. Now t he business want s a sim ulat ion version of t his screen t o let t he user experim ent wit h releasing orders int o t he fact ory, changing m achine at t ribut es, and alt ering ot her aspect s of t he fact ory's operat ion. A part icular feat ure your users want in t he sim ulat ion is t o be able t o drag- and- drop a copy of a sim ulat ed m achine. On t his proj ect , your j ob is t o creat e t he m achine sim ulat ion obj ect s, which you have com plet ed as Figure 18.3 shows. Figure 18.3. Machine simulations have at least a location and mean-time- between-failure for the machine.
Every m achine sim ulat ion obj ect has a m ean- t im e- bet ween- failure ( M TBF) , a field t hat shows t he average num ber of hours bet ween t he t im e a m achine goes down and t he t im e it com es up again. Each sim ulat ion also has a Location field. Not e t hat in t he sim ulat ion, bays are ident ified by num ber and are not t reat ed as m achine com posit es. The locat ion of a m achine is m easured as t he m achine's dist ance, in any unit s, from t he nort hwest corner of t he bay t hat t he m achine is in. Figure 18.4 shows t his coordinat e syst em . Figure 18.4. X and Y coordinates in a bay are measured as offsets from the northwest corner of the bay.
You have included a clone_1() m et hod wit h MachineSimulator. ( The underscore in t he m et hod nam e indicat es t hat we will lat er alt er t his m et hod.) The point of t he m et hod is t hat you can use clone_1() as a fact ory m et hod, producing a new m achine sim ulat ion obj ect from a prot ot ype t he user clicks on. Your init ial code for MachineSimulator.clone_1() is:
publ i c Obj ect cl one_1( ) { t ry { r et ur n super . cl one( ) ; } cat ch ( Cl oneNot Suppor t edExcept i on e) { / / t hi s shoul dn' t happen, si nce we ar e Cl oneabl e t hr ow new I nt er nal Er r or ( ) ; } } This code m akes t he clone_1() m et hod public, and we shall soon see t hat we need t o m odify t his code t o creat e a proper copy. First , t hough, not e t hat MachineSimulator im plem ent s Cloneable. This int erface is a m arker t hat Object.clone() checks t o ensure t hat t he clone() m et hod is appropriat e for a subclass. Wit hout t his declarat ion, t he clone_1() m et hod would t hrow a CloneNotSupportedException when it called super.clone(). The possibilit y t hat t his except ion will be t hrown forces you t o enclose t he call in a try/catch st at em ent . However, you are confident t hat t his except ion will never be t hrown; if it is t hrown, you decide t o t hrow in t urn an InternalError error, w hich is w hat t he frequent ly used ArrayList class does in t his sit uat ion. This code com piles and runs wit hout t hrowing an except ion. However, t he code cont ains a defect t hat Challenge 18.5 brings out .
CHALLENGE 18.5
The following lines of code creat e a sim ulat ion obj ect of a m ixer in bay 1 and a copy for bay 2:
package com. oozi noz. si mul at i on; i mpor t com. oozi noz. uni t s. *; publ i c cl ass ShowCl oni ngPr obl em i mpl ement s Uni t Const ant s { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { / / Mi xer 1 i s i n bay 1, at coor di nat e( 15m, 25m) Lengt h x = ( Lengt h) METER. t i mes( 15) ; Lengt h y = ( Lengt h) METER. t i mes( 25) ; Locat i on l oc = new Locat i on( 1, x, y) ;
/ / 10 hour s mean- t i me- bet ween- f ai l ur e Mi xer Si mul at or m1 = new Mi xer Si mul at or ( l oc, ( Ti me) HOUR. t i mes( 10) ) ; Mi xer Si mul at or m2 = ( Mi xer Si mul at or )
Mi xer Si mul at or m2 = ( Mi xer Si mul at or ) m1. cl one_1( ) ; / / Mi xer 2 i s i n bay 2, at coor di nat e ( 20, 75) m2. l ocat i on. set Bay( 2) ; m2. l ocat i on. set Coor di nat es( ( Lengt h) METER. t i mes( 20) , ( Lengt h) METER. t i mes( 75) ) ; Syst em. out . pr i nt l n( m1. l ocat i on. bay) ; } } Draw a diagram of t he obj ect s creat ed by t his code. Also, writ e down t he result s of t he println() st at em ent .
You m ight want t o m ake Location obj ect s im m ut able, rem oving t he class's set m et hods, so t hat any num ber of client s could safely share a reference t o t he sam e locat ion. At Oozinoz, however, m achine locat ions do occasionally change. Rat her t han m aking Location obj ect s im m ut able, you can fix t he problem wit h MachineSimulator.clone() by m aking Location obj ect s cloneable.
CHALLENGE 18.6
Suppose t hat you change t he Location class t o declare t hat it im plem ent s Cloneable and writ e a properly funct ioning clone() m et hod for it . Now writ e a revised version of t he clone() m et hod for t he MachineSimulator class.
Summary The PROTOTYPE pat t ern let s a client creat e new obj ect s by copying an exam ple. A m aj or difference bet ween calling a const ruct or and copying an obj ect is t hat a copy t ypically includes som e of t he st at e of t he original obj ect . You can use t his t o your advant age, part icularly when different "classes" of obj ect s differ only in t heir at t ribut es, not in t heir behaviors. I n such a case, you can creat e new classes at runt im e by craft ing prot ot ypical obj ect s for a client t o copy. When you need t o creat e a copy, Object.clone() can help, but you m ust rem em ber t hat t his m et hod creat es a new obj ect wit h t he sam e fields. This obj ect m ay not be a suit able clone, but any difficult ies relat ed t o deep copying are your responsibilit y. I f creat ing a deep copy is t oo com plex or if a prot ot ypical obj ect has t oo m any fields, you can creat e a copy by inst ant iat ing a new obj ect and set t ing it s fields t o m at ch j ust t he aspect s of original obj ect t hat you need t o copy.
Chapter 19. Memento Som et im es, you want t o creat e an obj ect t hat exist ed previously. This occurs when you want t o let a user undo operat ions, revert t o an earlier version of work, or resum e work t hat he or she previously suspended. The int ent of t he MEMENTO pat t ern is t o provide st orage and rest orat ion of an obj ect 's st at e.
Memento Durability A m em ent o is a t iny reposit ory t hat saves an obj ect 's st at e. You can creat e a m em ent o by using anot her obj ect , a st ring, or a file. The ant icipat ed durat ion bet ween t he st orage and t he reconst ruct ion of an obj ect has an effect on t he st rat egy t hat you can use in designing a m em ent o. The t im e t hat elapses can be m om ent s, hours, days, or years.
CHALLENGE 19.1
Writ e down t wo reasons t hat m ight drive you t o saving a m em ent o in a file rat her t han as an obj ect .
Applying Memento I n t he preceding chapt er, you used t he PROTOTYPE pat t ern t o design a sim ulat ion t hat let users speculat ively creat e new m achine sim ulat ors from exist ing ones. Your users like t he sim ulat ion, but t hey som et im es m ake changes t hat t hey wish t hey could undo. You m ight consider using t he javax.swing.undo package t o support an undo operat ion. However, t he undo package focuses it s support on operat ions for t ext ual Swing com ponent s. I n t his applicat ion, it is sim pler t o apply t he MEMENTO pat t ern t o save and t o rest ore t he st at e of t he sim ulat ion at key point s. •
•
Each time the user adds or moves a machine in the visualization, your code will create a memento of the simulated factory and add it to a stack. Each time the user clicks the Undo button, your code will pop the most recent memento and then restore the simulation to the state stored at the top of the stack.
When your visualizat ion st art s up, you will st ack an init ial, em pt y m em ent o and never pop it , t o ensure t hat t he t op of t he st ack is always a valid m em ent o. Any t im e t he st ack cont ains j ust one m em ent o, you disable t he Undo but t on. Figure 19.1 shows t he visualizat ion aft er t he user has added and dragged several m achines int o place. Figure 19.1. This visualization adds machines at the top left location of the panel and lets a user drag machines into place. Users can undo either drags or adds.
The com.oozinoz.visualization package cont ains t he classes t hat support t he visualizat ion. An execut able Visualization class subclasses from t he MouseInputAdapter class, from java.swing.event, t o sim plify list ening t o m ouse event s. The Visualization class relies on a machine.gif file t hat m ust lie in it s class pat h. Bot h t his class and a VizPanel class rely on a FactoryModel obj ect t hat cont ains t he m achine im ages and t heir locat ions. Figure 19.2 shows t he classes in t he visualizat ion package. Figure 19.2. A Visualization object lets a user paint a picture of a prospective factory.
When t he user clicks Add or drags and releases a m achine im age, t he Visualization obj ect asks t he FactoryModel obj ect for a new m em ent o. The Visualization obj ect st acks t his m em ent o. When t he user clicks Undo, t he Visualization obj ect pops a m em ent o and calls t he FactoryModel obj ect 's restore() m et hod, passing in t he m em ent o at t he new t op of t he st ack. The FactoryModel obj ect creat es a m em ent o by creat ing a list and populat ing it wit h clones of t he current MachineImage obj ect s in it s machines list .
CHALLENGE 19.2
Writ e t he code for t he createMemento() m et hod of FactoryModel so t hat t he m et hod ret urns a List of clones of m achine sim ulat ors.
The count erpart t o a createMemento() m et hod is a restore() m et hod t hat rest ores t he st at e of t he obj ect according t o t he inform at ion in t he m em ent o. I n t his exam ple, t he m em ent o is a list of m achine sim ulat ors. The restore() m et hod for FactoryModel t akes t his list as an argum ent and looks som et hing like t his:
publ i c voi d r est or e( Li st mement o) { machi nes = new Ar r ayLi st ( ) ; I t er at or i = mement o. i t er at or ( ) ; whi l e ( i . hasNext ( ) )
{ Machi neI mage mi = ( Machi neI mage) i . next ( ) ; machi nes. add( mi . cl one( ) ) ; } } When t he user clicks t he Undo but t on, t he visualizat ion's undo() m et hod pops t he m ost recent m em ent o, discarding t he st at e t o undo. Then t his m et hod rest ores t he sim ulat ion from t he m em ent o at t he t op of t he st ack. Finally, t his m et hod disables t he Undo but t on if t he st ack cont ains only t he init ial m em ent o.
CHALLENGE 19.3
Writ e t he code for t he visualizat ion's undo() m et hod.
I n t his exam ple, a clone of a MachineImage obj ect cont ains all t he st at e inform at ion you need t o lat er set t he obj ect back t o t hat st at e. Furt herm ore, a FactoryModel obj ect needs only a collect ion of clones of it s m achine im ages t o st ore t he st at e it needs t o rest ore t o in an undo() operat ion. When clones are available and t hey st ore t he inform at ion you need t o creat e a m em ent o, you can apply MEMENTO using clones. I n ot her cases, you m ay need t o creat e a special class, such as FactoryMemento, t o st ore t he st at e of a fact ory m odel.
Persisting Mementos across Sessions A se ssion occurs w hen a user runs a program , conduct s t ransact ions in t he program , and exit s. Suppose t hat your users want t o be able t o save a sim ulat ion in one session and rest ore it in anot her session. This abilit y is a m at t er norm ally referred t o as pe r sist e nt st or a ge . Persist ent st orage fulfills t he int ent of t he MEMENTO pat t ern and is a nat ural ext ension t o t he undo funct ionalit y you have already im plem ent ed. Suppose t hat you subclass t he Visualization class wit h a Visual-ization2 class[ 1] t hat has a m enu bar wit h a File m enu. This m enu offers Save and Load com m ands t hat you link t o act ion m et hods: [1]
The Visualization2 class's name ends with a number but no underscore, to indicate that it is a revision but not a class that will be refactored. pr ot ect ed voi d save( ) { i nt st at us = f c( ) . showSaveDi al og( nul l ) ; i f ( st at us == J Fi l eChooser . APPROVE_OPTI ON) { Fi l e f = f c. get Sel ect edFi l e( ) ; t ry { Obj ect Out put St r eam out = new Obj ect Out put St r eam( new Fi l eOut put St r eam( f ) ) ; out . wr i t eObj ect ( / * ? */ ) ; out . f l ush( ) ; out . cl ose( ) ; } cat ch ( I OExcept i on e)
{ / / expl ai n t he pr obl em and of f er t o t r y agai n } } } pr ot ect ed J Fi l eChooser f c( ) { i f ( f c == nul l ) { f c = new J Fi l eChooser ( ) ; } r et ur n f c; } The save() m et hod uses a JFileChooser dialog t o det erm ine where t he user want s t o save t he sim ulat ion st at e. The ObjectOutputStream class, from java.io, provides t he abilit y t o writ e an obj ect t o disk.
CHALLENGE 19.4
What obj ect should t he save() m et hod writ e out ?
For t he save() m et hod t o work correct ly, t he MachineImage class m ust im plem ent t he Serializable int erface, and t he class's persist ent at t ribut es m ust also be serializable. I f you inspect t he code in t he visualization package, you will find t hat t he MachineImage class m arks it s image at t ribut e as transient. This causes a serializat ion of a MachineImage class t o not save t he image at t ribut e. The class lazy- init ializes it s im age from t he im age's file nam e, so when a MachineImage obj ect is rest ored, t he im age is rebuilt from t he original graphics file. ( This requires t hat t he im age exist in t he class pat h of t he Visualization2 class.) To allow users t o rest ore a previously const ruct ed sim ulat ion, t he File m enu offers a Load it em t hat calls a load() m et hod. The load() m et hod is parallel in design t o t he save() m et hod but uses t he classes ObjectInputStream and FileInputStream inst ead of t heir Output count erpart s t o ret rieve a visualizat ion's prior st at e. The load() m et hod em pt ies t he mementos st ack, pushes t he ret rieved m em ent o, disables t he Undo but t on, and calls t he FactoryModel class's restore() m et hod:
pr ot ect ed voi d l oad( ) { i nt di al ogSt at us = f c( ) . showOpenDi al og( nul l ) ; i f ( di al ogSt at us == J Fi l eChooser . APPROVE_OPTI ON) { Fi l e f = f c. get Sel ect edFi l e( ) ; t ry { Obj ect I nput St r eam i n = new Obj ect I nput St r eam( new Fi l eI nput St r eam( f ) ) ; Li st m = ( Li st ) i n. r eadObj ect ( ) ; i n. cl ose( ) ;
mement os. r emoveAl l El ement s( ) ; mement os. push( m) ; f act or y( ) . r est or e( m) ; undoBut t on( ) . set Enabl ed( f al se) ; vi zPanel ( ) . r epai nt ( ) ; } cat ch ( Except i on e) { Syst em. out . pr i nt l n( e) ; } } } For anot her com plet e exam ple of applying serializat ion, see Java™ Exam ples in a Nut shell ( Flanagan 1997) .
Using Strings as Mementos Obj ect serializat ion provides a sim ple m eans for a m em ent o t o persist across m ult iple sessions t hat occur, say, on a given day. I f, on t he ot her hand, your user w ant s t o revive a sim ulat ion aft er a six- m ont h sabbat ical, he or she m ay find t hat various classes in t he sim ulat ion package have evolved t o new versions. The java.io package provides som e support for deserializing an obj ect when it s class has changed since t he serializat ion was writ t en, but proper handling of obj ect serializat ion across class versions is, in pract ice, problem at ic. To avoid t his problem , you can " serialize" your obj ect t o t ext . One way t o provide persist ent st orage of an obj ect is t o writ e out a t ext ual descript ion of it . You can save an obj ect 's dat a as an XML file or in a propriet ary language of your own design. When you read in such a file, it is com parat ively easy t o handle m issing dat a or changes in class design t hat can occur when a class version changes while t he dat a is in st orage.
CHALLENGE 19.5
Writ e down at least one disadvant age of st oring an obj ect as t ext .
Design Pat t erns ( Gam m a et al. 1995) st at es t he int ent of t he MEMENTO pat t ern as, " Wit hout violat ing encapsulat ion, capt ure and ext ernalize an obj ect 's int ernal st at e so t hat t he obj ect can be rest ored t o t his st at e lat er" ( p. 283) . You m ight argue t hat t his int ent addresses online st orage only: specifically, t he st orage of an obj ect 's dat a in anot her obj ect . I f you widen t he m eaning of m em ent o t o include offline st orage, arguably every dat abase row and every XML st ring t hat cont ains an obj ect 's st at e is a m em ent o. Possibly for t hese reasons, Design Pat t erns does not provide any offline st orage exam ples of MEMENTO. On t he ot her hand, MEMENTO and persist ent st orage bot h capt ure and ext ernalize an obj ect 's int ernal st at e. An XML st ring m ay lie som ewhere bet ween an online m em ent o and a dat abase, placing an obj ect 's st at e in a m achine- readable and hum an- readable form . I f you say you creat e m em ent os by st oring an obj ect 's dat a in an XML st ring, I will underst and what you m ean. And t hat 's t he point of design pat t erns: By using a com m on vocabulary, we can readily discuss design concept s and t heir applicat ion.
Summary
The MEMENTO pat t ern let s you capt ure an obj ect 's st at e so you can rest ore t he obj ect lat er. The m eans of st orage m ay depend on whet her you need t o be able t o rest ore an obj ect aft er a few clicks and keyst rokes or aft er days or years. The m ost com m on reason for saving and rest oring obj ect s during an applicat ion session is t he support of undo operat ions. I n such a case, you can st ore an obj ect 's st at e in anot her obj ect , possibly a clone. To let an obj ect persist across sessions, you can save t he m em ent o eit her t hrough obj ect serializat ion or as t ext t hat you will parse t o rest ore t he obj ect .
Part IV: Operation Patterns Chapter 20. Introducing Operations Chapter 21. Template Method Chapter 22. State Chapter 23. Strategy Chapter 24. Command Chapter 25. Interpreter
Chapter 20. Introducing Operations When you writ e a Java m et hod, you produce a fundam ent al unit of work t hat is a level up from writ ing a st at em ent . Your m et hods have t o part icipat e in an overall design, archit ect ure, and t est plan, but no act ivit y is m ore cent ral t o program m ing t han writ ing m et hods. I ronically, despit e t he cent ral role of m et hods, it is easy t o get confused about what m et hods are and how t hey funct ion. You need t o be aware of a few synt act ic subt let ies in Java m et hods. But m ore confusing is t he t endency of m any developers and aut hors t o slosh t oget her t he m eaning of t he words m et hod, operat ion, and algorit hm . By dist inguishing t he m eanings of t hese t erm s, you can express im port ant concept s t hat surface in m any design pat t erns. Having a clear underst anding of what m et hods are will also help you underst and several design pat t erns. I n part icular, STATE, STRATEGY, and I NTERPRETER all work by im plem ent ing an operat ion in m et hods across several classes, but such observat ions are useful only if we agree on t he m eaning of m et hod and operat ion.
Operations, Methods, and Algorithms I t is useful t o dist inguish operat ion from m et hod and t o t hen discuss t hese concept s' relat ions t o algorit hm . Fort unat ely, t he UML defines t he difference bet ween an operat ion and m et hod ( Booch, Rum baugh, and Jacobson 1999, p. 128) . • •
An operation is a specification of a service that can be requested from an instance of a class. A method is an implementation of an operation.[1] [1]
For definitions of the related terms abstract method and static method, see the Glossary.
An operat ion specifies som et hing t hat a class does and specifies t he int erface for calling on t his service. Mult iple classes m ay im plem ent t he sam e operat ion in different ways. For exam ple, m any classes im plem ent t he toString() operat ion. Every class t hat im plem ent s an operat ion im plem ent s it wit h a m et hod, t he code t hat m akes t he operat ion work for t hat class. The definit ions of m et hod and operat ion help t o clarify t he st ruct ure of m any design pat t erns. The m eaning of operat ion is a level of abst ract ion up from t he idea of m et hod. Because design pat t erns are also a level up from classes and m et hods, it is no surprise t hat operat ions feat ure prom inent ly in m any pat t erns. For exam ple, COMPOSI TE arranges for operat ions t o apply t o bot h it em s and groups. PROXY let s an int erm ediary t hat has t he sam e operat ions as a t arget obj ect int erpose it self t o m anage access t o t he t arget .
CHALLENGE 20.1
Explain how CHAI N OF RESPONSI BI LI TY im plem ent s an operat ion.
I f we agree on t he UML definit ions of t hese words, it is illum inat ing t o say t hat a m et hod is an im plem ent at ion of an operat ion. How, t hen, do operat ions and m et hods com e t oget her t o im plem ent an algorit hm ? Before answering t hat , we should agree on what algorit hm m eans. I nt roduct ion t o Algorit hm s ( Corm en, Leiserson, and Rivest 1990) says: " An algorit hm is any well- defined com put at ional procedure t hat t akes som e value, or set of values, as input and produces som e value, or set of values, as out put " ( p. 1) . An algorit hm is a procedure—a sequence of inst ruct ions—t hat accept s input s and produces out put . I n t his regard, an algorit hm is sim ilar t o a m et hod. A m et hod accept s input s—it s param et er list —and produces an out put —it s ret urn value. However, m any algorit hm s require m ore t han one m et hod t o execut e in an obj ect - orient ed program . For exam ple, t he isTree() algorit hm in Chapt er 5, Com posit e, requires four m et hods, as Figure 20.1 shows. Figure 20.1. Four isTree() methods collaborate to effect the algorithm for determining whether an instance of MachineComponent is a tree.
CHALLENGE 20.2
How m any algorit hm s, operat ions, and m et hods does Figure 20.1 depict ?
Algorit hm s get som et hing done. They m ay appear as part of a m et hod, or t hey m ay involve m any m et hods. Algorit hm s t hat require m ore t han one m et hod oft en rely on polym orphism t o allow m ult iple im plem ent at ions of a single operat ion. Polym orphism is t he principle t hat m et hod invocat ion depends on bot h t he operat ion invoked and t he class of t he invocat ion receiver. For exam ple, you m ight ask which m et hod execut es when Java encount ers t he expression m.isTree(). The answer is, it depends. I f m is an inst ance of Machine, Java will invoke Machine.isTree(). I f m is an inst ance of MachineComposite, Java will invoke MachineComposite.isTree(). I nform ally, polym orphism m eans t hat t he right m et hod get s invoked for t he right t ype of obj ect . Many pat t erns use polym orphism , which in som e cases t ies direct ly t o t he int ent of t he pat t ern. But before we invest igat e t hose pat t erns, it is a good idea t o have a solid underst anding of t he m echanics of Java m et hods.
The Mechanics of Methods When you writ e a m et hod, it becom es available for invocat ion according t o it s signa t ur e . The Java™Language Specificat ion ( Gosling et al. 2000) says: " The signat ure of a m et hod consist s of t he nam e of t he m et hod and t he num ber and t ypes of form al param et ers t o t he m et hod" ( p. 169) . Not e t hat a m et hod's signat ure does not include ret urn t ype. However, if a m et hod declarat ion overrides t he declarat ion of anot her m et hod, a com pile- t im e error occurs if t hey have different ret urn t ypes.
CHALLENGE 20.3
At Oozinoz, t he m et hod MachineSimulator.clone() ret urns Object, even t hough t he m et hod always ret urns an inst ance of MachineSimulator. Will t his m et hod be m ore effect ive if you change it t o ret urn MachineSimulator?
A signat ure specifies which m et hod is invoked when a client m akes a call. So how is signat ure different from operat ion? Aft er all, bot h t erm s refer t o t he specificat ion of a service. The t erm s differ m ainly in t he cont ext in which t hey are t ypically used. The t erm operat ion applies when discussing t he idea t hat m et hods in different classes m ay have t he sam e int erface. The t erm signat ure applies when discussing t he rules t hat govern how Java m at ches a m et hod call t o a m et hod in t he receiving obj ect . I n Java, a m et hod declarat ion includes a header and a body. A m et hod's body is t he series of inst ruct ions t hat can be called int o act ion by invoking t he m et hod's signat ure. A m et hod's header includes t he m et hod's ret urn t ype and signat ure and m ay include m odifiers and a throws clause. The form of a m et hod header is:
modi f i er s t ype si gnat ur e t hr ows- cl ause Each of t he four aspect s of a m et hod's header cont ains subt let ies and challenges. Met hod m odifiers include visibilit y m odifiers: abstract, static, final, synchronized, and native. Several earlier chapt ers have discussed visibilit y and t he abstract m odifier.
The rem aining m odifiers are all im port ant , of course, but only static appears frequent ly when im plem ent ing t he pat t erns t his book describes. A st at ic m et hod is invoked wit hout reference t o a part icular obj ect . This can lead t o surprising result s. Consider t he classes Firework and Rocket in Figure 20.2. Each class defines a st at ic flies() m et hod. Most fireworks don't fly, so Firework defines t his t o ret urn false, whereas Rocket defines it t o ret urn true. Figure 20.2. The classes Firework and Rocket each have a static method with the same signature and same return value.
CHALLENGE 20.4
What does t he following program print out ?
package com. oozi noz. appl i cat i ons; i mpor t com. oozi noz. uni t s. *; i mpor t com. oozi noz. f i r ewor ks. *; publ i c cl ass ShowSt at i c i mpl ement s Uni t Const ant s { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Lengt h apogee = ( Lengt h) METER. t i mes( 75) ; Fi r ewor k r = new Rocket ( "Byebaby", 13. 95, apogee) ; Syst em. out . pr i nt l n( r . f l i es( ) ) ; } } I n addit ion t o m odifiers, ret urn t ype, and signat ure, a m et hod header m ay include a throws clause. This clause lim it s som e of t he except ions t hat m ay occur during t he m et hod's execut ion.
Exceptions in Methods A m et hod header m ay declare t hat t he m et hod t hrows except ions. An except ion in Java is an inst ance of any subclass of Throwable, including t he Exception class and t he Error class. Not e t hat t he word except ion refers t o an inst ance of Throwable but not necessarily t o an inst ance of Exception. Figure 20.3 shows part of t he Throwable hierarchy.
Figure 20.3. Of the classes shown, only Throwable, Exception, and IOException must be handled.
Except ions are norm ally checked, m eaning t hey m ust be declared by a m et hod t hat m ight t hrow t hem . The Error class and it s subclasses and t he RuntimeException class and it s subclasses are unchecked, m eaning t hat t hey need not be declared. The Java™Language Specificat ion ( Gosling et al. 2000) says: Error
and its subclasses are exempted from compile-time checking because they can occur at many points in the program and recovery from them is difficult or impossible. (p. 221)
and RuntimeException
and its subclasses are exempted from compiletime checking because, in the judgment of the designers of the Java programming language, having to declare such exceptions would not aid significantly in establishing the correctness of programs. (p. 222)
The exist ence of unchecked except ions m eans t hat m et hods m ay t hrow except ions t hat you don't have t o declare. For exam ple, you can perform cast s wit hout handling ClassCastException in eit her a try/catch st at em ent or in t he signat ure of t he m et hod.
CHALLENGE 20.5
The designers of t he Java program m ing language creat ed a dist inct ion bet ween checked and unchecked except ions. Explain w hy you t hink t his was a good idea or a bad idea.
Summary Alt hough it is com m on t o int erm ingle t he m eanings of operat ion, m et hod, signat ure, and algorit hm , preserving dist inct ions am ong t hese t erm s helps t o describe im port ant
concept s. An operat ion is, like a signat ure, a specificat ion of a service. The word operat ion applies when t alking about t he idea t hat m any m et hods m ay have t he sam e int erface. The word signat ure applies when discussing m et hod lookup rules. A m et hod definit ion in- cludes it s signat ure—it s nam e and param et er list —along wit h m odifiers, ret urn t ype, a throws clause, and t he m et hod's body. A m et hod has a signat ure and im plem ent s an operat ion. An algorit hm is a procedure t hat accept s input s and produces out put s. Met hods accept input s, produce out put s, and cont ain a procedural m et hod body, so it is com m on t o refer t o a m et hod as an algorit hm . However, an algorit hm 's procedure m ay involve m any operat ions and m et hods, or it m ay exist as part of anot her m et hod. The word algorit hm best applies when you are discussing a procedure t hat produces a result . Many design pat t erns involve dist ribut ing an operat ion across several classes. You can also say t hat t hese pat t erns rely on polym orphism , t he principle t hat m et hod select ion depends on t he class of t he obj ect t hat receives a m et hod call. The decision of which m et hod execut es depends on m any rules and various aspect s of m et hod headers. I n part icular, it is im port ant t o underst and t hat t he static m odifier m eans t hat t he decision of which m et hod t o execut e depends on t he receiver's declared t ype, not t he class of t he obj ect . Met hods execut e unt il t hey ret urn, unless t hey produce an except ion. The pot ent ial for som e except ions is so com m onplace t hat m et hods need not declare t hem . Ot her, checked except ions m ust appear in a m et hod's throws clause. The decision of whet her an except ion should be checked or unchecked is a m at t er of j udgm ent .
Beyond Ordinary Operations Different classes can im plem ent an operat ion in different ways. I n ot her words, Java support s polym orphism . The power of t his seem ingly sim ple idea appears in several design pat t erns.
If you intend to •
• • • •
Implement an algorithm in a method, deferring the definition of some steps of the algorithm so that subclasses can redefine them
Apply the pattern Tem plat e Met hod ( Chapt er 21) St at e ( Chapt er 22)
Distribute an operation so that each class represents St rat egy ( Chapt er a different state 23)
Encapsulate an operation, making implementations interchangeable
Com m and ( Chapt er 24)
Encapsulate a method call in an object
I nt erpret er ( Chapt er 25)
Distribute an operation so that each implementation applies to a different type of composition
Operat ion- orient ed pat t erns address cont ext s in which you need m ore t han one m et hod, usually wit h t he sam e signat ure, t o part icipat e in a design. For exam ple, t he TEMPLATE METHOD pat t ern allows subclasses t o im plem ent m et hods t hat adj ust t he effect of a procedure defined in a superclass.
Chapter 21. Template Method
I n a sense, alm ost every m et hod is a t em plat e. Ordinary m et hods have bodies t hat define a sequence of inst ruct ions. I t is also quit e ordinary for a m et hod t o invoke m et hods on t he current obj ect and on ot her obj ect s. Ordinary m et hods are, in t his sense, " t em plat es" t hat m erely out line a series of inst ruct ions for t he com put er t o follow. The TEMPLATE METHOD pat t ern, however, involves a m ore specific t ype of t em plat e. When you writ e a m et hod, you m ay want t o define t he out line of an algorit hm , underst anding t hat t here m ay be differences in how you want t o im plem ent cert ain st eps. I n t his case, you can define t he m et hod but leave som e st eps as abst ract m et hods, as st ubbed- out m et hods, or as m et hods defined in a separat e int erface. This produces a m ore rigid " t em plat e" t hat specifically defines which st eps of an algorit hm ot her classes can or m ust supply. The int ent of TEMPLATE METHOD is t o im plem ent an algorit hm in a m et hod, deferring t he definit ion of som e st eps of t he algorit hm so t hat ot her classes can redefine t hem .
A Classic Example of Template Method: Sorting An ancient exam ple of TEMPLATE METHOD t hat is not only pre- Java but also prehist oric occurs in sort ing. [ 1] Sort ing has various algorit hm s, but in m ost set t ings, you can est ablish a single algorit hm and use it t o sort various collect ions of obj ect s. Any algorit hm t hat you use for sort ing will rely on t he prim it ive st ep of com paring t wo it em s, or at t ribut es. I f you can com pare, say, t he sharpness of t wo arrowheads, your sort ing algorit hm will let you sort t he arrowheads in a collect ion. [1]
Some developers think of sorting as an example of STRATEGY. Challenge 23.6 on page 247 will ask you to compare STRATEGY and TEMPLATE METHOD. I n recent t im es, sort ing probably reigns as t he m ost frequent ly reim plem ent ed algorit hm , wit h im plem ent at ions out num bering t he num ber of exist ing com put er program m ers. But unless you are sort ing a huge collect ion, you need not w rit e your own sort ing algorit hm . The Collections class in java.util, shown in Figure 21.1, provides a sort ing algorit hm as a TEMPLATE METHOD, deferring t he com parison st ep t o you. Figure 21.1. The sort() method can sort any list, using a method that you supply to perform the comparison step of the algorithm.
The st at ic sort() m et hod accept s a list and an inst ance of Comparator. The Comparator int erface requires it s im plem ent er t o supply t he com parison st ep of t he sort ing algorit hm . The compare() m et hod m ust ret urn a num ber less t han, equal t o, or great er t han 0. These values correspond t o t he idea t hat , in a sense t hat you define, obj ect o1 is less t han, equal t o, or great er t han obj ect o2. For exam ple, you m ight im plem ent compare() t o com pare t he sharpness of t wo rocks or t he apogee of t wo rocket s. The following class sort s a collect ion of rocket s by t heir apogees:
package com. oozi noz. appl i cat i ons; i mpor t j ava. ut i l . *; i mpor t com. oozi noz. uni t s. *; i mpor t com. oozi noz. f i r ewor ks. *; publ i c cl ass ShowCompar at or i mpl ement s Uni t Const ant s { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Rocket r 1 = new Rocket ( "Mach- i t ", 22. 95, ( Lengt h) METER. t i mes( 1000) ) ; Rocket r 2 = new Rocket ( "Pocket ", 2. 95, ( Lengt h) METER. t i mes( 12) ) ; Rocket r 3 = new Rocket ( "Sock- i t ", 9. 95, ( Lengt h) METER. t i mes( 100) ) ; Rocket r 4 = new Rocket (
"Spr ocket ", 3. 95, ( Lengt h) METER. t i mes( 50) ) ; Li st r ocket s = Ar r ays. asLi st ( new Rocket [ ] { r 1, r 2, r 3, r 4 } ) ; Compar at or c = new Compar at or ( ) { publ i c i nt compar e( Obj ect o1, Obj ect o2) { / * ? */ } }; Col l ect i ons. sor t ( r ocket s, c) ; I t er at or i = r ocket s. i t er at or ( ) ; whi l e ( i . hasNext ( ) ) { Syst em. out . pr i nt l n( i . next ( ) ) ; } } } The program print out depends on how Rocket im plem ent s toString() but shows t he rocket s sort ed by apogee:
Pocket $2. 95 ( 12. 0 met er s) Spr ocket $3. 95 ( 50. 0 met er s) Sock- i t $9. 95 ( 100. 0 met er s) Mach- i t $22. 95 ( 1000. 0 met er s)
CHALLENGE 21.1
The Rocket class supplies a getApogee() m et hod t hat ret urns t he height a rocket can reach. This height is of t ype Length, a m easure whose m agnit ude you can fet ch wit h a call t o getMagnitude(). Fill in t he compare() m et hod in t he ShowComparator program t o sort a collect ion of rocket s by apogee.
The sort() m et hod and t he Comparator int erface let you supply a dom ain- specific st ep t o an ancient algorit hm . I t can also happen t hat t he ent ire algorit hm is dom ain specific.
Completing an Algorithm As in t he ADAPTER pat t ern, a t hought ful developer m ay foresee t hat you have a role in com plet ing his or her design. I n ADAPTER, you supply an obj ect t hat responds t o t he needs of t he ot her developer's code. I n TEMPLATE METHOD, you supply a st ep of an algorit hm . Consider t he Ast er st ar press t hat Figure 21.2 shows. A st ar press from Ast er Corporat ion accept s em pt y m et al m olds and presses fireworks st ars int o t hem . The m achine has hoppers, not shown in t he diagram , t hat dispense t he chem icals t hat t he m achine m ixes int o a past e and presses int o t he m olds. When t he m achine shut s down, it st ops working
on t he m old in t he processing area, and ushers any m olds on it s input conveyor t hrough t he processing area t o t he out put , wit hout processing t he m olds. Then t he m achine discharges it s current bat ch of past e and flushes it s processing area wit h wat er. The m achine orchest rat es all of t his act ivit y by using an on- board com put er and t he AsterStarPress class shown in Figure 21.3. Figure 21.2. An Aster star press comes with input and output conveyors that move star press molds. Oozinoz adds a recovery conveyor that saves discarded star press paste.
Figure 21.3. Star presses from Aster Corporation come with an abstract class that you must subclass to work at Oozinoz.
The Ast er st ar press is sm art and independent , and it is also aware t hat it m ay be running in a sm art fact ory w it h w hich it m ust com m unicat e. For exam ple, t he shutDown() m et hod inform s t he fact ory if t he m old it was processing is left incom plet e:
publ i c voi d shut Down( ) { i f ( i nPr ocess( ) ) { st opPr ocessi ng( ) ; mar kMol dI ncompl et e( cur r ent Mol dI D) ; } usher I nput Mol ds( ) ; di schar gePast e( ) ; f l ush( ) ; } The markMoldIncomplete() m et hod and t he AsterStarPress class are abst ract . At Oozinoz, you creat e a subclass t hat im plem ent s t he required m et hod and download t his code t o t he st ar press com put er. You can im plem ent markMoldIncomplete() by passing t he inform at ion about t he incom plet e m old t o t he MaterialManager singlet on t hat t racks m at erial st at us.
CHALLENGE 21.2
Writ e t he code for t he markMoldIncomplete() m et hod of OzAsterStarPress.
package com. oozi noz. ast er . cl i ent ; i mpor t com. oozi noz. ast er . *; publ i c cl ass OzAst er St ar Pr ess ext ends
publ i c cl ass OzAst er St ar Pr ess ext ends Ast er St ar Pr ess { publ i c Mat er i al Manager get Manager ( ) { r et ur n Mat er i al Manager . get Manager ( ) ; } publ i c voi d mar kMol dI ncompl et e( i nt i d) { / * ? */ } } The Ast er st ar press developers are well aware of how fireworks fact ories work and have done a good j ob of com m unicat ing wit h t he fact ory at t he right processing point s. Nonet heless, you m ay need t o est ablish com m unicat ion at a point t hat t he Ast er developers have not foreseen.
Template Method Hooks A hook is a m et hod call t hat a developer places in his or her code t o give ot her developers a chance t o insert code at a specific spot in a procedure. When you are adapt ing anot her developer's code and you need cont rol at a point where you don't current ly have it , you can request a hook. An obliging developer can add a m et hod call at t he point t hat you need it . The developer will also usually supply a st ubbed- out version of t he hook m et hod so t hat ot her client s need not necessarily override t he hook m et hod. Consider t he Ast er st ar press t hat discharges it s chem ical past e and flushes it self wit h wat er when shut t ing down. The press has t o discharge t his past e t o keep it from drying and clogging t he m achine. At Oozinoz, you recover t his past e so t hat you can dice it for use as t iny st ars in Rom an candles. ( A Rom an candle is a st at ionary t ube t hat cont ains a m ixt ure of explosive charges, sparks, and st ars.) Aft er t he st ar press discharges t he past e, you arrange for a robot t o m ove t he past e t o a separat e conveyor, as Figure 21.2 shows. I t is crit ical t hat you rem ove t he past e before t he m achine flushes it s processing area wit h wat er, which would ruin t he past e m ixt ure. The problem is t hat you want t o gain cont rol bet ween t he t wo st at em ent s:
di schar gePast e( ) ; f l ush( ) ; You m ight override dischargePaste() wit h a m et hod t hat adds a call t o collect t he past e:
publ i c voi d di schar gePast e( ) { super . di schar gePast e( ) ; get Fact or y( ) . col l ect Past e( t hi s) ; } This m et hod uses a Factory singlet on t o collect discarded past e from a st ar press. This is dangerous code, t hough, because past e collect ion is a surprising side effect . Developers at Ast er will cert ainly be unaware t hat you're defining dischargePaste() in t his way. I f t hey m odify t heir code t o discharge past e at a t im e t hat you don't want t o collect it , an error will occur.
Developers usually st rive t o solve problem s by writ ing code. But here t he challenge is t o solve a problem by com m unicat ing wit h ot her developers.
CHALLENGE 21.3
Writ e a not e t o t he developers at Ast er, asking for a change t hat will let you safely collect discarded st ar past e before t he m achine flushes it s processing area.
The st ep t hat a subclass supplies in TEMPLATE METHOD m ay be required t o com plet e t he algorit hm , or it m ay be an opt ional st ep t hat hooks in a subclass's code, oft en at anot her developer's request . Alt hough t he int ent of t he pat t ern is t o let a separat e class define part of an algorit hm , you can also apply TEMPLATE METHOD when you refact or an algorit hm t hat appears in m ult iple m et hods.
Refactoring to Template Method When you apply TEMPLATE METHOD, classes in a hierarchy share t he out line of an algorit hm t hat you or som eone else codes in an abst ract superclass. The opport unit y for TEMPLATE METHOD m ay also crop up when you im plem ent sim ilar algorit hm s in m ore t han one m et hod. Consider t he Machine and MachinePlanner parallel hierarchy t hat you worked wit h in Chapt er 16, Fact ory Met hod. As Figure 21.4 shows, t he Machine class provides a createPlanner() m et hod as a FACTORY METHOD t hat ret urns an appropriat e subclass of MachinePlanner. Figure 21.4. A Machine object can create an appropriate MachinePlanner instance for itself.
Suppose t hat you not ice t hat subclasses of Machine include sim ilar t echniques for lazyinit ializing a planner. The ShellAssembler class has a ShellPlanner at t ribut e t hat it calls planner and t hat ShellAssembler init ializes in it s getPlanner() m et hod:
publ i c Machi nePl anner get Pl anner ( ) { i f ( pl anner == nul l ) { pl anner = new Shel l Pl anner ( t hi s) ; } r et ur n pl anner ;
} The StarPress class also has a planner at t ribut e but declares it t o be of t ype StarPressPlanner. The getPlanner() m et hod of StarPress also lazy- init ializes t he planner at t ribut e:
publ i c Machi nePl anner get Pl anner ( ) { i f ( pl anner == nul l ) { pl anner = new St ar Pr essPl anner ( t hi s) ; } r et ur n pl anner ; } The ot her subclasses of Machine have sim ilar approaches t o creat ing a planner only when it is first needed.
CHALLENGE 21.4
Suppose t hat you provide t he Machine class wit h a planner at t ribut e of t ype MachinePlanner and delet e t he exist ing getPlanner() m et hods in t he subclasses. Writ e t he code for getPlanner() in t he Machine class.
You can oft en refact or your code int o an inst ance of TEMPLATE METHOD. You do so by abst ract ing t he out line of t he sim ilar m et hods, m oving t he out line m et hod up t o a superclass, and let t ing subclasses supply j ust t he st ep where t hey differ in t heir im plem ent at ion of t he algorit hm .
Summary Ordinary m et hods are usually t em plat es in t hat you can't t ell exact ly what code will execut e j ust by looking at a single m et hod. The TEMPLATE METHOD pat t ern, however, does not m erely not e t hat ordinary m et hods are t em plat es. Rat her, t he int ent of TEMPLATE METHOD is t o define an algorit hm in a m et hod, leaving som e st eps abst ract , st ubbed out , or defined in an int erface so t hat ot her classes can fill t hem in. TEMPLATE METHOD oft en funct ions as a cont ract bet ween developers. One developer supplies t he out line of an algorit hm , and anot her developer supplies a cert ain st ep of t he algorit hm . This m ay be a st ep t hat let s t he algorit hm com plet e, or it m ay be a st ep t hat t he algorit hm developer includes t o hook in your code at specific point s in t he procedure. The int ent of TEMPLATE METHOD does not im ply t hat you will always writ e t he t em plat e m et hod in advance of defining subclasses. You m ay discover sim ilar m et hods in an exist ing hierarchy. I n t his case, you m ay be able t o dist ill t he out line of an algorit hm and m ove it up t o a superclass, applying TEMPLATE METHOD t o sim plify and t o organize your code.
Chapter 22. State The st a t e of an obj ect is a com binat ion of t he current values of it s at t ribut es. When you call a set- m et hod, you t ypically change an obj ect 's st at e, and an obj ect can change it s own st at e as it s m et hods execut e.
I n som e cases, an obj ect 's st at e can be a prom inent aspect of it s behavior, such as when m odeling t ransact ions and m achines. Logic t hat depends on t he obj ect 's st at e m ay spread t hrough m any of t he class's m et hods. To count er t his spread, you can m ove st at e- specific behavior int o a group of classes, wit h each class represent ing a different st at e. This let s you avoid having deep or com plex if st at em ent s, relying inst ead on polym orphism t o execut e t he right im plem ent at ion of an operat ion. The int ent of t he STATE pat t ern is t o dist ribut e st at e- specific logic across classes t hat represent an obj ect 's st at e.
Modeling States When you m odel an obj ect whose st at e is im port ant , you m ay find t hat you have a variable t hat t racks how t he obj ect should behave, depending on it s st at e. This variable m ay appear in com plex, cascading if st at em ent s t hat focus on how t o react t o t he event s t hat an obj ect can experience. One problem wit h t his approach t o m odeling st at e is t hat if st at em ent s can becom e com plex. Anot her problem is t hat when you adj ust how you m odel t he st at e, you oft en have t o adj ust if st at em ent s in several m et hods. The STATE pat t ern offers a cleaner, sim pler approach, using a dist ribut ed operat ion. Consider t he Oozinoz soft ware t hat m odels t he st at e of a carousel door. A ca r ou se l is a large, sm art rack t hat accept s m at erial t hrough a doorway and st ores t he m at erial according t o a bar code I D on t he m at erial. The door operat es wit h a single but t on. I f t he door is closed, clicking t he but t on m akes t he door st art opening. I f you click again before t he door opens fully, t he door will begin closing. I f you let t he door open all t he way, it will aut om at ically begin closing aft er a 2- second t im eout . You can prevent t his by clicking again when t he door is open. Figure 22.1 shows t he st at es and t ransit ions of t he carousel's door. Figure 22.1. A carousel door provides one-touch control with a single button that changes the door's state.
The diagram in Figure 22.1 is a UML st at e m achine. Such diagram s can be m uch m ore inform at ive t han a corresponding t ext ual descript ion.
CHALLENGE 22.1
Suppose t hat you open t he door and place a m at erial bin in t he doorway. I s t here a way t o m ake t he door begin closing wit hout wait ing for
m ake t he door begin closing wit hout wait ing for it t o t im e out ?
You can supply a Door_1 obj ect t hat t he carousel soft ware will updat e wit h st at e changes in t he carousel. ( The underscore in t he class nam e is a hint t hat we will soon refact or t his class.) Figure 22.2 shows t he Door_1 class. Figure 22.2. The Door_1 class models a carousel door, relying on state change events sent by the carousel machine.
The Door_1 class subclasses Observable so t hat client s, such as a GUI , can observe a door. The class definit ion declares it s superclass and est ablishes t he st at es t hat a door can ent er:
package com. oozi noz. car ousel ; publ i c cl ass Door _1 ext ends Obser vabl e { publ i c st at i c f i nal i nt CLOSED = publ i c st at i c f i nal i nt OPENI NG = publ i c st at i c f i nal i nt OPEN = publ i c st at i c f i nal i nt CLOSI NG = publ i c st at i c f i nal i nt STAYOPEN =
- 1; - 2; - 3; - 4; - 5;
pr i vat e i nt st at e = CLOSED; // } Not surprisingly, a t ext ual descript ion of t he st at e of a door depends on t he door's st at e:
publ i c St r i ng st at us( ) { swi t ch ( st at e)
{ case OPENI NG : r et ur n "Openi ng"; case OPEN : r et ur n "Open"; case CLOSI NG : r et ur n "Cl osi ng"; case STAYOPEN : r et ur n "St ayOpen"; def aul t : r et ur n "Cl osed"; } } When a user clicks t he carousel's one- t ouch but t on, t he carousel generat es a call t o a Door obj ect 's click() m et hod. The Door_1 code for a st at e t ransit ion m im ics t he inform at ion in Figure 22.1:
publ i c voi d cl i ck( ) { i f ( st at e == CLOSED) { set St at e( OPENI NG) ; } el se i f ( st at e == OPENI NG || st at e == STAYOPEN) { set St at e( CLOSI NG) ; } el se i f ( st at e == OPEN) { set St at e( STAYOPEN) ; } el se i f ( st at e == CLOSI NG) { set St at e( OPENI NG) ; } } The setState() m et hod of t he Door_1 class not ifies observers of t he door's change:
pr i vat e voi d set St at e( i nt st at e) { t hi s. st at e = st at e; set Changed( ) ; not i f yObser ver s( ) ; }
CHALLENGE 22.2
Refactoring to State
Writ e t he code for t he complete() and timeout() m et hods of t he Door_1 class.
The code for Door_1 is som ewhat com plex because t he use of t he state variable is spread t hroughout t he class. I n addit ion, you m ight find it difficult t o com pare t he st at e t ransit ion m et hods, part icularly click(), wit h t he st at e m achine in Figure 22.1. The STATE pat t ern can help you t o sim plify t his code. To apply STATE in t his exam ple, m ake each st at e of t he door a separat e class, as Figure 22.3 shows. Figure 22.3. This diagram shows a door's states as classes in an arrangement that mirrors the door's state machine.
The refact oring t hat Figure 22.3 shows uses t he Door_2 class t o cont ain t he cont ext of t he st at e m achine. A con t e x t is an obj ect t hat cont ains in form at ion t hat is environm ent al and relevant t o a group of ot her obj ect s. I n part icular, STATE uses a cont ext obj ect t o record which inst ance of DoorState is t he current st at e. The DoorState class const ruct or requires a Door_2 obj ect . Subclasses of DoorState use t his obj ect t o com m unicat e changes in st at e back t o t he door. By giving t hese classes an at t ribut e t hat t ies t hem t o a specific Door obj ect , t his design requires t hat a DoorState obj ect be referenced by a single Door obj ect . I n t urn, t his requires t he Door class t o define it s st at es as local variables:
package com. oozi noz. car ousel ; publ i c cl ass Door _2 ext ends Obser vabl e { publ i c f i nal Door St at e CLOSED = new Door Cl osed( t hi s) ;
publ i c f i nal Door St at e OPENI NG = publ i c f i nal Door St at e OPEN = publ i c f i nal Door St at e CLOSI NG = publ i c f i nal Door St at e STAYOPEN = // pr i vat e Door St at e st at e = CLOSED; // . . .
new Door Openi ng( t hi s) ; new Door Open( t hi s) ; new Door Cl osi ng( t hi s) ; new Door St ayOpen( t hi s) ;
} The abst ract DoorState class requires subclasses t o im plem ent click(). This is consist ent wit h t he st at e m achine, in which every st at e has a click() t ransit ion. The DoorState class st ubs out ot her t ransit ions, so t hat subclasses can override or ignore irrelevant m essages:
publ i c abst r act cl ass Door St at e { pr ot ect ed Door _2 door ; publ i c Door St at e( Door _2 door ) { t hi s. door = door ; } publ i c abst r act voi d cl i ck( ) ; publ i c voi d compl et e( ) { } publ i c St r i ng st at us( ) { St r i ng s = get Cl ass( ) . get Name( ) ; r et ur n s. subst r i ng( s. l ast I ndexOf ( ' . ' ) + 1) ; } publ i c voi d t i meout ( ) { } } Not e t hat t he status() m et hod works for all t he st at es and is m uch sim pler t han it s predecessor before refact oring.
CHALLENGE 22.3
The new status() m et hod ret urns a slight ly different descript ion of a door's st at e. What 's t he difference?
The new design doesn't change t he role of a Door obj ect in receiving st at e changes from t he carousel. But now t he Door_2 obj ect sim ply passes t hese changes t o it s current state obj ect :
package com. oozi noz. car ousel ;
publ i c cl ass Door _2 ext ends Obser vabl e { / / . . . ( Door St at e var i abl es) publ i c voi d cl i ck( ) { st at e. cl i ck( ) ; } publ i c voi d compl et e( ) { st at e. compl et e( ) ; } pr ot ect ed voi d set St at e( Door St at e st at e) { t hi s. st at e = st at e; set Changed( ) ; not i f yObser ver s( ) ; } publ i c St r i ng st at us( ) { r et ur n st at e. st at us( ) ; } publ i c voi d t i meout ( ) { st at e. t i meout ( ) ; } } The click(), complete(), status(), and timeout() m et hods show t he pure polym orphism of t his approach. Each of t hese m et hods is st ill a kind of swit ch. I n each case, t he operat ion is fixed, but t he class of t he receiver—t he class of state—varies. The rule of polym orphism is t hat t he m et hod t hat execut es depends on t he operat ion signat ure and t he class of t he receiver. What happens when you call click()? The answer depends on t he door's st at e. The code st ill effect ively perform s a swit ch, but by relying on polym orphism , t he code is sim pler t han before. The setState() m et hod in t he Door_2 class is now used by subclasses of DoorState. These subclasses closely resem ble t heir count erpart s in t he st at e m achine in Figure 22.1. For exam ple, t he code for DoorOpen handles calls t o click() and timeout(), t he t wo t ransit ions from t he Open st at e in t he st at e m achine:
package com. oozi noz. car ousel ; publ i c cl ass Door Open ext ends Door St at e { publ i c Door Open( Door _2 door ) { super ( door ) ; }
publ i c voi d cl i ck( ) { door . set St at e( door . STAYOPEN) ; } publ i c voi d t i meout ( ) { door . set St at e( door . CLOSI NG) ; } }
CHALLENGE 22.4
Writ e t he code for DoorClosing.java.
The new design leads t o m uch sim pler code, but you m ight feel a bit dissat isfied t hat t he " const ant s" t hat t he Door class uses are in fact local variables.
Making States Constant Suppose t hat you decide t o m ove t he st at e const ant s t o a DoorConstants int erface. For t his approach t o work, you have t o elim inat e t he Door inst ance variable from t he DoorState class. You can drop t his variable by changing t he click(), complete(), and timeout() t ransit ion m et hods in t he DoorState class t o require a Door obj ect as an input param et er. I n t his design, a Door obj ect t hat receives, for exam ple, a click() m et hod from t he carousel forwards t his call as state.click(this).
CHALLENGE 22.5
Com plet e t he class diagram in Figure 22.4 t o show a design t hat m oves t he door st at es t o an int erface. Figure 22.4. Complete this diagram to shows door states as constants defined by the DoorConstants interface.
Summary Generally speaking, t he st at e of an obj ect depends on t he collect ive value of t he obj ect 's inst ance variables. I n som e cases, an obj ect 's st at e can be a prom inent aspect of a class's behavior. This oft en occurs when an obj ect is m odeling a real- world ent it y whose st at e is im port ant . I n such a sit uat ion, com plex logic t hat depends on t he obj ect 's st at e m ay appear in m any m et hods. You can sim plify such code by m oving st at e- specific behavior t o a hierarchy of st at e obj ect s. This let s each st at e class cont ain t he behavior for one st at e in t he dom ain. I t also allows t he st at e classes t o correspond direct ly t o st at es in a st at e m achine. To handle t ransit ions bet ween st at es, you can let st at es ret ain a cont ext reference t hat cont ains a set of st at es. Alt ernat ively, you can pass around, in st at e t ransit ion calls, t he obj ect whose st at e is changing. Regardless of how you m anage st at e t ransit ions, t he STATE pat t ern sim plifies your code by dist ribut ing an operat ion across a collect ion of classes t hat represent an obj ect 's various st at es.
Chapter 23. Strategy A st rat egy is a plan, or approach, for achieving an aim , given cert ain input condit ions. A st rat egy is sim ilar t o an algorit hm : An algorit hm is a well- defined procedure t hat produces an out put from a set of input s. A st rat egy is a plan t hat pursues an out put given a set of input s. Usually, however, a st rat egy has m ore lat it ude in how t o pursue it s goal t han an algorit hm does. This lat it ude also m eans t hat st rat egies oft en appear in groups, or fam ilies, of alt ernat ives. When m ult iple st rat egies are available, t he logic t hat surrounds t he st rat egies m ust select one and t hen execut e it . Com plexit y in t he select ion
and use of st rat egies can lead t o com plex and t angled code. You can clean up such code wit h t he STRATEGY pat t ern. The int ent of STRATEGY is t o encapsulat e alt ernat ive st rat egies, or approaches, in separat e classes, each of which im plem ent s a com m on operat ion. The st rat egic operat ion defines t he input s and out put of a st rat egy but leaves im plem ent at ion up t o t he individual classes. Classes t hat im plem ent t he various approaches im plem ent t he sam e operat ion and are t hus int erchangeable, present ing t he sam e int erface t o client s.
Modeling Strategies Consider t he Oozinoz advert ising policy t hat suggest s a firework t o purchase when t he cust om er is visit ing t he Oozinoz Web sit e or calling int o t he call cent er. Oozinoz uses t wo com m ercial off- t he- shelf recom m endat ion engines t o help choose t he right firework t o offer a cust om er. The Customer class chooses and applies one of t hese engines t o decide on which firework t o recom m end t o a cust om er. One of t he recom m endat ion engines, t he Rel8 engine, suggest s a purchase based on t he cust om er's sim ilarit y t o ot her cust om ers. For t his recom m endat ion t o work, t he cust om er m ust have regist ered and given inform at ion about likes and dislikes about fireworks and ot her ent ert ainm ent s. I f t he cust om er has not regist ered, Oozinoz uses LikeMyStuff, anot her vendor's engine, t hat suggest s a purchase based on t he cust om er's recent purchases. I f dat a is insufficient for eit her engine t o funct ion, t he advert ising soft ware picks a firework at random . However, a special prom ot ion can override all t hese considerat ions t o prom ot e a specific firework t hat Oozinoz want s t o sell. Figure 23.1 shows t he classes t hat collaborat e t o suggest a firework t o a cust om er. Figure 23.1. The Customer class relies on other classes for its recommendations, including two off-the-shelf recommendation engines.
The LikeMyStuff and Rel8 engines bot h accept a Customer obj ect and suggest som et hing t o advert ise t o t he cust om er. Bot h engines are configured t o work for fireworks, alt hough LikeMyStuff requires a dat abase and Rel8 works ent irely from an
obj ect m odel. The code for getRecommended() in class Customer m irrors Oozinoz's advert ising policies:
publ i c Fi r ewor k get Recommended( ) { / / see i f we' r e pr omot i ng a par t i cul ar f i r ewor k t ry { Pr oper t i es p = new Pr oper t i es( ) ; p. l oad( Cl assLoader . get Syst emResour ceAsSt r eam( "conf i g/ st r at egy. dat ") ) ; St r i ng pr omot edFi r ewor kName = p. get Pr oper t y( "pr omot e") ;
i f ( pr omot edFi r ewor kName ! = nul l ) { Fi r ewor k f = Fi r ewor k. l ookup( pr omot edFi r ewor kName) ; i f ( f ! = nul l ) { r et ur n f ; } } } cat ch ( Except i on e) { } / / i f r egi st er ed, compar e t o ot her cust omer s i f ( i sRegi st er ed( ) ) { r et ur n ( Fi r ewor k) Rel 8. advi se( t hi s) ; } / / check spendi ng over t he l ast year Cal endar cal = Cal endar . get I nst ance( ) ; cal . add( Cal endar . YEAR, - 1) ; i f ( spendi ngSi nce( cal . get Ti me( ) ) > 1000) { r et ur n ( Fi r ewor k) Li keMySt uf f . suggest ( t hi s) ; } / / oh wel l ! r et ur n Fi r ewor k. get Random( ) ; } This code expect s t hat if a prom ot ion is on, it will be nam ed in a strategy.dat file in a config direct ory t hat lies in t he running program 's class pat h. Barring t his, t he code will use t he Rel8 engine if t he cust om er is regist ered or t he LikeMyStuff engine if t he cust om er has a known purchase record. I f t he cust om er is not regist ered and not a big
purchaser, t he code select s and recom m ends a firework at random . The code works, and you m ight feel t hat t his is not t he worst code you've ever seen. But we can m ake it bet t er.
Refactoring to Strategy The getRecommended() m et hod present s several problem s. First , it 's long—long enough t hat com m ent s have t o explain it s various part s. Short m et hods are easy t o underst and, seldom need explanat ion, and are usually preferable t o long m et hods. I n addit ion, t he getRecommended() m et hod chooses a st rat egy and t hen execut es it ; t hese are t wo different and separable funct ions. You can clean up t his code by applying STRATEGY. To do so, you need t o •
Create an interface that defines the strategic operation
•
Implement the interface with classes that represent each strategy
•
Refactor the code to select and to use an instance of the right strategic class
Suppose t hat you creat e an Advisor int erface and begin your refact oring by int roducing a GroupAdvisor class and an ItemAdvisor class. These classes can im plem ent a com m on recommend() operat ion by applying t he off- t he- shelf recom m endat ion engines, as Figure 23.2 shows. Figure 23.2. Implementations of the Advisor interface provide the strategic recommend() operation, relying on off-the-shelf engines.
To provide a st rat egy for a client , you can pass it a part icular im plem ent at ion of t he Advisor int erface. I nt erfaces can define only inst ance m et hods, so GroupAdvisor and ItemAdvisor m ust be inst ant iat ed t o support t he Advisor int erface. However, only one such obj ect is ever necessary, and so t hese classes define publicly accessible singlet ons. The advisor classes t ranslat e calls t o recommend() int o t he int erfaces t hat t he underlying engines require. For exam ple, t he GroupAdvisor class t ranslat es calls t o recommend() int o t he advise() int erface t hat t he Rel8 engine requires:
publ i c Fi r ewor k r ecommend( Cust omer c) { r et ur n ( Fi r ewor k) Rel 8. advi se( c) ; }
CHALLENGE 23.1
CHALLENGE 23.2
I n addit ion t o SI NGLETON and STRATEGY, what pat t ern appears in t he GroupAdvisor and ItemAdvisor classes?
Fill in t he class diagram in Figure 23.3, which shows t he recom m endat ion logic refact ored int o a collect ion of st rat egy classes. Figure 23.3. Complete this diagram to show a refactoring of the recommendation software, with strategies appearing as implementations of a common interface.
common interface.
Each im plem ent at ion of t he Advisor int erface should supply a singlet on t hat offers t he recommend() operat ion. You m ight correspondingly give each class a single, privat e const ruct or t o prevent ot her classes from inst ant iat ing it . The const ruct or for PromotionAdvisor should invest igat e whet her a prom ot ion is on. You m ight t hen supply t his class wit h a hasItem() m et hod t hat indicat es whet her t here is a prom ot ed it em .
package com. oozi noz. r ecommend; i mpor t j ava. i o. *; i mpor t j ava. ut i l . *; i mpor t com. oozi noz. f i r ewor ks. *; publ i c cl ass Pr omot i onAdvi sor i mpl ement s Advi sor { publ i c st at i c f i nal Pr omot i onAdvi sor si ngl et on = new Pr omot i onAdvi sor ( ) ; pr i vat e Fi r ewor k pr omot ed; pr i vat e Pr omot i onAdvi sor ( ) { t ry { Pr oper t i es p = new Pr oper t i es( ) ; p. l oad( Cl assLoader . get Syst emResour ceAsSt r eam( "conf i g/ st r at egy. dat ") ) ; St r i ng pr omot edFi r ewor kName = p. get Pr oper t y( "pr omot e") ; i f ( pr omot edFi r ewor kName ! = nul l ) { pr omot ed = Fi r ewor k. l ookup(
pr omot edFi r ewor kName) ; } } cat ch ( Except i on e) { } } publ i c bool ean hasI t em( ) { r et ur n pr omot ed ! = nul l ; } publ i c Fi r ewor k r ecommend( Cust omer c) { r et ur n pr omot ed; } } The RandomAdvisor class is sim ple:
package com. oozi noz. r ecommend; i mpor t com. oozi noz. f i r ewor ks. *; publ i c cl ass RandomAdvi sor i mpl ement s Advi sor { publ i c st at i c f i nal RandomAdvi sor si ngl et on = new RandomAdvi sor ( ) ; pr i vat e RandomAdvi sor ( ) { } publ i c Fi r ewor k r ecommend( Cust omer c) { r et ur n Fi r ewor k. get Random( ) ; } }
CHALLENGE 23.3
Writ e t he code for ItemAdvisor.java.
The refact oring of Customer separat es t he select ion of a st rat egy ( or " advisor" ) from t he use of t he st rat egy. An advisor at t ribut e of a Customer obj ect holds t he cont ext , or current choice, of t he st rat egy t o apply. The refact ored Customer class lazy- init ializes t his at t ribut e wit h logic t hat reflect s Oozinoz's advert ising policies:
pr i vat e Advi sor get Advi sor ( ) { i f ( advi sor == nul l ) { i f ( Pr omot i onAdvi sor . si ngl et on. hasI t em( ) ) { advi sor = Pr omot i onAdvi sor . si ngl et on; }
el se i f ( i sRegi st er ed( ) ) { advi sor = Gr oupAdvi sor . si ngl et on; } el se i f ( i sBi gSpender ( ) ) { advi sor = I t emAdvi sor . si ngl et on; } el se { advi sor = RandomAdvi sor . si ngl et on; } } r et ur n advi sor ; } pr i vat e bool ean i sBi gSpender ( ) { Cal endar cal = Cal endar . get I nst ance( ) ; cal . add( Cal endar . YEAR, - 1) ; r et ur n spendi ngSi nce( cal . get Ti me( ) ) > BI G_SPENDER_DOLLARS; }
CHALLENGE 23.4
CHALLENGE 23.5
Writ e t he code for Customer.getRecommended().
I n t he new design, four singlet ons im plem ent t he Advisor int erface in four ways, encapsulat ing four st rat egies. I s t he presence of m ult iple sim ilar singlet ons rem iniscent of a design pat t ern?
Comparing Strategy and State The refact ored code consist s alm ost ent irely of sim ple m et hods in sim ple classes. This is an advant age in it s own right and m akes adding new st rat egies easy. The refact oring relies prim arily on t he idea of dist ribut ing an operat ion across a relat ed group of classes. I n t his regard, STRATEGY is ident ical t o STATE. I n fact , m any developers wonder whet her t hese are really different pat t erns. On t he one hand, in t he real world, st rat egies ( such as recom m ending a firework) and st at es ( such as a carousel door wit h a one- t ouch cont rol but t on) are clearly different ideas. This real difference leads t o different problem s in m odeling st at es and st rat egies. For exam ple, t ransit ions are im port ant when m odeling st at es but usually irrelevant when choosing a st rat egy. Anot her difference is t hat STRATEGY m ight allow a client t o select or t o provide a st rat egy, an idea t hat rarely applies t o STATE. On t he ot her hand, t he differences in m odeling st at es and st rat egies m ay seem subt le. Cert ainly, t he reliance on polym orphism m akes STATE and STRATEGY appear alm ost ident ical st ruct urally. Eit her
way, if you decide t hat t hese are or are not t wo separat e pat t erns, you will be in good com pany.
Comparing Strategy and Template Method Chapt er 21, Tem plat e Met hod, described sort ing as an exam ple of t he TEMPLATE METHOD pat t ern. You can use t he sort() algorit hm from t he Collections class t o sort any list of obj ect s, so long as you supply a st ep t o com pare t wo obj ect s. But you m ight argue t hat when you supply a com parison st ep t o a sort ing algorit hm , you are changing t he st rat egy. For inst ance, if you are selling rocket s, present ing t hem sort ed by price is a different m arket ing st rat egy from present ing t hem sort ed by t hrust .
CHALLENGE 23.6
Provide an argum ent as t o whet her t he Collections.sort() m et hod provides an exam ple of TEMPLATE METHOD or an exam ple of STRATEGY.
Summary St rat egies t hat occur in t he real world m ay not nat urally lie down as separat e m et hods in a collect ion of classes. Logic t hat m odels alt ernat ive st rat egies m ay appear in a single class, oft en in a single m et hod. Such m et hods m ay be t oo com plicat ed and m ay m ix st rat egy- select ion logic wit h st rat egy execut ion. To sim plify such code, creat e a group of classes, one for each st rat egy. Define an operat ion and dist ribut e it across t hese classes. This let s each class encapsulat e one st rat egy, creat ing sim pler code. You also need t o arrange for t he client t hat uses a st rat egy t o be able t o select one. This select ion code m ay be com plex even aft er refact oring, but you should be able t o reduce t his code so t hat it is nearly equivalent t o pseudocode t hat describes st rat egy select ion in t he problem dom ain. Typically, a client will hold a select ed st rat egy in a cont ext variable. This m akes t he execut ion of a st rat egy a sim ple m at t er of forwarding t he st rat egic operat ion call t o t he cont ext , using polym orphism t o execut e t he right st rat egy. By encapsulat ing alt ernat ive st rat egies in separat e classes t hat each im plem ent a com m on operat ion, t he STRATEGY pat t ern let s you creat e clean, sim ple code t hat m odels a fam ily of approaches t o solving a problem .
Chapter 24. Command Polym orphism let s you encapsulat e a request , or a com m and as an obj ect : Est ablish t he signat ure of a m et hod t o call, and vary t he effect of calling t he m et hod by varying t he im plem ent at ion. The COMMAND pat t ern est ablishes a m et hod signat ure, m ost oft en execute() or perform(), and let s you define various im plem ent at ions of t his int erface. This let s you encapsulat e a request as an obj ect , so t hat you can param et erize client s wit h different request s, queue, t im e, or log request s, and require com panion operat ions, such as undo().
A Classic Example: Menu Commands Menus provide a classic exam ple of t he need for t he COMMAND pat t ern. When you add a m enu t o an applicat ion, you have t o configure t he m enu wit h words t hat describe act ions t hat t he user can choose, such as Save and Load. You also have t o configure t he m enu
so t hat it can t ake act ion, calling a m et hod in response t o a user's click. However, t he developers who wrot e t he code for t he JMenuItem class had no way of knowing what act ion you will want when an it em is select ed. How can you arrange for a class t o call a m et hod of yours when t he user clicks? The answer is t o use polym orphism : Make t he nam e of t he operat ion fixed, and let t he im plem ent at ion vary. For JMenuItem, t he operat ion is actionPerformed(). When t he user m akes a choice, a JMenuItem obj ect calls t he actionPerformed() m et hod of any obj ect t hat has regist ered as a list ener.
CHALLENGE 24.1
The m echanics of Java m enus m ake it easy t o apply t he COMMAND pat t ern, but t hey do not require t hat you organize your code as com m ands. I n fact , it is com m on t o develop an applicat ion in which a single obj ect list ens t o all t he event s in a GUI . What pat t ern does t hat follow?
When you develop a Swing applicat ion, you m ay want t o regist er a single list ener for all of an applicat ion's GUI event s, especially if t he GUI com ponent s int eract . For m enus, however, t his m ay not be t he best pat t ern t o follow. I f you use a single obj ect as a list ener, t hat obj ect has t o sort out which GUI obj ect generat ed an event . I f you have m any m enu it em s t hat t ake independent act ions, it m ay be bet t er t o apply COMMAND. The m et hod t hat a m enu it em calls when a user select s it is actionPerformed(), but you m ight t hink of it as execute(). When you creat e a JMenuItem obj ect , you can supply it wit h a com m and t o execut e when t he user select s t he it em . Your t ask is t o creat e an obj ect t hat im plem ent s actionPerformed() wit h a m et hod t hat t akes an act ion t hat corresponds t o t he user's com m and. Rat her t han define a new class t o im plem ent t his one lit t le behavior, it is oft en bet t er t o use an anonym ous class t o out fit m enu it em s wit h act ion com m ands. Consider t he Visualization2 class from t he com.oozinoz.visualization package. This class provides a m enu t hat let s t he user save and rest ore visualizat ions of a sim ulat ed Oozinoz fact ory. The applicat ion has a m enu bar t hat includes, at present , j ust a file m enu:
pr ot ect ed J MenuBar menuBar ( ) { i f ( menuBar == nul l ) { menuBar = new J MenuBar ( ) ; Menu f i l eMenu = new Menu( "Fi l e") ; menuBar . add( f i l eMenu( ) ) ; } r et ur n menuBar ; } The file m enu creat es Save and Load m enu it em s and regist ers list eners t hat wait for t he user t o choose t hem . The list eners are inst ances of anonym ous classes t hat im plem ent t he actionPerformed() m et hod by calling t he save() and load() m et hods of t he Visualization2 class:
pr ot ect ed J Menu f i l eMenu( ) { i f ( f i l eMenu == nul l )
{ f i l eMenu = new J Menu( "Fi l e") ; Font f = Swi ngFacade. get St andar dFont ( ) ; f i l eMenu. set Font ( f ) ; J MenuI t em save = new J MenuI t em( "Save") ; save. set Font ( f ) ; f i l eMenu. add( save) ; save. addAct i onLi st ener ( new Act i onLi st ener ( ) { / / chal l enge! } );
J MenuI t em l oad = new J MenuI t em( "Load") ; l oad. set Font ( f ) ; f i l eMenu. add( l oad) ; l oad. addAct i onLi st ener ( new Act i onLi st ener ( ) { / / chal l enge! } ); } r et ur n f i l eMenu; }
CHALLENGE 24.2
Fill in t he code for t he anonym ous subclasses of ActionListener, overriding actionPerformed(), not ing t hat t his m et hod expect s an ActionEvent argum ent .
When you out fit a m enu wit h com m ands, you are plugging your com m ands int o a cont ext provided by anot her developer. I n ot her cases of COMMAND, you will t ake t he role of t he cont ext developer, providing t he cont ext in which com m ands will execut e. For exam ple, you m ight want t o provide a t im ing service t hat records how long m et hods t ake t o execut e.
Using Command to Supply a Service Suppose t hat you want t o let developers t im e how long a m et hod t akes t o execut e. Figure 24.1 shows a Command int erface and a time() ut ilit y t hat t im es t he execut ion of a com m and. Figure 24.1. The time() method returns the number of milliseconds that a command takes to execute.
The code for time() capt ures t he syst em t im e before and aft er execut ing t he com m and and ret urns t he difference:
publ i c st at i c l ong t i me( Command c) { l ong t 1 = Syst em. cur r ent Ti meMi l l i s( ) ; c. execut e( ) ; l ong t 2 = Syst em. cur r ent Ti meMi l l i s( ) ; r et ur n t 2 - t 1; } Suppose t hat you decide t o t est t he time() m et hod wit h an aut om at ed t est ing t ool. One reasonable t est is t hat a com m and t hat sleeps for, say, 2,000 m illiseconds should t ake about 2,000 m illiseconds t o execut e:
package com. oozi noz. ut i l ; i mpor t j uni t . f r amewor k. *; publ i c cl ass Test Command ext ends Test Case { publ i c Test Command( St r i ng name) { super ( name) ; } publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { new j uni t . awt ui . Test Runner ( ) . st ar t ( new St r i ng[ ] { "com. oozi noz. ut i l . Test Command" } ) ; } publ i c voi d t est Sl eep( ) { Command doze = new Command( ) { publ i c voi d execut e( ) { t ry { Thr ead. sl eep( 2000) ;
} cat ch ( I nt er r upt edExcept i on i gnor e) { } } }; l ong t = / * ? */ asser t Equal s( 2000, t , 50) ; } } The assertEquals() m et hod checks t hat t he expect ed value, 2,000, is wit hin 50 m illiseconds of t he act ual value.
CHALLENGE 24.3
Com plet e t he assignm ent st at em ent t hat t im es t he execut ion of t he doze com m and.
Command in Relation to Other Patterns The COMMAND pat t ern has int erest ing relat ionships t o m any ot her pat t erns. For exam ple, COMMAND provides an alt ernat ive t o TEMPLATE METHOD. Recall t he Ast er st ar press code t hat let you override t he m et hod t hat m arks a m old as incom plet e if it is in process when you shut down t he press. The AsterStarPress class is abst ract , requiring you t o subclass it wit h a class t hat has a markMoldIncomplete() m et hod. The shutdown() m et hod of AsterStarPress relies on t his m et hod t o ensure t hat your dom ain obj ect knows t hat t he m old is incom plet e:
publ i c voi d shut Down( ) { i f ( i nPr ocess( ) ) { st opPr ocessi ng( ) ; mar kMol dI ncompl et e( cur r ent Mol dI D) ; } usher I nput Mol ds( ) ; di schar gePast e( ) ; f l ush( ) ; } You m ight find it inconvenient t o subclass AsterStarPress wit h a class t hat you have t o m ove t o t he st ar press's on- board com put er. Suppose t hat you ask t he developers at Ast er t o provide t he hook in a different way, using t he COMMAND pat t ern. Figure 24.2 shows a Hook int erface t hat t he AsterStarPress class can use, let t ing you param et erize t he st ar press code at runt im e. Figure 24.2. A class can provide a hook—a way to insert custom code—by calling a supplied object's execute() command at a specific point in a procedure.
The AsterStarPress class init ially set s it s hook t o be a NullHook obj ect . The execute() m et hod of NullHook does not hing. You can supply your own im plem ent at ion of Hook for t he shutDown() m et hod t o call. The shutDown() m et hod now relies on t his hook, execut ing it and passing it t he AsterStarPress obj ect at t he right m om ent :
publ i c voi d shut Down( ) { i f ( i nPr ocess( ) ) { st opPr ocessi ng( ) ; mar kMol dI ncompl et eHook. execut e( t hi s) ; } usher I nput Mol ds( ) ; di schar gePast e( ) ; f l ush( ) ; } Using t he previous version of t he AsterStarPress class, you creat ed a subclass t hat handled t he effect s of a shut down, leaving a m old incom plet e:
publ i c cl ass OzAst er St ar Pr ess ext ends Ast er St ar Pr ess { publ i c Mat er i al Manager get Manager ( ) { r et ur n Mat er i al Manager . get Manager ( ) ; } publ i c voi d mar kMol dI ncompl et e( i nt i d) { get Manager ( ) . set Mol dI ncompl et e( i d) ; } } You no longer need t he OzAsterStarPress class, but you do need t o set t he com m and hook in t he AsterStarPress class.
CHALLENGE 24.4
Writ e an expression t hat creat es an obj ect t hat you can pass t o setShutdownHook() and t hat will
24.4
you can pass t o setShutdownHook() and t hat will not ify t he Oozinoz m at erial m anager of t he m old's st at e.
The execute() com m and of Hook accept s an inst ance of AsterStarPress as an argum ent . The idea here is t hat t he developer of a hook—nam ely you—will need som e inform at ion about t hat st at e of t he press when your hook is called. I f t he m ark- m oldincom plet e hook were t he only hook for st ar press processing, it would m ake m ore sense t o use t he I D of t he m old m achine as t he param et er of t he execute() com m and. The chosen design, however, ant icipat es t hat ot her hooks will com e along. Passing t he com plet e AsterStarPress obj ect is a hedge t hat m akes it likely t hat fut ure hooks can be added wit hout changing t he Hook int erface. The COMMAND pat t ern affords an alt ernat ive design t o a TEMPLATE METHOD for hooks and is sim ilar in int ent , or st ruct ure, t o several ot her pat t erns. The COMMAND pat t ern is sim ilar t o ADAPTER, in t hat a class such as JMenuItem or JTable arranges t o call a client 's code at t he right m om ent . COMMAND is also sim ilar t o I NTERPRETER; Challenge 25.3 on page 268 w ill ask you t o com pare t hem . Finally, COMMAND is sim ilar t o a pat t ern in which a client knows when an act ion is required but doesn't know exact ly which operat ion t o call.
CHALLENGE 24.5
Which pat t ern addresses t he sit uat ion in which a client knows when t o creat e an obj ect but doesn't know which class t o inst ant iat e?
The COMMAND pat t ern oft en nam es it s st andard operat ion execute() or perform(). But not hing lim it s t he operat ion t o t his nam e; nor is t here a lim it at ion t hat t his pat t ern m ay use only a single operat ion. I n part icular, inst ances of COMMAND oft en include an undo() operat ion t o reverse t he effect s of an execute(). I n som e cases, you m ay be able t o im plem ent undo(), relying only on inform at ion in a com m and obj ect . More oft en, you will need t o rest ore a com plet e syst em t o t he st at e it was in before a com m and execut ed.
CHALLENGE 24.6
Which pat t ern provides for t he st orage and rest orat ion of an obj ect 's st at e?
Summary The COMMAND pat t ern let s you encapsulat e a request in an obj ect so t hat you can m odify at runt im e t he m et hod t hat a client will call. A classic exam ple of t he usefulness of COMMAND com es wit h m enus. Menus know when t o execut e an act ion but don't know which m et hod t o call. COMMAND let s you param et erize a m enu wit h t he m et hod calls t hat correspond t o m enu labels. When you use a Java m enu you can supply com m ands int o a cont ext t hat ot her developers have creat ed. I n ot her cases, you m ay t ake t he role of t he cont ext provider, developing an environm ent in which t o execut e com m ands. Your environm ent m ay allow for param et erizat ion of a service, as Java m enus do. You m ay also provide hooks, or add ext ra value, such as t im ing an operat ion. COMMAND fixes t he signat ure of an operat ion and let s classes vary, applying t his t rick of polym orphism t o encapsulat e an operat ion in an obj ect . Perhaps because t his idea is so
fundam ent al, COMMAND has int erest ing relat ionships t o m any ot her pat t erns. COMMAND can be an alt ernat ive t o MEDI ATOR or TEMPLATE METHOD and oft en int eract s wit h ot her pat t erns.
Chapter 25. Interpreter The int erpret er pat t ern, like t he STATE and STRATEGY pat t erns, dist ribut es an operat ion across a collect ion of classes. I n such pat t erns, t he effect of calling t he operat ion depends on t he class of t he obj ect t hat receives t he call. I n bot h STATE and STRATEGY, t he receiver of an operat ion call is a single obj ect . I NTERPRETER t akes t he sam e idea and applies it t o a com posit ion—in part icular, a r ich com posit e , or one wit h various ways of form ing groups. The I NTERPRETER pat t ern is sim ilar t o t he COMPOSI TE pat t ern, which defines a com m on int erface for individual it em s and groups of it em s. COMPOSI TE does not require various, int erest ing ways of form ing groups, alt hough it allows t his. For exam ple, t he ProcessComponent hierarchy in Chapt er 5, Com posit e, allows sequences and alt ernat ions of process flows. I n I nt erpret er, t he idea t hat t here are various t ypes of com posit ion is essent ial. The way a class com poses ot her com ponent s defines how an I NTERPRETER class will im plem ent , or int erpret , a dist ribut ed operat ion. Each com posit e class in an inst ance of I NTERPRETER m odels a rule for how com posit ion m ay occur. The int ent of t he I NTERPRETER pat t ern is t o let you com pose execut able obj ect s according t o a set of com posit ion rules t hat you define. An int e r pr e t e r obj ect conduct s t he execut ion, or evaluat ion of a collect ion of rules, let t ing you build expression evaluat ors and com m and languages.
An Interpreter Example The robot s t hat Oozinoz uses t o m ove m at erial along a processing line com e wit h an int erpret er t hat cont rols t he robot and t hat has lim it ed cont rol of m achines on t he line. The int erpret er com es as a hierarchy of classes t hat encapsulat e robot com m ands. At t he head of t he hierarchy is an abst ract Command class. Dist ribut ed across t he hierarchy is an execute() operat ion. The int erpret er perform s t he sam e role as an API ( applicat ion program m ing int erface) but let s you creat e new m achine program s at runt im e. Figure 25.1 shows t he com m ands t hat t he robot int erpret er support s. Figure 25.1. An interpreter hierarchy provides for runtime programming of a factory robot.
The int erpret er hierarchy includes com m ands t hat let you st art up, shut down, and unload m achines. ( These com m ands require a bit of cust om izat ion t o work wit h our m achinery.) The int erpret er hierarchy also provides a com m and t hat carries m at erial from one m achine t o anot her and provides sequence, for, and if com m ands t hat are com posit ions of ot her com m ands. The ForMachines class uses anot her com m and as t he body of a for loop. This com m and can be eit her a single com m and or a sequence em bodied in a CommandSequence obj ect . The IfCommand class const ruct or t akes a condit ion t erm , not shown, and t wo com m ands, one of which it execut es, depending on t he condit ion. The NullCommand class im plem ent s execute() t o do not hing. You can use t his, for exam ple, t o nullify t he else part of an if com m and. Each class in t he Command hierarchy is an int erpret er. Polym orphism let s each class int erpret t he execute() operat ion different ly, in a way t hat m at ches t he nam e of t he class. You m ight also say t hat each inst ance of a class in t he hierarchy is an int erpret er. The CommandSequence, ForMachines, and IfCommand classes let you com pose new, arbit rarily com plex int erpret ers t hat can int erpret t he execute() com m and as a com plet e program . The Command int erpret er design uses a Term hierarchy t o cont ain references t o m achines, let t ing you creat e com m ands eit her for specific m achines or by using variables. For exam ple, t he CarryCommand class has a const ruct or t hat requires t wo Term obj ect s, as Figure 25.2 shows. Figure 25.2. Command classes, such as CarryCommand, use Term objects as parameters. A Context object holds a mapping from machine and variable names to Machine objects.
The Term class defines an abst ract eval() m et hod t hat ret urns a m achine and t hat subclasses m ust im plem ent . This provides flexibilit y t o such classes as CarryCommand. I f you want t o creat e a CarryCommand obj ect t hat carries m at erial bet ween t wo specific m achines, you can use Constant obj ect s t o represent t hose m achines. Alt ernat ively, you can creat e a CarryCommand obj ect t hat will carry m at erial bet ween t wo variables, perhaps fromMachine and toMachine. You can assign t hose variables in any cont ext , such as wit hin a for loop, reusing t he CarryCommand obj ect . The com.oozinoz.robot.interpreter package includes, for dem onst rat ion purposes, a MachineLine class. This class offers a st at ic createContext() m et hod t hat ret urns a cont ext wit h a m apping of m achines in a part icular line. The cont ent s of t his init ial cont ext are:
"Mi xer 1201" "St ar Pr ess1401" "Shel l Assembl er 1301" "Fuser 1101" "Unl oadBuf f er 1501"
-> -> -> -> ->
Mi xer 1201 St ar Pr ess1401 Shel l Assembl er 1301 Fuser 1101 Unl oadBuf f er 1501
This cont ext m aps m achine nam es t o inst ances of Machine. You can program a CarryCommand obj ect , for exam ple, t o carry m at erial from t wo m achines t o t he buffer:
package com. oozi noz. r obot . i nt er pr et er ; i mpor t j ava. ut i l . *; i mpor t com. oozi noz. machi ne. *; publ i c cl ass ShowCar r y { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Cont ext c = Machi neLi ne. cr eat eCont ext ( ) ;
Var i abl e x = new Var i abl e( "x") ; Machi ne ub = c. l ookup( "Unl oadBuf f er 1501") ; Car r yCommand comm = new Car r yCommand( x, new Const ant ( ub) ) ; c. assi gn( x, c. l ookup( "Mi xer 1201") ) ; comm. execut e( c) ; c. assi gn( x, c. l ookup( "St ar Pr ess1401") ) ; comm. execut e( c) ; } } The CarryCommand obj ect direct s t he robot t o m ove m at erial. A log of t he m achine line's response t o t he com m and execut ions is:
Robot wi l l car r y f r om Mi xer 1201 t o Unl oadBuf f er 1501 Mi xer 1201 unl oadi ng Robot wi l l car r y f r om St ar Pr ess1401 t o Unl oadBuf f er 1501 St ar Pr ess1401 unl oadi ng Not e t hat t he CarryCommand obj ect is reusable. To obt ain t his kind of flexibilit y, ot her Command classes also define t hem selves using Term obj ect s, as Figure 25.3 shows. Figure 25.3. Command classes generally define themselves using terms that may be variables, specific machines, or Boolean expressions.
The int erpret er classes let you com pose program s t hat cont rol t he m achine line. Each class int erpret s t he execute() com m and in a way t hat is consist ent wit h t he class's nam e. For exam ple, t he execute() m et hod for StartupCommand is:
publ i c voi d execut e( Cont ext c) { Machi ne m = t er m. eval ( c) ; m. st ar t up( ) ; } For t he ForMachines class, execute() is:
publ i c voi d execut e( Cont ext c) { I t er at or i = c. machi nes. i t er at or ( ) ; whi l e ( i . hasNext ( ) ) { Machi ne m = ( Machi ne) i . next ( ) ; c. assi gn( var i abl e, m) ; body. execut e( c) ; } }
This code it erat es over t he m achines in t he provided cont ext , assigning a variable t o each m achine and execut ing t he loop's body. The idea here is for client s t o creat e a body t hat depends on t he variable, as in a norm al for loop. Consider a short Java program t hat shut s down all t he m achines in t he MachineLine cont ext :
package com. oozi noz. r obot . i nt er pr et er ; i mpor t com. oozi noz. machi ne. *; publ i c cl ass ShowFor Command { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Cont ext c = Machi neLi ne. cr eat eCont ext ( ) ; Var i abl e m = new Var i abl e( "m") ; For Machi nes f m = new For Machi nes( m, new Shut downCommand( m) ) ; f m. execut e( c) ; } } The body of t he for com m and shut s down m achine "m". The for com m and assigns t his variable t o each m achine in t he provided cont ext and execut es t he body. The program produces a log of t he com m and execut ion:
Mi xer 1201 shut t i ng down Fuser 1101 shut t i ng down St ar Pr ess1401 shut t i ng down Shel l Assembl er 1301 shut t i ng down Unl oadBuf f er 1501 shut t i ng down I m plem ent ors of t he Term int erface m ust im plem ent eval() so t hat it ret urns an obj ect of t ype Machine. However, t he Term hierarchy includes t wo classes t hat are effect ively Boolean operat ors t hat ret urn null t o m ean false and ret urn a Machine obj ect t o m ean t rue. Figure 25.4 shows t he Boolean subclasses of Term. Figure 25.4. The Term hierarchy includes classes that are effectively Booleans.
The Equals class and t he HasMoreMaterial class ret urn null t o indicat e false. You can creat e, for exam ple, an Equals t erm t hat com pares a variable t o t he const ant value of t he unload buffer:
Const ant ub = new Const ant ( c. l ookup( "Unl oadBuf f er 1501") ) ; Ter m t = new Equal s( m, ub) ; You can use an inst ance of Equals as t he condit ional t erm in an IfCommand obj ect . An IfCommand obj ect has a t erm , a body com m and, and an elseBody com m and. When it execut es, an IfCommand obj ect execut es it s m ain body com m and unless it s t erm evaluat es t o null, in which case it execut es it s elseBody com m and:
package com. oozi noz. r obot . i nt er pr et er ; i mpor t com. oozi noz. machi ne. *; publ i c cl ass I f Command ext ends Command { pr ot ect ed Ter m t er m; pr ot ect ed Command body; pr ot ect ed Command el seBody;
publ i c I f Command( Ter m t er m, Command body, Command el seBody) { t hi s. t er m = t er m; t hi s. body = body; t hi s. el seBody = el seBody; }
publ i c voi d execut e( Cont ext c) { i f ( t er m. eval ( c) ! = nul l ) { body. execut e( c) ; } el se { el seBody. execut e( c) ; } } }
CHALLENGE 25.1
Writ e a program t hat creat es and uses an int erpret er t o shut down all t he m achines t hat MachineLine cont rols, except for t he unload buffer.
Now suppose t hat you want t o unload all t he m at erial from a m achine. The HasMaterial class t akes anot her t erm —usually a m achine—as a const ruct or argum ent . A HasMaterial t erm ret urns null if it s t erm has no m at erial and ot herwise ret urns t he t erm 's value:
package com. oozi noz. r obot . i nt er pr et er ; i mpor t com. oozi noz. machi ne. *; publ i c cl ass HasMat er i al ext ends Ter m { pr ot ect ed Ter m t er m; publ i c HasMat er i al ( Ter m t er m) { t hi s. t er m = t er m; } publ i c Machi ne eval ( Cont ext c) { Machi ne m = t er m. eval ( c) ; r et ur n m. hasMat er i al ( ) ? m : nul l ; } } To unload a m achine and t o keep unloading it unt il it 's em pt y, you need a WhileCommand class. Suppose t hat you call up t he m achine line com pany and ask for t his class. While you wait for a pat ch or t he next release, you can also t em porarily creat e t he class yourself. The code for WhileCommand should be sim ilar t o t he code in IfCommand.java.
CHALLENGE 25.2
Writ e t he code for WhileCommand.java.
You m ight put your WhileCommand class int o use wit h an int erpret er t hat unloads a st ar press:
package com. oozi noz. r obot . i nt er pr et er ; i mpor t com. oozi noz. machi ne. *; publ i c cl ass ShowWhi l e
{ publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Cont ext c = Machi neLi ne. cr eat eCont ext ( ) ; Const ant sp = new Const ant ( c. l ookup( "St ar Pr ess1401") ) ; Const ant ub = new Const ant ( c. l ookup( "Unl oadBuf f er 1501") ) ; Whi l eCommand wc = new Whi l eCommand( new HasMat er i al ( sp) , new Car r yCommand( sp, ub) ) ; wc. execut e( c) ; } } The wc obj ect is an int erpret er t hat int erpret s execute() t o m ean unload all t he bins from st ar press 1401. The m achine com m and int erpret er hierarchy let s you creat e arbit rary program s at runt im e by com posing new int erpret er obj ect s. I n t his regard, I NTERPRETER is sim ilar t o COMMAND.
CHALLENGE 25.3
What is t he difference, if any, bet ween t he COMMAND and I NTERPRETER pat t erns?
Interpreters, Languages, and Parsers The I NTERPRETER pat t ern addresses how int erpret ers work but does not specify how you should com pose new int erpret ers at runt im e. I n t his chapt er, you have built new int erpret ers " m anually," by writ ing lines of Java code. But by far t he m ost com m on way t o creat e a new int erpret er is wit h a parser. A parser can read t ext ual com m ands from a file or from a user prom pt and can use t he t ext t o creat e an int erpret er. Powerful! Anot her reason t o learn about building parsers is t hat if you want t o underst and t he I NTERPRETER chapt er in Design Pat t erns ( Gam m a et al. 1995) , you really need t o underst and t he int errelat ion of int erpret ers, parsers, and com put er languages. A pa r se r is an obj ect t hat can recognize t ext and decom pose it s st ruct ure according t o a set of rules int o a form suit able for furt her processing. For exam ple, you can writ e a parser t hat will creat e a m achine com m and int erpret er obj ect t hat corresponds t o a t ext ual program , such as:
whi l e St ar Pr ess1401 hasMor eMat er i al { car r y f r om St ar Pr ess1401 t o Unl oadBuf f er 1501; } When you writ e a parser, t he set of st rings it recognizes is a la ngua ge . When you creat e a new lit t le com put er language, you will m ost oft en creat e a cont e x t - fr e e la ngua ge , a pat t ern of t ext t hat follows a gr a m m a r —a collect ion of rules t hat det erm ine how you can com pose an elem ent of t he language. A parser decom poses a st ring according t o t he rules of com posit ion. I n a som ewhat abst ract sense, a language need not be a set of st rings. Design Pat t erns uses t he word language t o m ean t he set of possibilit ies t hat can occur by applying t he set
of rules t hat an int erpret er hierarchy m odels. For exam ple, t he m achine com m and int erpret er hierarchy est ablishes a language: t he set of all possible obj ect s t hat you m ight com pose from t he hierarchy. An IfCommand obj ect , for exam ple, is an elem ent , or sent ence, of t his language. Using t his t erm inology, you m ight st at e t he int ent of I NTERPRETER as: Define a class hierarchy t hat represent s a gram m ar—a set of com posit ion rules—and define an operat ion t hroughout t his hierarchy t o int erpret , or bring m eaning t o, inst ances of t he set of possible com posit ions. This explanat ion is sim ilar t o t he Design Pat t erns st at em ent of t he int ent of I NTERPRETER: " Given a language, define a represent at ion for it s gram m ar along wit h an int erpret er t hat uses t he represent at ion t o int erpret sent ences in t he language" ( p. 243) . I f you t hink of t he possible com posit ions of t he m achine com m and hierarchy as a language, t he j ob of a parser is t o t ranslat e a t ext ual language int o an equivalent int erpret er language. Many t ools perform t his t ranslat ion. You can search t he Web for " parser generat or" t ools t o find t he lat est freeware parsers. You can also use t he code t hat com es wit h Building Parsers wit h Java™ ( Met sker 2001) . That book's int ent is t o m ake it easier for you t o creat e new lit t le com put er languages. You don't have t o develop your own parsers t o underst and I NTERPRETER. You should, however, underst and why an int erpret er oft en needs a parser.
CHALLENGE 25.4
Close t his book and writ e a sent ence t hat describes t he associat ion of int erpret ers, languages, and parsers.
Summary I nt erpret ers let you com pose new com m ands, using t he classes in a hierarchy t hat you creat e. Each class in t he hierarchy defines a way of com posing com m ands—a gram m ar rule. This com posit ion rule det erm ines how t he class im plem ent s, or int erpret s, an operat ion t hat you dist ribut e t hroughout t he hierarchy. The operat ion in an int erpret er is oft en nam ed execute() or evaluate(). The operat ion nam e is necessarily vague, becom ing m eaningful only when com bined wit h t he nam e of t he class t hat im plem ent s it . Like STATE, STRATEGY, and COMMAND, t he I NTERPRETER pat t ern fixes t he nam e of an operat ion and uses various classes t o im plem ent t he operat ion. I NTERPRETER applies t his idea t o let you com pose execut able obj ect s according t o a set of com posit ion rules t hat you define.
Part V: Extension Patterns Chapter 26. Introducing Extensions Chapter 27. Decorator Chapter 28. Iterator Chapter 29. Visitor
Chapter 26. Introducing Extensions
An ext ension is t he addit ion of a class, an int erface, or a m et hod t o an exist ing code base. The m ost com m on way t o ext end code is t o writ e a new class, alt hough you can also ext end a class t hrough delegat ion. I n part icular, when you'd like t o subclass from t wo parent s, you can subclass from one parent and use delegat ion t o " inherit " t he behavior of t he ot her class. Obj ect - orient ed developm ent in Java does not begin from scrat ch. Rat her, it 's fair t o say t hat obj ect - orient ed soft ware developm ent is ext ension. To develop in Java, learn what is available in an exist ing code base, including a com pany's code and t he Java class libraries. Aft er seeing where your changes fit in, add t he code you need for your applicat ion. I n Java, applicat ion developm ent is ext ension, and ext ension begins where reuse leaves off.
Reuse as an Alternative to Extension When you shop around before you code, you have less code t o m aint ain, you benefit from t he wisdom of developers who have gone before you, and you can ensure t hat your own code dovet ails wit h t he class you are ext ending. Every class t hat you creat e will inherit from t he Object class, and t his is a nat ural place t o st art shopping for exist ing code. Figure 26.1 shows t he Object class. Figure 26.1. The Object class has methods that every OO developer should know how to use.
Earlier exam ples in t his book have reused m ost of t he behavior available in Object, including t he clone(), equals(), and getClass() m et hods. As a Java developer, you should be able t o reuse t he wait() and notify() m et hods t hat Object provides. Suppose t hat t he Oozinoz robot can st ack bins of m at erial but only t o a height of, say, t hree bins. I f you com m and t he robot t o st ack a fourt h bin, it will wait unt il a hum an rem oves a bin. Figure 26.2 shows a Swing applicat ion—t he ShowWaitAndNotify class in com.oozinoz.applications—t hat m odels t his sit uat ion. Figure 26.2. The user of this application has pressed Load a fourth time, launching a thread that will wait until the user clicks Unload.
Wit h t he applicat ion in t he st at e t hat Figure 26.2 shows, a fourt h, unseen bin await s st acking. Clicking Unload will rem ove a bin, let t ing t he t hread com plet e t hat is wait ing t o load a bin. This will leave t hree bins in t he st ack and will reenable t he Load but t on. Clicking Unload four m ore t im es will clear t he st ack and t hen put t he Unload but t on in a wait st at e, await ing a bin t hat it will im m ediat ely clear. At t he heart of an applicat ion is a BinStack class t hat uses wait() and notify() t o suspend t hreads t hat load or unload m ore bins t han t he st ack can handle. Figure 26.3 shows t he applicat ion classes. Figure 26.3. These classes collaborate to produce the bin-stacking application.
This applicat ion depends on t he m onit ors t hat Java supplies t o every obj ect . The wait() m et hod relinquishes a t hread's possession of an obj ect 's m onit or and m akes t he t hread wait t o get it back. The notify() m et hod wakes up a t hread t hat will obt ain t he m onit or when it becom es available. Eit her of t hese m et hods can execut e only in a t hread t hat has t he m onit or. One way t o obt ain an obj ect 's m onit or is t o execut e a synchronized inst ance m et hod of t hat obj ect . This is t he t echnique t hat t he BinStack class uses:
package com. oozi noz. machi ne; i mpor t j ava. ut i l . *; publ i c cl ass Bi nSt ack { publ i c st at i c f i nal i nt STACK_LI MI T = 3; pr i vat e St ack st ack = new St ack( ) ;
synchr oni zed publ i c Bi n pop( ) { whi l e ( st ack. si ze( ) == 0) { t ry { wai t ( ) ; Thr ead. sl eep( 500) ; } cat ch ( I nt er r upt edExcept i on i gnor e) { } } i f ( st ack. si ze( ) == STACK_LI MI T) { not i f y( ) ; } r et ur n ( Bi n) st ack. pop( ) ; } synchr oni zed publ i c voi d push( Bi n b) { / * ? */ } publ i c i nt si ze( ) { r et ur n st ack. si ze( ) ; } } I f t he st ack is em pt y, t he pop() m et hod causes t he t hread t hat called it t o wait . This t hread will wait unt il t he push() m et hod—which you are about t o writ e—awakens it . When t he pop() m et hod wakes up aft er wait ing, it wait s anot her half second. This let s a newly st acked bin briefly appear in t he GUI before t his m et hod snat ches it away. The pop() m et hod checks t he size of t he st ack in a while loop in case ot her t hreads m ight pop t he st ack, alt hough t his won't occur in t he ShowWaitAndNotify applicat ion. When t he st ack is nonem pt y, t he pop() m et hod checks t o see whet her t he st ack is full. I f t he st ack is full, anot her t hread m ay be wait ing t o push anot her bin, so t he pop() m et hod calls notify(). This will cause any t hread wait ing on t he BinStack obj ect 's m onit or t o wake up when pop() com plet es. The pop() m et hod concludes by popping t he t op Bin obj ect .
CHALLENGE 26.1
Writ e t he code for t he push() m et hod of t he BinStack class.
You m ay have not iced t hat t he pop() m et hod of BinStack calls notify() before it pops an obj ect from t he st ack. I f a wait ing t hread were t o wake up and st art accessing t he st ack before t his m et hod pops an obj ect , t he st ack would overflow.
CHALLENGE 26.2
There is, in fact , no danger in t he BinStack code t hat a t hread wait ing t o push an obj ect will st art execut ing before t he pop() m et hod com plet es it s pop. Why is t hat ?
The Object class com m ent s provide a surprising am ount of docum ent at ion on how wait() and notify() work. Anot her excellent reference on t hese m et hods is The Java™ Class Libraries, Volum e 1 ( Chan, Lee, and Kram er 1998) . For an ext ensive work on m any applicat ions of Java's locking facilit ies, I also recom m end Concurrent Program m ing in Java™ ( Lea 2000) . To use a BinStack obj ect , creat e a t hread t hat pushes or pops an obj ect t o t he st ack. The actionPerformed() m et hod of ShowWaitAndNotify provides an exam ple:
publ i c voi d act i onPer f or med( Act i onEvent e) { Obj ect sour ce = e. get Sour ce( ) ; i f ( sour ce. equal s( l oadBut t on( ) ) ) { new Thr ead( ) { publ i c voi d r un( ) { l oadBut t on( ) . set Enabl ed( f al se) ; bi nSt ack( ) . push( new Bi n( "a bi n") ) ; l oadBut t on( ) . set Enabl ed( t r ue) ; st ackPanel ( ) . r epai nt ( ) ; } } . st ar t ( ) ; } i f ( sour ce. equal s( unl oadBut t on( ) ) ) { new Thr ead( ) { publ i c voi d r un( ) { unl oadBut t on( ) . set Enabl ed( f al se) ; bi nSt ack( ) . pop( ) ; unl oadBut t on( ) . set Enabl ed( t r ue) ; st ackPanel ( ) . r epai nt ( ) ; } } . st ar t ( ) ; } } You m ight want t o refact or t his code, applying OBSERVER, so t hat t he StackPanel obj ect will repaint it self when t he st ack changes.
Shopping around and discovering such t ools as Java's support for locking m ay occasionally m ake you feel overwhelm ed. There is so m uch t o learn! Avid learning is good, but it has t he downside t hat you are coding t oday wit h m uch less knowledge t han you'll have in a year. A good balance is t o spend an hour a day learning and t o spend t he rest of your work t im e applying what you know. Alt hough you are learning all t he t im e, you have t o writ e code t oday, creat ing your own ext ensions t o an exist ing soft ware base.
Extending by Subclassing When you creat e a new class, you should ensure t hat it is a logical and consist ent ext ension of t he class you ext end, as Chapt er 7, I nt roducing Responsibilit y, explores. A Java com piler will enforce cert ain aspect s of consist ency.
CHALLENGE 26.3
What four aspect s of t he m odel in Figure 26.4 will a Java com piler t ake except ion t o? Figure 26.4. What's wrong with this picture?
A Java com piler helps t o ensure t he consist ency of your classes, but m any pot ent ial ext ension problem s will elude a com piler. I n part icular, class nam es have a m eaning in English t hat only hum an- level int elligence can appreciat e. For exam ple, an overt error is t o m ake Customer a subclass of Sparkler. ( A spa r k le r is a wire coat ed wit h a com bust ible past e sat urat ed wit h iron filings t hat do, in fact , sparkle.) Ot her errors are subt ler and, as such, subj ect t o j udgm ent as t o whet her t hey are in fact errors.
CHALLENGE 26.4
List t hree aspect s of t he Figure 26.5 m odel t hat are problem s or at least quest ionable.
26.4
quest ionable. Figure 26.5. What's at least questionable with this picture?
The Liskov Substitution Principle New classes should be logical, consist ent ext ensions of t heir superclasses, but what does it m ean t o be logical and consist ent ? A Java com piler will ensure a cert ain level of consist ency, but m any principles of consist ency will elude a com piler. One principle you should consider in your designs is t he Liskov Subst it ut ion Principle ( LSP) . This principle, docum ent ed in Liskov ( 1987) , can be paraphrased: An inst ance of a class should funct ion as an inst ance of it s superclass. Basic LSP com pliance is built int o OO languages, such as Java. For exam ple, it is legal t o refer t o a Rocket obj ect as a Firework, as Rocket is a subclass of Firework:
Fi r ewor k f = new Rocket ( ) ; Som e aspect s of LSP com pliance require hum an- level int elligence, or at least m ore int elligence t han t oday's com pilers possess. For exam ple, does code t hat set s a collect ion of fireworks t o be nonexploding violat e LSP?
publ i c voi d def use( Fi r ewor kLi st l i st ) { f or ( i nt i = 0; i < l i st . si ze( ) ; i ++) { Fi r ewor k f = l i st . get ( i ) ; f . set Expl odes( f al se) ; } }
The Firework class provides t he setExplodes() m et hod so t hat you can specify whet her cert ain t ypes of fireworks explode. I n part icular, rocket s m ay or m ay not explode. The setExplodes() m et hod is useful for rocket s, but som e subclasses of Firework cannot ent irely support t his operat ion. I t is a m odeling error, for exam ple, t o indicat e t hat a Firework inst ance explodes if t hat inst ance is in fact an inst ance of Sparkler. The m odel violat es LSP because subclasses do not support a superclass m et hod. However, violat ions of LSP are not necessarily bad. I n t his exam ple, a m odeling error occurs only when setExplodes() is called wit h an argum ent t hat doesn't m ake sense for a part icular t ype of firework. Violat ions of LSP can occur out side of dom ain- specific code and can occur in t he Java class libraries. Consider a program t hat convert s an array t o a list , print s it , and adds an elem ent t o t he list :
package com. oozi noz. appl i cat i ons; i mpor t j ava. ut i l . *; publ i c cl ass ShowAsLi st { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { St r i ng[ ] names = new St r i ng[ ] { "Mi xer ", "St ar Pr ess", "Shel l Assembl er " } ; Syst em. out . pr i nt l n( names) ; Li st L = Ar r ays. asLi st ( names) ; Syst em. out . pr i nt l n( L) ; L. add( "Fuser ") ; } } This program print s t wo lines of t ext and t hen crashes:
[ Lj ava. l ang. St r i ng; @7259da [ Mi xer , St ar Pr ess, Shel l Assembl er ] Except i on i n t hr ead "mai n"j ava. l ang. Unsuppor t edOper at i onExcept i on at j ava. ut i l . Abst r act Li st . add( Unknown Source) ... The first line is an array's represent at ion of it self. The second line is a list 's selfrepresent at ion. The rem aining out put reflect s an unhandled except ion t hat occurs because t he add() m et hod t hrows an UnsupportedOperationException. The im plem ent at ion of List t hat asList() ret urns does not support adding values t o t he list . You can work around t his lim it at ion by defining t he list as follows:
Li st L = new Ar r ayLi st ( Ar r ays. asLi st ( names) ) ;
CHALLENGE 26.5
Does Arrays.asList() violat e LSP? Can you j ust ify why t his m et hod ret urns an im plem ent at ion of List t hat does not support t he full List int erface?
Extending by Delegating Ext ension t hrough delegat ion occurs when a class has m et hods t hat forward calls t o ident ical operat ions supplied by anot her obj ect . This t echnique m akes available t he
behavior of t he forwarded- t o class on t he class you are ext ending, but t his is usually not t he best way t o achieve ext ension. I f you want t o inherit t he exact m et hods of an exist ing class, t he m ost st raight forward approach is t o subclass it . However, t his is not always possible. The class whose behavior you need m ay be declared final—as String is, for exam ple—or your class m ay already be subclassing som et hing else. I n t hese cases, you can ext end your class t hrough delegat ion.
CHALLENGE 26.6
Suppose t hat you want t o m ake a class in an exist ing hierarchy observable, so t hat int erest ed client s can regist er for not ificat ion of changes. Nam e an exist ing class from w hich you can " inherit " t hese feat ures, and describe how t he delegat ion works.
Ext ending a final class using delegat ion is usually quest ionable, or j ust a plain bad idea. Suppose t hat a colleague asks for your t hought s about ext ending t he String class wit h an OoString class t hat adds a few new behaviors. Specifically, he want s Oozinoz st rings t o provide setTitle-Case() and setRandomCase() m et hods. ( Tit le ca se m eans t hat charact ers aft er whit espace are in uppercase. Ra n dom ca se m eans t hat charact ers in t he st ring are uppercase or lowercase at random .) Figure 26.6 shows t he proposed OoString class, which adds funct ions t hat change t he case of t ext , such as ad copy for a Web sit e:
package com. oozi noz. dubi ous; publ i c cl ass ShowOoSt r i ng { publ i c st at i c voi d mai n( St r i ng ar gs[ ] ) { OoSt r i ng os = new OoSt r i ng( "Launch our newest r ocket " + "f r om any i nt er nat i onal wat ers! ") ; os. set RandomCase( ) ; os. set Ti t l eCase( ) ; Syst em. out . pr i nt l n( os) ; } } Figure 26.6. In this questionable design, the OoString class extends the String class, delegating certain operations to an underlying String instance.
This code print s out som et hing like:
LaUnch OUr NeWEsT ROcKEt Fr Om ANY I nt er nat I Onal WATer s!
CHALLENGE 26.7
Which of t he following obj ect ions are valid?
A. You can achieve the desired case effects by introducing similar operations in a utility class. B. One reason String is final is to preserve immutability, and the OoString class undermines this. C. Classes such as StringReader cannot effectively be extended to work with OoString objects instead of the String objects they expect. D. To "extend" String, the OoString class should provide all the operations that the String class provides. E. Strings are built into the compiler in a special way that OoString cannot emulate.
Summary A m aj or change t hat has accom panied t he advent of obj ect - orient ed program m ing is t hat program m ing is now a m at t er of ext ending an exist ing code base. Talent ed Java developers spend m ore t im e reading t han writ ing, t o underst and how t heir code will fit wit h and benefit from t he volum inous class libraries. One class t hat you should be especially sure t o underst and is Object, t he superclass from which all your classes will derive. The subclasses you creat e should be logical and consist ent ext ensions of t heir superclasses. The Java com piler will enforce som e aspect s of consist ency; in m any areas, however, consist ency is a m at t er of j udgm ent . The Liskov Subst it ut ion Principle suggest s t hat subt ypes should uphold t he behavior of supert ypes. A good policy is t o violat e t his principle only when you have a com pelling j ust ificat ion. I n addit ion t o subclassing, you can ext end t hrough delegat ion. This is m ore fragile t han subclassing but m ay be necessary if your class needs t o provide t he behavior of m ult iple superclasses. A healt hy view is t hat developm ent is a m at t er of ext ending t he Java code base wit h j ust t he changes you need for your applicat ion. Aggressive reuse of t he Java class libraries will you help you reduce t he chances for int roducing defect s and will let you benefit from t he hard work of m any developers who have preceded you. Wit h a good underst anding of t he exist ing code base, you can develop rich applicat ions as m inim al ext ensions of exist ing code.
Beyond Ordinary Extension
The ordinary way t o ext end a soft ware syst em is t o find a suit able superclass—usually Object—and t o subclass it . You can also " inherit " behavior from m ore t han one class by copying som e of t he operat ions of a second class and delegat ing calls t o an inst ance of t hat class. Bot h of t hese ext ension t echniques, however, require t hat you know at com pile t im e what behaviors you want t o add. I f you need t o add behavior t o an obj ect wit hout changing it s class, you m ay be able t o apply t he Tem plat e Met hod ( Chapt er 21) , Com m and ( Chapt er 24) , Decorat or ( Chapt er 27) , or Visit or ( Chapt er 29) design pat t erns. Ordinary ext ension also adds behavior t o individual inst ances of a class. You m ay need t o add behaviors t hat apply t o a collect ion of inst ances of your class. The I TERATOR pat t ern addresses one such case.
If you intend to • • • • •
Allow a client to hook in an operation at a step in an algorithm
Apply the pattern Tem plat e Met hod ( Chapt er 21) Com m and ( Chapt er
Let a client outfit your code with an operation to 24) execute in response to an event Attach additional responsibilities to an object dynamically Provide a way to access a collection of instances of a class that you create
Decorat or ( Chapt er 27) I t erat or ( Chapt er 28) Visit or ( Chapt er 29)
Allow for the addition of new operations to a class without changing the class
Ext ension- orient ed pat t erns address cont ext s in which you need t o add behavior specific t o a collect ion of obj ect s or t o add new behaviors t o an obj ect wit hout alt ering t he obj ect 's class. For exam ple, when you need t o be able t o add new behavior t o an obj ect dynam ically, you can apply t he DECORATOR pat t ern.
Chapter 27. Decorator Ordinarily, an obj ect inherit s behaviors from it s superclasses. As earlier chapt ers have shown, you can apply t he STATE and t he STRATEGY pat t erns t o alt er t he behavior of an obj ect dynam ically. Like STATE and STRATEGY, t he DECORATOR pat t ern relies on polym orphism , but DECORATOR com bines polym orphism wit h delegat ion t o let you ext end an obj ect 's behavior. The int ent of DECORATOR is t o let you com pose an obj ect 's behavior dynam ically.
A Classic Example of Decorator: Streams The Java class libraries provide a classic exam ple of t he DECORATOR pat t ern in t he overall design of Java input and out put st ream s. A st r e a m is a serial collect ion of byt es or charact ers, such as t hose t hat appear in a docum ent . I n Java, each st ream obj ect usually cont ains anot her st ream obj ect t o w hich it forwards it s operat ions. This st ruct ure is t ypical of DECORATOR. The DECORATOR pat t ern arranges for each decorat or obj ect t o cont ain anot her decorat or obj ect . I n t his regard, a decorat or is like a slim com posit e whose elem ent s each have a single child. Unlike t he COMPOSI TE pat t ern, whose purpose is t o com pose aggregat e obj ect s, t he purpose of DECORATOR is t o com pose behavior.
St ruct urally, DECORATOR arranges classes in a hierarchy and dist ribut es operat ions across t his hierarchy. Each class in t he hierarchy t ypically has a const ruct or t hat requires anot her inst ance of a class in t he hierarchy. For exam ple, Figure 27.1 shows a port ion of t he OutputStream hierarchy from t he Java class libraries. Figure 27.1. Java input/output (I/O) streams provide a classic example of Decorator.
DECORATOR classes t ypically im plem ent t heir operat ions by relying on t he decorat or obj ect t hey receive in t heir const ruct ors. For exam ple, t he GZIPOutputStream class im plem ent s it s write() operat ions by com pressing t he byt es it receives and t hen writ ing t hem t o t he OutputStream obj ect t hat it s const ruct or requires. I n ot her w ords, t he GZIPOutputStream class adds t he behavior of com pression t o t he OutputStream obj ect it receives. This is t ypical of t he int ent of DECORATOR. Requiring an OutputStream obj ect in each OutputStream const ruct or is a recursive idea. Like COMPOSI TE, t he DECORATOR pat t ern requires som e classes in a hierarchy t o act as leaf nodes. For exam ple, you can inst ant iat e t he FileOutputStream class wit hout already having an OutputStream obj ect . Most of Java's st ream classes are part of t he java.io package, but t he DeflaterOutputStream, GZIPOutputStream, and ZipOutputStream classes t hat Figure 27.1 shows are part of t he package java.util.zip. The ZipOutputStream class let s you creat e a com pressed file wit h m ult iple ent ries, whereas t he GZIPOutputStream class com presses a single input file. Which of t hese classes you choose depends on how you want t o " decorat e" t he out put . For exam ple, you can wrap a GZIPOutputStream obj ect around a FileOutputStream obj ect t o creat e a zipped version of an exist ing file:
package com. oozi noz. appl i cat i ons; i mpor t j ava. i o. *; i mpor t j ava. ut i l . zi p. *; publ i c cl ass ShowGzi p { publ i c st at i c voi d mai n( St r i ng ar gs[ ] ) t hr ows I OExcept i on { St r i ng f i l eName = "demo. doc"; j ava. net . URL ur l = Cl assLoader . get Syst emResour ce( f i l eName) ; I nput St r eam i n = ur l . openSt r eam( ) ; GZI POut put St r eam out = new GZI POut put St r eam( new Fi l eOut put St r eam( ur l . get Fi l e( ) + ". gz") ) ; byt e[ ] dat a = new byt e[ 100] ; whi l e ( t r ue) { i nt n = i n. r ead( dat a) ; i f ( n == - 1) { br eak; } out . wr i t e( dat a, 0, n) ; } out . cl ose( ) ; i n. cl ose( ) ; } }
This program uses t he ClassLoader class t o find t he URL ( uniform resource locat or) of t he demo.doc file. I f you set t he class pat h for t he ShowGzip class t o include t he parent direct ory of a demo.doc file, t he getSystemResource() m et hod will find it . The ShowGzip class's main() m et hod t acks a .gz ext ension ont o t he out put file and places it int o t he sam e direct ory as t he input . The program creat es a com pressed demo.doc.gz file t hat any zip ut ilit y can decom press. The OutputStream class has m any m ore subclasses, and you can creat e your own. The FilterOutputStream class is designed specifically t o sim plify t he creat ion of new out put st ream s. This class provides st ub im plem ent at ions of t he OutputStream class m et hods t hat forward t heir operat ions t o t he underlying st ream . You can subclass FilterOutputStream and override j ust t he m et hods t hat you need. I n t his regard, FilterOutputStream is sim ilar t o it s count erpart , FilterWriter. I n addit ion t o it s support for byt e st ream s, Java includes a som ewhat parallel collect ion of classes t hat m anage st ream s of charact ers. Like t he byt e st ream hierarchies, Java provides Reader and Writer hierarchies for m anaging charact er st ream s. These hierarchies include Filter subclasses t hat , like t heir byt e- orient ed count erpart s, are designed for subclassing. The FilterWriter class, as shown in Figure 27.2, has t hree versions of write() m et hods. Figure 27.2. Oozinoz developers use the OozinozFilter class as the superclass for classes that decorate output character streams.
The OozinozFilter class direct s it s t wo concret e write() m et hods t o call it s abst ract write(), so t hat subclasses have only one m et hod t o override:
package com. oozi noz. i o; i mpor t j ava. i o. *; publ i c abst r act cl ass Oozi nozFi l t er ext ends Fi l t er Wr i t er { pr ot ect ed Oozi nozFi l t er ( Wr i t er out ) { super ( out ) ; } publ i c voi d wr i t e( char cbuf [ ] , i nt of f , i nt l en) t hr ows I OExcept i on
{ f or ( i nt i = 0; i < l en; i ++) { wr i t e( cbuf [ i ] ) ; } } publ i c abst r act voi d wr i t e( i nt c) t hr ows I OExcept i on; publ i c voi d wr i t e( St r i ng s, i nt of f , i nt l en) t hr ows I OExcept i on { wr i t e( s. t oChar Ar r ay( ) , of f , l en) ; } } Oozinoz has a variet y of out put charact er st ream decorat ors t hat m anipulat e t ext for display in t he com pany's Web sit e. Figure 27.3 shows t he Oozinoz decorat ors. Figure 27.3. The Oozinoz filters can decorate a Writer object with behaviors that manipulate ad copy text for a Web site.
For exam ple, t he WrapFilter class com presses whit espace and wraps t ext at a specified line lengt h. ( The cont ent s of WrapFilter.java and all t he filt er classes are available at oozinoz.com .) The following program line- wraps t ext in an input file and put s it in t it le case:
package com. oozi noz. appl i cat i ons; i mpor t j ava. i o. *;
i mpor t com. oozi noz. i o. *; publ i c cl ass ShowFi l t er s { publ i c st at i c voi d mai n( St r i ng ar gs[ ] ) t hr ows I OExcept i on { Buf f er edReader i n = new Buf f er edReader ( new Fi l eReader ( ar gs[ 0] ) ) ; Wr i t er out = new Fi l eWr i t er ( ar gs[ 1] ) ; out = new Wr apFi l t er ( new Buf f er edWr i t er ( out ) , 40) ; out = new Ti t l eCaseFi l t er ( out ) ;
whi l e ( t r ue) { St r i ng s = i n. r eadLi ne( ) ; i f ( s == nul l ) { br eak; } out . wr i t e( s + "\ n") ; } out . cl ose( ) ; i n. cl ose( ) ; } } The BufferedReader class provides t he abilit y t o read one line at a t im e from a file. The ShowFilters program creat es a FileWriter obj ect and decorat es it wit h behaviors t o wrap t he t ext at 40 charact ers and t o t it lecase t he t ext . Suppose t hat a file nam ed demo.txt cont ains:
The "SPACESHOT" shel l hover s at 100 met er s f or 2 t o 3 mi nut es, er upt i ng st ar bur st s ever y 10 seconds t hat gener at e abundant r eadi ng- l evel l i ght f or a t ypi cal st adi um. Running t his file t hrough t he ShowFilters program cleans up t he t ext :
> j ava - cl asspat h \ oozi noz\ cl asses com. oozi noz. appl i cat i ons. ShowFi l t er s demo. t xt demo- out . t xt >t ype demo- out . t xt The "Spaceshot " Shel l Hover s At 100 Met er s For 2 To 3 Mi nut es, Er upt i ng St ar Bur st s Ever y 10 Seconds That Gener at e Abundant Readi ng- l evel Li ght For A Typi cal St adi um.
CHALLENGE 27.1
I f you want t o direct out put t o System.out inst ead of t o a file, you can creat e a Writer
27.1
inst ead of t o a file, you can creat e a Writer obj ect t hat direct s it s out put t o System.out:
Wr i t er out = new Pr i nt Wr i t er ( Syst em. out ) ; Writ e a snippet of code t o define a Writer obj ect t hat wraps t ext at 15 charact ers, cent ers t he t ext , set s t he t ext t o random casing, and direct s t he out put t o System.out.
The code for m ost of t he filt er classes is sim ple. For exam ple:
i mpor t j ava. i o. *; publ i c cl ass Lower CaseFi l t er ext ends Oozi nozFi l t er { pr ot ect ed Lower CaseFi l t er ( Wr i t er out ) { super ( out ) ; } publ i c voi d wr i t e( i nt c) t hr ows I OExcept i on { out . wr i t e( Char act er . t oLower Case( ( char ) c) ) ; } } The code for t he TitleCaseFilter class is a bit m ore com plex, as it has t o keep t rack of whet her t he st ream is in whit espace:
i mpor t j ava. i o. *; publ i c cl ass Ti t l eCaseFi l t er ext ends Oozi nozFi l t er { bool ean i nWhi t e = t r ue; pr ot ect ed Ti t l eCaseFi l t er ( Wr i t er out ) { super ( out ) ; } publ i c voi d wr i t e( i nt c) t hr ows I OExcept i on { out . wr i t e( i nWhi t e ? Char act er . t oUpper Case( ( char ) c) : Char act er . t oLower Case( ( char ) c) ) ; i nWhi t e = Char act er . i sWhi t espace( ( char ) c) || c == ' "' ; } }
CHALLENGE 27.2
Writ e t he code for RandomCaseFilter.java.
I nput and out put st ream s provide a classic exam ple of how t he DECORATOR pat t ern let s you assem ble t he behavior of an obj ect at runt im e. Anot her im port ant applicat ion of DECORATOR occurs when you need t o creat e m at hem at ical funct ions at runt im e.
Function Decorators You can com bine DECORATOR wit h t he idea of t reat ing funct ions as obj ect s t o allow for runt im e com posit ion of new funct ions. I n Chapt er 4, Facade, you refact ored an applicat ion t hat shows t he flight pat h of a nonexploding aerial shell. The original code calculat ed t he pat h in t he paintComponent() m et hod of a JPanel subclass:
publ i c voi d pai nt Component ( Gr aphi cs g) { super . pai nt Component ( g) ; / / pai nt t he backgr ound i nt nPoi nt = 101; doubl e w = get Wi dt h( ) ; doubl e h = get Hei ght ( ) ; i nt [ ] x = new i nt [ nPoi nt ] ; i nt [ ] y = new i nt [ nPoi nt ] ; f or ( i nt i = 0; i < nPoi nt ; i ++) { / / t goes 0 t o 1 doubl e t = ( ( doubl e) i ) / ( nPoi nt - 1) ; / / x goes 0 t o w x[ i ] = ( i nt ) ( t * w) ; / / y i s h at t = 0 and t = 1, and y i s 0 at t = . 5 y[ i ] = ( i nt ) ( 4 * h * ( t - . 5) * ( t - . 5) ) ; } g. dr awPol yl i ne( x, y, nPoi nt ) ; } This m et hod hard- codes t he funct ion it plot s. Suppose t hat you want t o be able t o creat e funct ions for x and y at runt im e. A first st ep is t o apply polym orphism : Make t he nam e of t he m et hod fixed, and provide classes wit h varying im plem ent at ions of t his m et hod. Figure 27.4 shows a FunPanel class t hat accept s funct ions t o plot . Figure 27.4. The FunPanel class accepts x and y functions to plot.
The setXY() m et hod in t he FunPanel class accept s x and y funct ions and calculat es t he m inim um and m axim um values of t hese curves:
publ i c voi d set XY( Funct i on f x, Funct i on f y) { t hi s. f x = f x; t hi s. f y = f y; cal cul at eExt r ema( ) ; r epai nt ( ) ; } The funct ions t hat t he setXY() m et hod accept s are param et ric funct ions of t im e. Any code t hat works wit h t he curve of a funct ion will evaluat e t he funct ion as t im e varies from 0 t o 1. We shall see t hat t his approach will let us plot parabolas, circles, and ot her t ypes of curves for which y is not st rict ly a funct ion of x. The idea t hat t im e varies from 0 t o 1 appears several places in t he funct ion- plot t ing code, such as t he calculat ion of t he ext rem e values of t he funct ions:
pr ot ect ed voi d cal cul at eExt r ema( ) { f or ( i nt i = 0; i < nPoi nt ; i ++) {
doubl e t = ( ( doubl e) i ) / ( nPoi nt - 1) ; doubl e dx = f x. f ( t ) ; doubl e dy = f y. f ( t ) ; i f ( i == 0 || dx > xMax) { xMax = dx; } i f ( i == 0 || dx < xMi n) { xMi n = dx; } i f ( i == { yMax } i f ( i == { yMi n }
0 || dy > yMax) = dy; 0 || dy < yMi n) = dy;
} } The paintComponent() m et hod also let s t im e vary from 0 t o 1 and scales t he curves t o fill t he panel:
publ i c voi d pai nt Component ( Gr aphi cs g) { super . pai nt Component ( g) ; doubl e h = ( doubl e) ( get Hei ght ( ) - 1) ; doubl e w = ( doubl e) ( get Wi dt h( ) - 1) ; f or ( i nt i = 0; i < nPoi nt ; i ++) { doubl e t = ( ( doubl e) i ) / ( nPoi nt - 1) ; xAr r ay[ i ] = ( i nt ) ( w * ( f x. f ( t ) - xMi n) / ( xMax - xMi n) ) ; yAr r ay[ i ] = ( i nt ) ( h - h * ( f y. f ( t ) - yMi n) / ( yMax - yMi n) ) ; } g. set Col or ( Col or . bl ack) ; g. dr awPol yl i ne( xAr r ay, yAr r ay, nPoi nt ) ; } The FunPanel class let s you supply various im plem ent at ions of t he f() m et hod in separat e classes. I f you were t o im plem ent t he f() m et hod direct ly, you would wind up wit h classes like FlightPathX and FlightPathY, which provide x and y funct ions. Rat her t han creat ing new classes for each new funct ion, DECORATOR let s you assem ble a funct ion from an exist ing hierarchy. Figure 27.5 shows a hierarchy of funct ion classes t hat im plem ent a com m on operat ion f(). These classes appear in t he com.oozinoz.function package.
Figure 27.5. Each subclass of Function implements f(t) in a way that corresponds to the class name.
You can use classes from t he Function hierarchy t o com pose, for exam ple, t he flight pat h of a dud:
package com. oozi noz. appl i cat i ons; i mpor t com. oozi noz. f unct i on. *; publ i c cl ass ShowFunFl i ght { publ i c st at i c voi d mai n( St r i ng ar gs[ ] ) { FunPanel p = new FunPanel ( ) ; p. set Pr ef er r edSi ze( new j ava. awt . Di mensi on( 200, 200) ) ; Funct i on x = new T( ) ; //
y = 1 - 4 * ( t - . 5) **2;
Funct i on f t = new Ar i t hmet i c( ' - ' , new T( ) , new Const ant ( . 5) ) ; Funct i on y = new Ar i t hmet i c( ' -' , new Const ant ( 1) , new Ar i t hmet i c( ' *' , new Const ant ( 4) , new Ar i t hmet i c( ' *' , f t , f t ) ) ) ; p. set XY( x, y) ; com. oozi noz. ui . Swi ngFacade. l aunch( p, " Fl i ght Pat h") ; } } Figure 27.6 shows t he result s of t his code, as well as t he following code, which plot s a circle wit hout requiring a new class:
package com. oozi noz. appl i cat i ons; i mpor t com. oozi noz. f unct i on. *; publ i c cl ass ShowFunZone { publ i c st at i c voi d mai n( St r i ng ar gs[ ] ) { FunPanel p = new FunPanel ( ) ; p. set Pr ef er r edSi ze( new j ava. awt . Di mensi on( 200, 200) ) ; Funct i on t het a = new Ar i t hmet i c( ' *' , new Const ant ( 2 * Mat h. PI ) , new T( ) ) ; Funct i on x = new Cos( t het a) ; Funct i on y = new Si n( t het a) ; p. set XY( x, y) ; com. oozi noz. ui . Swi ngFacade. l aunch( p, " Dud Zone") ; } } Figure 27.6. You can apply Decorator to create and to plot new functions without introducing new classes.
Most of t he classes in t he Function hierarchy work by wrapping t heir nam esake funct ion around anot her funct ion t hat t hey receive in a const ruct or. The Function class holds an array of " sources," t he source funct ions t hat each inst ance of a subclass will wrap:
package com. oozi noz. f unct i on; publ i c abst r act cl ass Funct i on { pr ot ect ed Funct i on[ ] sour ce; publ i c Funct i on( Funct i on[ ] sour ce) { t hi s. sour ce = sour ce; } publ i c Funct i on( Funct i on f ) { t hi s( new Funct i on[ ] { f } ) ; } publ i c abst r act doubl e f ( doubl e t ) ; } Most Function subclasses wrap a single funct ion. Consider t he code for Abs.java:
package com. oozi noz. f unct i on; publ i c cl ass Abs ext ends Funct i on { publ i c Abs( Funct i on f ) { super ( f ) ; } publ i c doubl e f ( doubl e t ) { r et ur n Mat h. abs( sour ce[ 0] . f ( t ) ) ; } } The const ruct or for t he Abs class collaborat es wit h it s superclass t o st ore a source funct ion in source[0]. The f() m et hod of class Abs shows DECORATOR at work. This m et hod applies an absolut e- value funct ion t o t he value of anot her funct ion at t he given t im e. I n ot her
words, t he Abs.f() m et hod decorat es it s source funct ion wit h an absolut e- value funct ion. This sim ple idea let s us com pose an infinit e num ber of funct ions from a sm all hierarchy of classes. The code for t he Cos and Sin classes is alm ost ident ical t o t he code for t he Abs class. The T class provides a t rivial ident it y funct ion:
package com. oozi noz. f unct i on; publ i c cl ass T ext ends Funct i on { publ i c T( ) { super ( new Funct i on[ 0] ) ; } publ i c doubl e f ( doubl e t ) { r et ur n t ; } } The T class let s you m odel funct ions t hat vary linearly wit h t im e. For exam ple, in t he ShowFunZone class, t he arc of a circle varies from 0 t o 2 t im es pi as t im e varies from 0 t o 1:
Funct i on t het a = new Ar i t hmet i c( ' *' , new Const ant ( 2 * Mat h. PI ) , new T( ) ) ; The Constant class represent s a value t hat does not change over t im e:
package com. oozi noz. f unct i on; publ i c cl ass Const ant ext ends Funct i on { pr i vat e doubl e const ant ; publ i c Const ant ( doubl e const ant ) { super ( new Funct i on[ 0] ) ; t hi s. const ant = const ant ; } publ i c doubl e f ( doubl e t ) { r et ur n const ant ; } } The Arithmetic class requires an operat or and t wo Function argum ent s:
package com. oozi noz. f unct i on; publ i c cl ass Ar i t hmet i c ext ends Funct i on { pr ot ect ed char oper at or ;
publ i c Ar i t hmet i c( char oper at or ,
Funct i on f 1, Funct i on f 2) { super ( new Funct i on[ ] { f 1, f 2 } ) ; t hi s. oper at or = oper at or ; } publ i c doubl e f ( doubl e t ) { swi t ch ( oper at or ) { case ' +' : r et ur n sour ce[ 0] . f ( t ) case ' - ' : r et ur n sour ce[ 0] . f ( t ) case ' *' : r et ur n sour ce[ 0] . f ( t ) case ' / ' : r et ur n sour ce[ 0] . f ( t ) def aul t : r et ur n 0; } }
+ sour ce[ 1] . f ( t ) ; - sour ce[ 1] . f ( t ) ; * sour ce[ 1] . f ( t ) ; / sour ce[ 1] . f ( t ) ;
}
CHALLENGE 27.3
Writ e t he code for an Exp class t hat wraps Math.exp() around it s source funct ion.
Suppose t hat t he bright ness of a st ar is a sine wave t hat decreases exponent ially:
As before, a plot of bright ness versus t im e requires no new classes:
package com. oozi noz. appl i cat i ons; i mpor t com. oozi noz. f unct i on. *; publ i c cl ass ShowBr i ght ness { publ i c st at i c voi d mai n( St r i ng ar gs[ ] ) { FunPanel p = new FunPanel ( ) ; p. set Pr ef er r edSi ze( new j ava. awt . Di mensi on( 200, 200) ) ; / / ?? Funct i on br i ght ness = / / ??
p. set XY( new T( ) , br i ght ness) ; com. oozi noz. ui . Swi ngFacade. l aunch( p, " Br i ght ness") ; } } This code produces t he plot in Figure 27.7. Figure 27.7. A star's brightness peaks quickly and then tails off.
CHALLENGE 27.4
Writ e t he code t o define a brightness obj ect t hat represent s t he bright ness funct ion.
You can add ot her funct ions t o t he Function hierarchy as needed. For exam ple, you m ight add Random, Sqrt, and Tan classes. You can also creat e new hierarchies t hat work on different t ypes, such as st rings, or t hat have a different not ion of how t o define t he f() operat ion. For exam ple, you m ight define f() as a t wo- or t hree- dim ensional funct ion of t im e. Regardless of t he hierarchy you creat e, if you apply DECORATOR and you wrap funct ions around each ot her, you will develop a rich set of funct ions t hat you can com pose at runt im e.
Decorating without Decorator Having seen som e good exam ples of DECORATOR, it is im port ant t o not e t hat sim ilar designs allow for " decorat ing" an obj ect wit hout applying t he DECORATOR pat t ern. Two areas t hat can be m ist aken for DECORATOR are t he use of Swing borders and list eners.
A GUI fram ework developer m ight apply DECORATOR in est ablishing how GUI com ponent s get borders. To do so, you can creat e a hierarchy of com ponent s t hat expect anot her com ponent in t heir const ruct or and dist ribut e a paint() m et hod across t his hierarchy. Border classes in t his hierarchy would paint t heir border and t hen forward t he paint() call t o t he com ponent t hey wrap. Swing, however, does not work t his way. Swing ant icipat es t hat any com ponent m ight need a border, and so t he JComponent class includes a setBorder() m et hod. I f you want t o " decorat e" a com ponent wit h m ore t han one border, you can apply t he CompoundBorder class. The following code gives an exam ple wit h result s shown in Figure 27.8. Figure 27.8. Borders can make a component more appealing.
package com. oozi noz. appl i cat i ons; i mpor t j avax. swi ng. *; i mpor t j avax. swi ng. bor der . *; i mpor t j ava. awt . *; i mpor t com. oozi noz. ui . *; publ i c cl ass ShowBor der s { pr ot ect ed st at i c J But t on cr eat eBut t on( St r i ng l abel ) { J But t on b = new J But t on( l abel ) ; b. set Font ( Swi ngFacade. get St andar dFont ( ) ) ; b. set FocusPai nt ed( f al se) ; r et ur n b; } publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { J But t on i gni t e = cr eat eBut t on( "I gni t e") ; J But t on l aunch = cr eat eBut t on( "Launch") ; l aunch. set Bor der ( new CompoundBor der ( new Li neBor der ( Col or . bl ack, 4) , new CompoundBor der ( new Bevel Bor der ( Bevel Bor der . RAI SED) , new Empt yBor der ( 10, 10, 10, 10) ) ) ) ; J Panel p = new J Panel ( ) ; p. add( i gni t e) ; p. add( l aunch) ; Swi ngFacade. l aunch( p, " Bor der s") ; } }
I f you t hink of paint ing a border as a behavior, you m ight design borders by using t he DECORATOR pat t ern. On t he ot her hand, if you t hink of borders as at t ribut es, t here is no need com pose behavior and no need for DECORATOR. Anot her feat ure of Java t hat m ight be m ist aken for DECORATOR is t he abilit y t o add behaviors at runt im e t hrough t he use of list eners. Suppose t hat you int roduce a rollover effect for a but t on by list ening t o t he m ouse, as t he following code and Figure 27.9 show. Figure 27.9. Listeners let you add behavior, such as rollover effects, without creating new component subclasses.
package com. oozi noz. appl i cat i ons; i mpor t com. oozi noz. ui . *; i mpor t j ava. awt . event . *; i mpor t j ava. awt . *; i mpor t j avax. swi ng. *; publ i c cl ass ShowLi st en { pr ot ect ed st at i c J But t on cr eat eBut t on( St r i ng l abel ) { J But t on b = new J But t on( l abel ) ; b. set Font ( Swi ngFacade. get St andar dFont ( ) ) ; b. set FocusPai nt ed( f al se) ; r et ur n b; } publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { J Panel p = new J Panel ( ) ; p. add( cr eat eBut t on( "Nor mal ") ) ; f i nal J But t on b = cr eat eBut t on( "Ni f t y") ;
b. addMouseLi st ener ( new MouseAdapt er ( ) { publ i c voi d mouseEnt er ed( MouseEvent e) { b. set Backgr ound( Col or . cyan) ; b. r epai nt ( ) ; } publ i c voi d mouseExi t ed( MouseEvent e) { b. set Backgr ound( Col or . l i ght Gr ay) ; b. r epai nt ( ) ;
} } ); b. set Backgr ound( Col or . l i ght Gr ay) ; p. add( b) ; Swi ngFacade. l aunch( p, " Rol l over ") ; } }
CHALLENGE 27.5
The ShowListen program out fit s a JButton obj ect at runt im e wit h a new behavior, int eract ing wit h t he cursor posit ion. But t his design is not an inst ance of DECORATOR. Why not ?
Summary When you have an obj ect t ype t hat needs a variet y of behaviors t hat you m ight com pose in various ways, t he DECORATOR pat t ern offers a flexible alt ernat ive t o creat ing a class for every possible behavioral com binat ion. The Java class libraries provide a classic exam ple of DECORATOR in t he im plem ent at ion of input and out put st ream s. I n applicat ion code, you can apply DECORATOR t o set up funct ion hierarchies. This let s you creat e a large fam ily of funct ion obj ect s from a fixed set of funct ion classes. I t is easy t o m ist ake exam ples of " decorat ion" in a design as inst ances of DECORATOR. A design m ay allow runt im e cust om izat ion of an obj ect 's at t ribut es and it s behavior wit hout using DECORATOR. The int ent of DECORATOR is t o let you com pose an obj ect 's behavior from a collect ion of cooperat ing classes.
Chapter 28. Iterator I n ordinary developm ent , you add behavior by ext ending classes wit h new m et hods. Occasionally, t hough, you need t o add behavior t o a collect ion of inst ances of t he classes you creat e. The Java class libraries provide m any feat ures for working wit h collect ions, including behaviors for sort ing, shuffling, reversing, and it erat ing over a collect ion. You m ay find, however, t hat you need it erat ion behaviors t hat go beyond t hose built int o Java. I n part icular, if you need t o add t ype safet y t o it erat ion, or if you need t o creat e a new t ype of collect ion, you will probably need t o develop your own it erat or code t o walk t hrough t he collect ion. The int ent of t he I TERATOR pat t ern is t o provide a way t o access t he elem ent s of collect ion sequent ially.
Type-Safe Collections When you pass a collect ion of obj ect s t o a client , you will oft en im ply t hat t he cont ent s of t he collect ion are inst ances of a cert ain t ype. For exam ple, if you have a st at ic m et hod getPromotionalFireworks() t hat ret urns an inst ance of List, t he im plicat ion is t hat t he list cont ains inst ances of t he Firework class. You can st rengt hen t his t ype of cont ract by creat ing a FireworkList class t hat guarant ees t hat it s cont ent s are inst ances of Firework. Figure 28.1 shows t his class. Figure 28.1. The FireworkList class provides type-specific lists for Firework objects. The Itr class is an inner class for FireworkList.
The FireworkList class st ores it s obj ect s in a regular inst ance of ArrayList. The add() m et hod ensures t hat only Firework obj ect s ent er t he list . For exam ple, t he Promo class creat es and ret urns a list of prom ot ional fireworks, using a FireworkList obj ect :
package com. oozi noz. f i r ewor ks; i mpor t com. oozi noz. uni t s. *; publ i c cl ass Pr omo i mpl ement s Uni t Const ant s { publ i c st at i c Fi r ewor kLi st get Pr omot i onal Fi r ewor ks( ) { Fi r ewor kLi st f l = new Fi r ewor kLi st ( ) ; f l . add( new Spar kl er ( "Br i ght f ul ", . 35) ) ; f l . add( new Fi r ecr acker ( "Fr i ght f ul ", . 19) ) ; f l . add ( new Rocket ( "Hei ght f ul ", 33. 95, ( Lengt h) METER. t i mes( 300) ) ); r et ur n f l ; }
} The get() m et hod in t he FireworkList class relieves client s from having t o cast it s ret urn value. To com plet e t his t hought , it is useful t o provide an it erat or t hat also relieves client s from cast ing and from get t ing a possible ClassCastException. The Itr class is useful only t o t he FireworkList class, so it is a good idea t o m ake it an inner class:
package com. oozi noz. f i r ewor ks; i mpor t j ava. ut i l . *; publ i c cl ass Fi r ewor kLi st { pr ot ect ed Li st l i st = new Ar r ayLi st ( ) ; publ i c cl ass I t r { pr i vat e I t er at or i t er at or = l i st . i t er at or ( ) ; publ i c bool ean hasNext ( ) { r et ur n i t er at or . hasNext ( ) ; } publ i c Fi r ewor k next ( ) { r et ur n ( Fi r ewor k) i t er at or . next ( ) ; } } publ i c voi d add( Fi r ewor k f ) { l i st . add( f ) ; } publ i c Fi r ewor k get ( i nt i ndex) { r et ur n ( Fi r ewor k) l i st . get ( i ndex) ; } publ i c Fi r ewor kLi st . I t r i t er at or ( ) { r et ur n new I t r ( ) ; } publ i c i nt si ze( ) { r et ur n l i st . si ze( ) ; } } Not e t hat t he Itr class does not im plem ent t he Iterator int erface from t he java.util package. The Iterator int erface requires a remove() m et hod t hat is not appropriat e in t his code. By not im plem ent ing any int erface, however, t he code requires client s t o refer t o t he it erat or's t ype wit h t he t ype Firework.Itr. A client can it erat e over a FireworkList collect ion wit hout having t o cast it s result s:
package com. oozi noz. appl i cat i ons; i mpor t com. oozi noz. f i r ewor ks. *; publ i c cl ass ShowFi r ewor kLi st { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Fi r ewor kLi st f l i st = Pr omo. get Pr omot i onal Fi r ewor ks( ) ; ?? i = ?? whi l e ( i . hasNext ( ) ) { Fi r ewor k f = i . next ( ) ; Syst em. out . pr i nt l n( f . get Name( ) + ", $" + f . get Pr i ce( ) + ", " + f . get Type( ) ) ; } } } Running t his program print s out :
Br i ght f ul , $0. 35, Spar kl er Fr i ght f ul , $0. 19, Fi r ecr acker Hei ght f ul , $33. 95, Rocket
CHALLENGE 28.1
Fill in t he m issing code in ShowFireworkList t o inst ant iat e t he it erat or.
When you creat e a t ype- specific collect ion, you m ay want t o inherit behavior from an exist ing collect ion, such as ArrayList. I n t his approach, you have t o use different m et hod nam es for get() and iterator(), as you can't change t he ret urn t ype of an overridden m et hod. You will also find t hat using ut ilit ies, such as t he sort() and min() m et hods of t he Collections class, will force client s back int o using cast s. I f you don't want t o give client s t his m uch flexibilit y, you can leave your t ype- safe class as a subclass of Object and add ut ilit ies as your client s need t hem . For exam ple, if a client needs t o know t he cheapest firework in a list , you m ight add t o FireworkList t he following m et hod:
publ i c Fi r ewor k cheapest ( ) { Compar at or c = new Compar at or ( ) { publ i c i nt compar e( Obj ect o1, Obj ect o2) { Fi r ewor k f 1 = ( Fi r ewor k) o1; Fi r ewor k f 2 = ( Fi r ewor k) o2; r et ur n ( i nt ) ( 100 * ( f 1. get Pr i ce( ) - f 2. get Pr i ce( ) ) ) ; }
}; r et ur n ( Fi r ewor k) Col l ect i ons. mi n( l i st , c) ; } This code creat es and applies a Comparator obj ect t hat reflect s t he client 's specific need t o know t he cheapest firework. I f t he client want s, say, a list of fireworks sort ed by price, you can add yet anot her m et hod t o t he FireworkList class. I f you don't t hink t hat you can keep up wit h client dem ands, m ake your collect ion class a subclass of ArrayList or anot her class from java.util. The t radeoff is bet ween offering flexibilit y versus providing t ype safet y and applying dom ain- specific knowledge about what is im port ant about your collect ion. By creat ing t ype- specific collect ions, you can st rengt hen t he cont ract bet ween your code and your client 's code, reducing t he chance for defect s. I t erat ors are a nat ural ext ension t o collect ions, and it is usually wort hwhile t o accom pany a t ype- specific collect ion wit h a t ype- specific it erat or. Anot her occasion for supplying an it erat or m ay occur when you creat e a new collect ion t ype, such as a com posit e.
Iterating Over a Composite I n Challenge 5.3, from Chapt er 5, Com posit e, you developed recursive behaviors t hat execut e by t raversing a com posit e st ruct ure. I t erat ors int roduce a new challenge when working wit h com posit es, because you want t o ret urn cont rol t o a client as you t raverse each node. You m ight t hink t hat you would need a new it erat or class for each dom ain- specific com posit e you m odel. I n fact , you can design a fairly reusable com posit e it erat or, alt hough you have t o arrange for dom ain classes t o be able t o inst ant iat e your it erat or. You m ay also need t o handle com posit es t hat cont ain cycles. As an exam ple of a com posit e st ruct ure t hat you m ight want t o it erat e over, recall t he ProcessComponent hierarchy t hat Chapt er 5 int roduced. Figure 28.2 shows t he process com posit e hierarchy t hat Oozinoz uses for m odeling t he m anufact uring work flows t hat produce various t ypes of fireworks. Figure 28.2. Manu-facturing process flows at Oozinoz are composites.
Figure 28.3 shows t he st ruct ure of t he specific process flow t hat Oozinoz uses for m aking aerial shells. The code t hat creat es t he process obj ect m odel creat es ProcessComponent obj ect s in st at ic m et hods:
Figure 28.3. The process flow for making aerial shells is a cyclic composite. Each leaf node in this diagram is an instance of ProcessStep. The remaining nodes are instances of ProcessComposite.
package com. oozi noz. pr ocess; publ i c cl ass Shel l Pr ocess { pr ot ect ed st at i c Pr ocessSequence make; publ i c st at i c Pr ocessSequence make( ) { i f ( make == nul l ) { make = new Pr ocessSequence( "Make an aer i al shel l ") ; make. add( bui l dI nner Shel l ( ) ) ; make. add( i nspect ( ) ) ; make. add( r ewor kOr Fi ni sh( ) ) ; } r et ur n make; } pr ot ect ed st at i c Pr ocessSt ep bui l dI nner Shel l ( ) { r et ur n new Pr ocessSt ep( "Bui l d i nner shel l ") ; } pr ot ect ed st at i c Pr ocessSt ep i nspect ( ) {
r et ur n new Pr ocessSt ep( "I nspect ") ; } pr ot ect ed st at i c Pr ocessAl t er nat i on r ewor kOr Fi ni sh( ) { r et ur n new Pr ocessAl t er nat i on( "Rewor k i nner shel l , or compl et e shel l ", new Pr ocessComponent [ ] { r ewor k( ) , f i ni sh( ) } ) ; } pr ot ect ed st at i c Pr ocessSequence r ewor k( ) { r et ur n new Pr ocessSequence( "Rewor k", new Pr ocessComponent [ ] { di sassembl e( ) , make( ) } ) ; } pr ot ect ed st at i c Pr ocessSt ep di sassembl e( ) { r et ur n new Pr ocessSt ep( "Di sassembl e") ; } pr ot ect ed st at i c Pr ocessSt ep f i ni sh( ) { r et ur n new Pr ocessSt ep( "Fi ni sh: At t ach l i f t , i nser t f usi ng, wrap") ; } } Suppose t hat you want t o provide an it erat or for t he ProcessComponent hierarchy. Mechanics for it erat ing over a com posit e node will differ from t hose for it erat ing over a leaf node. These t wo t ypes of it erat ors will presum ably have som e behavior or at t ribut es in com m on, so you can creat e a hierarchy of t hese it erat ors, such as t he classes t hat Figure 28.4 shows. The classes in Figure 28.4 provide t he hasNext() and next() m et hods t hat you m ight expect from an it erat or, as well as a depth() m et hod t hat will t ell t he current dept h of an it erat or wit hin a com posit e. Figure 28.4. The ComponentIterator hierarchy anticipates different behavior for iterating over composites and leaves.
The CompositeIterator class needs t o creat e new " subit erat ors" as it t raverses over t he children of a com posit e obj ect . For each child, a CompositeIterator obj ect m ust creat e eit her a new leaf node it erat or or a new com posit e it erat or, depending on t he t ype of t he child. This is an opport unit y t o apply polym orphism , let t ing t he nodes decide which t ype of it erat or t o inst ant iat e. I f you have access t o t he collect ion classes t hat you want t o it erat e over, you can add an iterator() operat ion and let leaf and com posit e classes im plem ent t his operat ion different ly. I f you int roduce an Iterable int erface, such as Figure 28.5 shows, t he Composite-Iterator class can call t he iterator() m et hod for each of it s children. The iterator() m et hod produces an it erat or t hat t he com posit e m ust exhaust before get t ing an it erat or for t he next child. Figure 28.5. You can extend the ProcessComponent hierarchy to implement an Iterable interface, allowing CompositeIterator to build new iterators for each child of a composite node.
The ProcessComposite class can im plem ent iterator() t o ret urn an inst ance of CompositeIterator. The ProcessStep class can im plem ent t his m et hod t o ret urn an inst ance of LeafIterator.
CHALLENGE 28.2
What pat t ern are you applying if you let classes in t he ProcessComponent hierarchy im plem ent iterator() t o creat e inst ances of an appropriat e it erat or class?
The ComponentIterator hierarchy is som ewhat difficult t o develop, but it is highly reusable and easy t o apply. For exam ple, a short program can print t he nodes of a com posit e, using indent at ion t o show each node's dept h:
package com. oozi noz. appl i cat i ons; i mpor t com. oozi noz. pr ocess. *; i mpor t com. oozi noz. ut i l . *; publ i c cl ass ShowPr ocessI t er at i on { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Pr ocessComponent pc = Shel l Pr ocess. make( ) ; Component I t er at or i = pc. i t er at or ( ) ; whi l e ( i . hasNext ( ) ) { f or ( i nt j = 0; j < i . dept h( ) * 4; j ++)
{ Syst em. out . pr i nt ( ' ' ) ; } Syst em. out . pr i nt l n( i . next ( ) ) ; } } } This program print s out :
Make an aer i al shel l Bui l d i nner shel l I nspect Rewor k i nner shel l , or compl et e shel l Rewor k Di sassembl e Fi ni sh: At t ach l i f t , i nser t f usi ng, wr ap I t erat ing over a cyclic com posit e is not t he best way t o produce a t ext ual descript ion of t he com posit e. For a com posit e pret t y- print er, see page 346 in Chapt er 29, Visit or. The print out here dem onst rat es t hat t he code com plet es wit hout ent ering an infinit e loop, a t est am ent t o t he Component-Iterator hierarchy's abilit y t o it erat e over a cyclic com posit e. The out put also shows t hat t he it erat or correct ly t racks t he dept h of each node. To writ e t he code for t he com posit e it erat or, you can st art at t he t op of t he hierarchy, wit h t he ComponentIterator class. This class defines t he com m on operat ions for t he hierarchy and holds a node obj ect t hat represent s eit her a leaf or an int erior node. For exam ple, when it erat ing over a process com posit e, t he node obj ect will be an inst ance of eit her ProcessStep or ProcessComposite.
package com. oozi noz. ut i l ; i mpor t j ava. ut i l . *; publ i c abst r act cl ass Component I t er at or { pr ot ect ed Obj ect node; pr ot ect ed Set vi si t ed = new HashSet ( ) ;
publ i c Component I t er at or ( Obj ect node, Set vi si t ed) { t hi s. node = node; t hi s. vi si t ed = vi si t ed; } publ i c abst r act i nt dept h( ) ; publ i c abst r act bool ean hasNext ( ) ; publ i c abst r act Obj ect next ( ) ; } The ComponentIterator class includes a visited set t hat can keep t rack of whet her an it erat or has already t raversed a node in a com posit e. For exam ple, t he aerial shell process flow st art s wit h a com posit e make st ep t hat cont ains a rework st ep t hat cont ains, again, t he make st ep. When you writ e an it erat or for a com posit e t hat m ay cont ain cycles, you m ust ensure t hat you do not st ep int o an infinit e loop. As wit h com posit es, t he t erm inal class for a com posit e it erat or is com parat ively sim ple:
package com. oozi noz. ut i l ; i mpor t j ava. ut i l . *; publ i c cl ass Leaf I t er at or ext ends Component I t er at or { publ i c Leaf I t er at or ( Obj ect node, Set vi si t ed) { super ( node, vi si t ed) ; } publ i c i nt dept h( ) { r et ur n 0; } publ i c bool ean hasNext ( ) { r et ur n ! vi si t ed. cont ai ns( node) ; } publ i c Obj ect next ( ) { i f ( vi si t ed. cont ai ns( node) ) { r et ur n nul l ; } vi si t ed. add( node) ; r et ur n node; } } Not e t hat t he dept h of an it erat or is different from t he dept h of a node in a t ree. The dept h of an it erat or depends on t he dept h of it s subit erat or. Leaf it erat ors do not have subit erat ors, so t heir dept h is always 0. The value of depth() for a CompositeIterator obj ect is 1 plus t he dept h of it s subit erat or:
publ i c i nt dept h( ) { i f ( subi t er at or ! = nul l ) { r et ur n subi t er at or . dept h( ) + 1; } r et ur n 0; } The CompositeIterator has t o walk across t he children of t he com ponent —a ProcessComposite, say—over which it is it erat ing. The CompositeIterator class can use a children variable t o hold t his it erat or. Each child m ay be an arbit rarily deep com posit e, so t he Composite-Iterator class needs a subiterator obj ect t o t raverse t he com ponent t hat each child represent s:
package com. oozi noz. ut i l ; i mpor t j ava. ut i l . *; publ i c cl ass Composi t eI t er at or ext ends Component I t erat or {
pr ot ect ed I t er at or chi l dr en; pr ot ect ed Component I t er at or subi t er at or ; pr ot ect ed Obj ect peek;
publ i c Composi t eI t er at or ( Obj ect node, Li st component s, Set vi si t ed) { super ( node, vi si t ed) ; chi l dr en = component s. i t er at or ( ) ; } //. . . } The CompositeIterator uses a peek obj ect t o facilit at e t he im plem ent at ion of hasNext(). I t can be difficult t o t ell whet her a ( possibly cyclic) com posit e has a next node. A sim ple workaround is t o have hasNext() search for t he next value, and report whet her or not t his search was successful. The CompositeIterator class em ploys t his approach:
publ i c bool ean hasNext ( ) { i f ( peek == nul l ) { peek = next ( ) ; } r et ur n peek ! = nul l ; } publ i c Obj ect next ( ) { i f ( peek ! = nul l ) { Obj ect o = peek; peek = nul l ; r et ur n o; } i f ( ! vi si t ed. cont ai ns( node) ) { vi si t ed. add( node) ; r et ur n node; } r et ur n next Descendant ( ) ; } The next() m et hod checks whet her a peek value is loaded and t hen checks whet her it has report ed t he int erior node of t he com posit e t hat is being t raversed. I f not , t he next() m et hod t ransfers cont rol t o next-Descendant(). The nextDescendant() m et hod exhaust s t he com posit e beneat h t he current child, m oving ont o t he next child, if necessary:
pr ot ect ed Obj ect next Descendant ( ) {
whi l e ( t r ue) { i f ( subi t er at or ! = nul l ) { i f ( subi t er at or . hasNext ( ) ) { r et ur n subi t er at or . next ( ) ; } } i f ( ! chi l dr en. hasNext ( ) ) { r et ur n nul l ; } I t er abl e i = ( I t er abl e) chi l dr en. next ( ) ; i f ( ! vi si t ed. cont ai ns( i ) ) { subi t er at or = i . i t er at or ( vi si t ed) ; } } } There is a fair am ount of j udgm ent in how you design a com posit e it erat or. A crit ical decision in t he design j ust described is t hat we can expect t o be able t o add iterator() m et hods t o t he dom ain classes. You will have t o use a different approach if you can't t ouch t he classes over which you want t o it erat e. I n addit ion, you m ay find t hat you want t o int roduce ot her behaviors t o your com posit e it erat ors. Suppose t hat you decide t o add a setShowInterior() m et hod t o t he ComponentIterator class. This let s you it erat e over j ust t he leaves of a com posit e:
package com. oozi noz. appl i cat i ons; i mpor t com. oozi noz. ut i l . *; i mpor t com. oozi noz. pr ocess. *; publ i c cl ass ShowLeavesOnl y { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Pr ocessComponent pc = Shel l Pr ocess. make( ) ; Component I t er at or i = pc. i t er at or ( ) ; i . set ShowI nt er i or ( f al se) ; whi l e ( i . hasNext ( ) ) { Syst em. out . pr i nt l n( i . next ( ) ) ; } } } This program print s:
Bui l d i nner shel l I nspect Di sassembl e Fi ni sh: At t ach l i f t , i nser t f usi ng, wr ap.
CHALLENGE 28.3
To support t he setShowInterior() m et hod, suppose t hat you add a showInterior Boolean at t ribut e t o t he ComponentIterator class. What changes would you have t o m ake in t he CompositeIterator class so t hat next() will ret urn only leaf nodes if showInterior is false?
Thread-Safe Iterators When it ret urns t he next value in a collect ion t o a client , an it erat or also ret urns cont rol of t he program . This opens t he possibilit y t hat a client will t ake an act ion t hat will change t he collect ion over which you are it erat ing. Consider a program t hat can updat e a list in one t hread while anot her t hread is it erat ing over t he list :
package com. oozi noz. appl i cat i ons; i mpor t j ava. ut i l . *; publ i c cl ass ShowConcur r ent For i mpl ement s Runnabl e { pr i vat e Li st l i st ;
pr ot ect ed st at i c Li st upMachi neNames( ) { r et ur n new Ar r ayLi st ( Ar r ays. asLi st ( new St r i ng[ ] { "Mi xer 1201", "Shel l Assembl er 1301", "St ar Pr ess1401", "Unl oadBuf f er 1501" } ) ); } pr ot ect ed voi d go( ) { l i st = Col l ect i ons. synchr oni zedLi st ( upMachi neNames( ) ) ; f or ( i nt i = 0; i < l i st . si ze( ) ; i ++) { i f ( i == 1) { / / si mul at e wake- up new Thr ead( t hi s) . st ar t ( ) ; } Syst em. out . pr i nt l n( l i st . get ( i ) ) ; }
} publ i c voi d r un( ) { l i st . add( 0, "Fuser 1101") ; } publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { new ShowConcur r ent For ( ) . go( ) ; } } This program uses t he synchronizedList() m et hod of t he Collections ut ilit y class t o im prove t he t hread safet y of t he code. The result ing list guarant ees t hat m et hods t hat access t he list will execut e ent irely before anot her t hread can access t he list . This helps t o prevent a m et hod in one t hread from alt ering a collect ion while a m et hod in anot her t hread is t raversing t he collect ion. To sim ulat e a t hread waking up at an inopport une t im e, t he program launches a new t hread in t he m iddle of a for loop. Suppose t hat t he first t im e you run t his program , it print s:
Mi xer 1201 Shel l Assembl er 1301 St ar Pr ess1401 St ar Pr ess1401 Unl oadBuf f er 1501
CHALLENGE 28.4
Explain t he out put of t he ShowConcurrentFor program .
The program 's behavior st em s part ially from t he fact t hat it uses a for loop inst ead of using an it erat or and a while loop. The it erat ors t hat classes in t he java.util package supply provide fa il- fa st recognit ion of concurrent m odificat ion t o a collect ion. I f you m odify t he list bet ween calls t o an it erat or's next() m et hod, t he it erat or will t hrow a Concurrent-ModificationException. Suppose t hat you m odify t he program t o use an it erat or inst ead of a for loop.
package com. oozi noz. appl i cat i ons; i mpor t j ava. ut i l . *; publ i c cl ass ShowConcur r ent I t er at or i mpl ement s Runnabl e { pr i vat e Li st l i st ; publ i c st at i c Li st upMachi neNames( ) { / / . . . as bef or e } pr ot ect ed voi d go( ) { l i st = Col l ect i ons. synchr oni zedLi st (
upMachi neNames( ) ) ; I t er at or i = l i st . i t er at or ( ) ; i nt j = 0; whi l e ( i . hasNext ( ) ) { j ++; i f ( j == 1) { / / si mul at e wake- up new Thr ead( t hi s) . st ar t ( ) ; } Syst em. out . pr i nt l n( i . next ( ) ) ; } } publ i c voi d r un( ) { l i st . add( 0, "Fuser 1101") ; } publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { new ShowConcur r ent I t er at or ( ) . go( ) ; } } Wit h t his code in place, t he program crashes:
> j ava com. oozi noz. appl i cat i ons. ShowConcur r ent I t er at or Mi xer 1201 Shel l Assembl er 1301 St ar Pr ess1401 Unl oadBuf f er 1501 Except i on i n t hr ead "mai n" j ava. ut i l . Concur r ent Modi f i cat i onExcept i on ... The program crashes because t he it erat or obj ect det ect s t hat t he list has changed during t he it erat ion. You don't need t o creat e a new t hread t o show t his behavior. However, a m ult it hreaded applicat ion is m uch m ore likely t o accident ally m odify a list while an it erat or is t raversing it . I n a large applicat ion, you m ay have lit t le cont rol over, or even knowledge of, t he num ber of t hreads running in t he applicat ion and t heir various roles. I n such an applicat ion, you have t o avoid present ing a corrupt represent at ion of a collect ion, but you can't afford t o crash your applicat ion as a prevent ive m easure. Two m ain approaches provide safe it erat ion over a collect ion in a m ult it hreaded applicat ion. Bot h approaches involve t he use of an obj ect —som et im es called a m u t e x — t hat is shared by t hreads t hat vie for cont rol of t he obj ect 's m onit or. I n one approach, your design can require t hat all t hreads gain cont rol of t he m ut ex m onit or before accessing t he collect ion:
package com. oozi noz. appl i cat i ons; i mpor t j ava. ut i l . *; publ i c cl ass ShowConcur r ent Mut ex i mpl ement s Runnabl e {
pr i vat e Li st l i st ; pr i vat e Obj ect mut ex = new Obj ect ( ) ; pr ot ect ed st at i c Li st upMachi neNames( ) { / / . . . as bef or e } pr ot ect ed voi d go( ) { l i st = upMachi neNames( ) ; synchr oni zed ( mut ex) { I t er at or i = l i st . i t er at or ( ) ; i nt j = 0; whi l e ( i . hasNext ( ) ) { j ++; i f ( j == 1) { new Thr ead( t hi s) . st ar t ( ) ; } Syst em. out . pr i nt l n( i . next ( ) ) ; } } } publ i c voi d r un( ) { synchr oni zed ( mut ex) { l i st . add( 0, "Fuser 1101") ; } } publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { new ShowConcur r ent Mut ex( ) . go( ) ; } } This program will print t he original list , because t he run() logic m ust wait for t he m ut ex m onit or:
Mi xer 1201 Shel l Assembl er 1301 St ar Pr ess1401 Unl oadBuf f er 1501 This out put is correct , but you will usually not want ot her t hreads t o block while one t hread it erat es over a collect ion. I t m ay be m ore effect ive t o clone t he collect ion in a synchronized operat ion and t hen work on t he clone. The int erfaces List and Set are not cloneable, but t he im plem ent ing classes ArrayList and HashSet are.
Cloning a collect ion and it erat ing over t he clone m ay cause problem s. The clone() m et hods for ArrayList and HashSet produce a shallow copy: They m erely creat e a new collect ion t hat refers t o t he sam e obj ect s as t he original. Relying on a clone will fail if ot her t hreads can change t he underlying obj ect s in a way t hat int erferes wit h your m et hod. But in som e cases, t his risk is sm all. For exam ple, if you want t o display a list of m achine nam es, it m ay be unlikely or inconsequent ial whet her t he nam es change while your m et hod it erat es over a clone of t he list . The advant age of cloning t he list before t raversing it is speed. Cloning a collect ion is oft en m uch fast er t han wait ing for anot her m et hod t o finish operat ing on t he collect ion's cont ent s.
CHALLENGE 28.5
What changes would you m ake t o t he ShowConcurrentMutex program t o let t he prim ary t hread it erat e over a clone of t he collect ion, allowing t he second t hread t o work wit hout wait ing for access t o t he collect ion?
Summary The int ent of t he I TERATOR pat t ern is t o let a client access t he elem ent s of a collect ion sequent ially. The collect ion classes in t he Java class libraries offer rich support for operat ing on collect ions, including support for it erat ion. These it erat ors usually require a client t o cast t he ret urned obj ect s t o t he appropriat e t ype. You can st rengt hen your cont ract wit h client s by creat ing t ype- specific collect ions wit h t ype- specific it erat ors. I f you creat e a new t ype of collect ion, you will oft en want t o creat e an it erat or t o go wit h it . Dom ain- specific com posit es are a com m on exam ple of a new collect ion t ype. You can design a fairly generic it erat or for com posit es t hat you m ay be able t o apply against a variet y of com posit e hierarchies. When you inst ant iat e an it erat or, you should consider whet her t he collect ion can change while you are it erat ing over it . There is usually not m uch chance of t his in a singlet hreaded applicat ion. I n a m ult it hreaded applicat ion, you first have t o ensure t hat access t o a collect ion is synchronized. The Java class libraries provide good support for t his, but t hat support does not guarant ee safe it erat ion. To safely it erat e in a m ult it hreaded applicat ion, you can synchronize on t he m onit or of a m ut ex obj ect . You can eit her block out all access while it erat ing or block access briefly while you m ake a clone of t he collect ion. Wit h a proper design, you can provide t hread safet y and t ype safet y t o t he client s of your it erat or code.
Chapter 29. Visitor The ordinary way t o ext end a class hierarchy's behavior is t o add m et hods t hat provide t he behavior you need. I t m ay happen, t hough, t hat t he behavior you need is not consist ent wit h t he t hrust of t he exist ing obj ect m odel. I n ot her cases, t he developer of a hierarchy m ay have lit t le inform at ion about t he behaviors t hat lat er developers will need. I f t he developers who cont rol t he hierarchy code can't or won't change quickly enough t o m eet your needs, it m ay be im possible t o ext end t he hierarchy's behavior wit hout m odifying t he hierarchy's classes. But VI SI TOR let s a hierarchy developer build in support for t he prospect t hat anot her developer m ay want t o ext end t he behavior of t he hierarchy. The int ent of VI SI TOR is t o let you define a new operat ion for a hierarchy wit hout changing t he hierarchy classes.
Supporting Visitor Chapt er 5, Com posit e, int roduced t he MachineComponent hierarchy and suggest ed several possible behaviors for t his com posit e st ruct ure. Suppose t hat t he developers at Oozinoz have built som e of t hese behaviors int o t he hierarchy and have also applied VI SI TOR t o allow for lat er ext ensions. Figure 29.1 shows t he current st at e of t he hierarchy. Figure 29.1. The MachineComponent hierarchy designers planned for extension by building in support for visitors.
The figure doesn't explain how VI SI TOR works; t he next sect ion does t hat . The figure sim ply shows som e of t he groundwork t hat let s you apply VI SI TOR. One aspect of allowing for lat er ext ensions is t hat t he hierarchy classes m ake t heir at t ribut es accessible. I n part icular, t he getId() m et hod of t he MachineComponent class and t he getComponents() m et hod of t he MachineComposite class m ake it possible for visit ors t o access t hese crit ical at t ri- but es of hierarchy obj ect s. The m echanics of providing support for VI SI TOR also include adding an accept() operat ion t o t he hierarchy and adding a visit or int erface t hat developers can im plem ent t o creat e new ext ensions. The accept() m et hod in t he MachineComponent class is abst ract . Bot h subclasses im plem ent t his m et hod wit h exact ly t he sam e code:
publ i c voi d accept ( Machi neVi si t or v) { v. vi si t ( t hi s) ;
} You m ight t hink t hat because t his m et hod is ident ical in t he Machine and MachineComposite class, you could m ove t he m et hod up t o t he abst ract MachineComponent class. However, a com piler will see a difference in t hese t wo " ident ical" m et hods.
CHALLENGE 29.1
What difference will a Java com piler see bet ween t he accept() m et hods in t he Machine and MachineComposite classes?
The MachineVisitor int erface requires im plem ent ers t o define m et hods for visit ing m achines and m achine com posit es:
package com. oozi noz. machi ne; publ i c i nt er f ace Machi neVi si t or { voi d vi si t ( Machi ne m) ; voi d vi si t ( Machi neComposi t e mc) ; } The accept() m et hods in t he MachineComponent, t oget her wit h t he visit or int erface, invit e developers t o provide new operat ions t o t he hierarchy.
Extending with Visitor Suppose t hat you accept an assignm ent t o help bring up a new Oozinoz fact ory in Dublin, I reland. The developers in Dublin have creat ed an obj ect m odel of t he new fact ory's m achine com posit ion and have m ade t his m odel accessible as t he st at ic dublin() m et hod of t he OozinozFactory class. To display t his com posit e, t he developers creat ed a MachineTreeModel class t o adapt t he m odel's inform at ion t o a JTree obj ect 's needs. ( The code for t he MachineTreeModel class is in t he com.oozinoz.dublin package.) Displaying t he new fact ory's m achines requires building an inst ance of MachineTreeModel from t he fact ory com posit e and wrapping t his m odel in Swing com ponent s:
package com. oozi noz. appl i cat i ons; i mpor t j avax. swi ng. *; i mpor t j avax. swi ng. t r ee. *; i mpor t com. oozi noz. ui . Swi ngFacade; i mpor t com. oozi noz. dubl i n. Machi neTr eeModel ; i mpor t com. oozi noz. machi ne. Oozi nozFact or y; publ i c cl ass ShowMachi neTr eeModel { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Machi neTr eeModel mt m = new Machi neTr eeModel ( Oozi nozFact or y. dubl i n( ) ) ; J Tr ee t r ee = new J Tr ee( mt m) ; t r ee. set Font ( Swi ngFacade. get St andar dFont ( ) ) ; Swi ngFacade. l aunch( new J Scr ol l Pane( t r ee) , " A New Oozi noz Fact or y") ; }
} This code produces t he t ree browser t hat Figure 29.2 shows. Figure 29.2. This GUI application presents the composition of machines at the new factory in Dublin.
There are m any possible behaviors for t he m achine com posit e t hat you m ight desire. For exam ple, suppose t hat you need t o find a part icular m achine wit hin t he fact ory m odel. To add t his abilit y wit hout m odifying t he MachineComponent hierarchy, you can creat e a FindVisitor class, such as Figure 29.3 shows. Figure 29.3. The FindVisitor class effectively adds a find() operation to the MachineComponent hierarchy.
The visit() m et hods do not ret urn an obj ect , so t he FindVisitor class records t he st at us of a search in it s sought inst ance variable:
package com. oozi noz. dubl i n; i mpor t com. oozi noz. machi ne. *; i mpor t j ava. ut i l . *; publ i c cl ass Fi ndVi si t or i mpl ement s Machi neVi si t or { pr i vat e Machi neComponent sought ; pr i vat e i nt sought I d; publ i c Machi neComponent f i nd( Machi neComponent mc, i nt i d) { sought = nul l ; sought I d = i d; mc. accept ( t hi s) ; r et ur n sought ; } publ i c voi d vi si t ( Machi ne m)
{ i f ( sought == nul l && m. get I d( ) == sought I d) { sought = m; } } publ i c voi d vi si t ( Machi neComposi t e mc) { i f ( sought == nul l && mc. get I d( ) == sought I d) { sought = mc; r et ur n; } I t er at or i = mc. get Component s( ) . i t er at or ( ) ; whi l e ( sought == nul l && i . hasNext ( ) ) { ( ( Machi neComponent ) i . next ( ) ) . accept ( t hi s) ; } } } The visit() m et hods frequent ly check t he sought variable so t hat t he t ree t raversal will end as soon as t he desired com ponent is found.
CHALLENGE 29.2
Writ e a program t hat finds and print s out t he StarPress3404 obj ect wit hin t he inst ance of MachineComponent t hat OozinozFactory.dublin() ret urns.
Not e t hat t he find() m et hod does not worry about whet her t he m achine com ponent it receives as an argum ent is an inst ance of Machine or of MachineComposite. The find() m et hod sim ply calls accept(), which in t urn calls visit(). Figure 29.4 shows t his sequence of m et hod calls. Figure 29.4. A FindVisitor object calls a MachineComponent object's accept() method to determine which visit() method to execute.
The short t rip from t he FindVisitor obj ect t o t he MachineComponent obj ect and back again picks up t he t ype of t he MachineComponent obj ect , ensuring t hat t he right visit() m et hod of t he FindVisitor class execut es. This t echnique is called double dispa t ch. The FindVisitor obj ect dispat ches a call t o t he MachineComponent obj ect . The MachineComponent obj ect dispat ches back t o t he visit or, calling visit() and sending it self as an argum ent . This call will execut e eit her t he FindVisitor class's visit(:Machine) m et hod or it s visit(:MachineComposite) m et hod, depending on t he t ype of t he m achine com ponent . The double dispat ching in VI SI TOR let s you creat e visit or classes wit h m et hods t hat are specific t o t he various t ypes in t he visit ed hierarchy. As anot her exam ple, consider a visit or t hat finds all t he m achines—t he leaf nodes—in a m achine com ponent .
i mpor t com. oozi noz. machi ne. *; i mpor t j ava. ut i l . *; publ i c cl ass RakeVi si t or i mpl ement s Machi neVi si t or { pr i vat e Set l eaves; publ i c Set get Leaves( Machi neComponent mc) { l eaves = new HashSet ( ) ; mc. accept ( t hi s) ; r et ur n l eaves; } publ i c voi d vi si t ( Machi ne m) { ?? }
publ i c voi d vi si t ( Machi neComposi t e mc) { ?? } }
CHALLENGE 29.3
Com plet e t he code of t he RakeVisitor class t o collect t he leaves of a m achine com ponent .
A short program can find t he leaves of a m achine com ponent and print t hem out :
package com. oozi noz. appl i cat i ons; i mpor t com. oozi noz. machi ne. *; i mpor t j ava. i o. *; i mpor t com. oozi noz. i o. Wr apFi l t er ; i mpor t com. oozi noz. dubl i n. RakeVi si t or ; publ i c cl ass ShowRake { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) t hr ows I OExcept i on { Machi neComponent f = Oozi nozFact or y. dubl i n( ) ; Wr i t er out = new Pr i nt Wr i t er ( Syst em. out ) ; out = new Wr apFi l t er ( new Buf f er edWr i t er ( out ) , 60) ; out . wr i t e( new RakeVi si t or ( ) . get Leaves( f ) . t oSt r i ng( ) ) ; out . cl ose( ) ; } } This program uses a wrap filt er t o produce t he out put :
[ Mi xer 1201, St ar Pr ess3401, Mi xer 2202, St ar Pr ess3404, St ar Pr ess1401, Mi xer 3202, St ar Pr ess2402, Fuser 1101, Shel l Assembl er 3302, Mi xer 3204, Mi xer 2201, Fuser 2101, St ar Pr ess3403, Fuser 3102, Mi xer 3201, St ar Pr ess3402, St ar Pr ess2401, Shel l Assembl er 3301, Shel l Assembl er 1301, Mi xer 3203, Shel l Assembl er 2301, Fuser 3101] The FindVisitor and RakeVisitor classes each add a new behavior t o t he MachineComponent hierarchy, and t hese classes appear t o work correct ly. However, a danger in writ ing visit ors is t hat t hey require an underst anding of t he hierarchy t hat you are ext ending. A change in t he hierarchy m ay break your visit or, and you m ay m isunderst and t he m echanics of t he hierarchy init ially. I n part icular, you m ay have t o handle cycles if t he com posit e you are visit ing does not prevent t hem .
Visitor Cycles The ProcessComponent hierarchy t hat Oozinoz uses t o m odel process flows is anot her com posit e st ruct ure t hat can benefit from building in support for VI SI TOR. Unlike m achine com posit es, it is nat ural for process flows t o cont ain cycles, and visit ors m ust t ake care not t o cause infinit e loops while t raversing process com posit es. Figure 29.5 shows t he Process-Component hierarchy.
Figure 29.5. Like the MachineComponent hierarchy, the ProcessComponent hierarchy can build in support for Visitor.
Suppose t hat you want t o print out a process com ponent in a " pret t y," or indent ed, form at . I n Chapt er 28, I t erat or, you used an it erat or t o print out a process flow's st eps. This print out looked like:
Make an aer i al shel l Bui l d i nner shel l I nspect Rewor k i nner shel l , or compl et e shel l Rewor k Di sassembl e Fi ni sh: At t ach l i f t , i nser t f usi ng, wr ap You m ay recall t hat t he st ep aft er " Disassemble" is " Make an aerial shell." The print out doesn't show t his st ep, because t he it erat or sees t hat t he st ep has already appeared once. However, it would be m ore inform at ive t o show t he st ep nam e and t o indicat e t hat t he process ent ers a cycle at t his point . I t would also be helpful t o indicat e which com posit es are alt ernat ions as opposed t o sequences. To creat e a pret t y- print er for processes, you can creat e a visit or class t hat init ializes a StringBuffer obj ect and t hat adds t o t his buffer as t he visit or visit s t he nodes in a process com ponent . To indicat e t hat a com posit e st ep is an alt ernat ion, t he visit or can prepend a quest ion m ark ( ?) t o an alt ernat ion st ep's nam e. To indicat e t hat a st ep has occurred before, t he visit or can at t ach an ellipsis ( …) t o t he end of t he st ep's nam e. A process com ponent visit or has t o wat ch for cycles, but t his is easily achieved by using a Set obj ect t o keep t rack of t he nodes t he visit or has already seen:
package com. oozi noz. dubl i n; i mpor t j ava. ut i l . *; i mpor t com. oozi noz. pr ocess. *;
publ i c cl ass Pr et t yVi si t or i mpl ement s Pr ocessVi si t or { publ i c st at i c i nt I NDENT_DEPTH = 4; pr i vat e St r i ngBuf f er buf ; pr i vat e i nt dept h; pr i vat e Set vi si t ed;
publ i c St r i ngBuf f er get Pr et t y( Pr ocessComponent pc) { buf = new St r i ngBuf f er ( ) ; vi si t ed = new HashSet ( ) ; dept h = 0; pc. accept ( t hi s) ; r et ur n buf ; } publ i c voi d vi si t ( Pr ocessAl t er nat i on a) { pr i nt Composi t e( "?" + a. get Name( ) , a) ; } publ i c voi d vi si t ( Pr ocessSequence s) { pr i nt Composi t e( s. get Name( ) , s) ; } publ i c voi d vi si t ( Pr ocessSt ep s) { pr i nt Tag( s. get Name( ) ) ; } pr ot ect ed voi d pr i nt Composi t e( St r i ng t ag, Pr ocessComposi t e c) { i f ( vi si t ed. cont ai ns( c) ) { pr i nt Tag( t ag + ". . . ") ; } el se { vi si t ed. add( c) ; pr i nt Tag( t ag) ; accept Chi l dr en( c) ; } } pr ot ect ed voi d accept Chi l dr en( Pr ocessComposi t e c) { I t er at or i = c. get Subpr ocesses( ) . i t er at or ( ) ; dept h++;
whi l e ( i . hasNext ( ) ) { ( ( Pr ocessComponent ) i . next ( ) ) . accept ( t hi s) ; } dept h- - ; } pr ot ect ed voi d pr i nt Tag( St r i ng t ag) { f or ( i nt i = 0; i < dept h * I NDENT_DEPTH; i ++) { buf . append( " ") ; } buf . append( t ag) ; buf . append( "\ n") ; } } A short program can now pret t y- print a process flow:
package com. oozi noz. appl i cat i ons; i mpor t com. oozi noz. pr ocess. *; i mpor t com. oozi noz. dubl i n. *; publ i c cl ass ShowPr et t y { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Pr ocessComponent p = Shel l Pr ocess. make( ) ; Pr et t yVi si t or v = new Pr et t yVi si t or ( ) ; Syst em. out . pr i nt l n( v. get Pr et t y( p) ) ; } } Running t his program print s out :
Make an aer i al shel l Bui l d i nner shel l I nspect ?Rewor k i nner shel l , or compl et e shel l Rewor k Di sassembl e Make an aer i al shel l ... Fi ni sh: At t ach l i f t , i nser t f usi ng, wr ap The developers of t he ProcessComponent hierarchy are well aware of t he need t o avoid infinit e loops while t raversing process flows. As t he PrettyVisitor class shows, t he developers of t he visit or also have t o be aware of t he pot ent ial for cycles in process com ponent s. I t would help prevent errors if t he ProcessComponent developers could provide som e support of cycle m anagem ent as part of t heir support of VI SI TOR.
CHALLENGE 29.4
How can t he ProcessComponent developers include support of cycle m anagem ent in t he hierarchy's support for VI SI TOR?
Visitor Controversy VI SI TOR is a cont roversial pat t ern. Som e developers consist ent ly avoid applying it ; ot hers defend it s use and suggest ways t o st rengt hen it , alt hough t hese suggest ions usually add com plexit y. The fact is t hat m any design problem s t end t o accom pany t he VI SI TOR pat t ern. The fragilit y of VI SI TOR shows up in t he exam ples in t his chapt er. For inst ance, in t he MachineComponent hierarchy, t he hierarchy developers decided t o different iat e bet ween Machine nodes and MachineComposite nodes but not t o different iat e am ong Machine subclasses. I f you need t o dist inguish am ong t ypes of m achines in your visit or, you will have t o resort t o using instanceof or anot her t echnique t o t ell which t ype of m achine a visit() m et hod has received. You m ight argue t hat t he hierarchy developers should have included all m achine t ypes as well as a cat chall visit(:Machine) m et hod in t he visit or int erface. But new m achine t ypes com e along all t he t im e, so t his does not appear t o be any st urdier. Anot her exam ple of fragilit y showed up in t he ProcessComponent hierarchy. The developers of t he hierarchy are aware of t he danger of cycles t hat lurks wit hin process flow m odels. How can t he developers convey t heir concerns t o a visit or developer? This m ay expose t he fundam ent al problem wit h VI SI TOR: Ext ending a hierarchy's behavior usually requires som e expert knowledge of t he hierarchy's design. I f you lack t hat expert ise, you m ay st ep in a t rap, such as not avoiding cycles in a process flow. I f you do have expert knowledge of t he hierarchy's m echanics, you m ay build in dangerous dependencies t hat will break if t he hierarchy changes. For exam ple, not e t hat t he FindVisitor class depends on t he not ion t hat a m achine's I D num ber uniquely ident ifies t he m achine. I f t his principle changes so t hat ident it y depends on, say, t he cont aining fact ory plus t he m achine's I D, t he FindVisitor class m ay break wit h a com posit e t hat cont ains t wo fact ories. The division of expert ise and code cont rol can m ake VI SI TOR a dangerous pat t ern t o apply. The classic case in which VI SI TOR seem s t o work well wit hout creat ing downst ream problem s is language developm ent . When you develop a language parser, you m ay arrange for t he parser t o creat e an abst ract synt ax t ree, a st ruct ure t hat organizes t he input t ext according t o t he language's gram m ar. You m ay want t o develop a variet y of behaviors t o accom pany t hese t rees, and t he VI SI TOR pat t ern is an effect ive approach for allowing t his. Not e, t hough, t hat in t his classic case, t he visit ed hierarchy usually has lit t le or no behavior. Thus, all t he responsibilit y for behavior design lies wit h visit ors, avoiding t he split of responsibilit y t hat t his chapt er's exam ples m ust endure. Like any pat t ern, VI SI TOR is never necessary; if it were, it would aut om at ically appear everywhere it was needed. For VI SI TOR, t hough, alt ernat ives oft en provide a st urdier design.
CHALLENGE 29.5
Summary
List t wo alt ernat ives t o building VI SI TOR int o t he Oozinoz m achine and process hierarchies.
The VI SI TOR pat t ern let s you define a new operat ion for a hierarchy wit hout changing t he hierarchy classes. The m echanics for VI SI TOR include defining an int erface for visit ors and adding accept() m et hods in t he hierarchy t hat a visit or will call. The accept() m et hods dispat ch t heir calls back t o t he visit or in a double- dispat ching schem e. This schem e arranges for t he execut ion of a visit() m et hod t hat applies t o t he specific t ype of obj ect from t he hierarchy. A visit or developer m ust be aware of som e, if not all, of t he subt let ies in t he design of t he visit ed hierarchy. I n part icular, visit ors need t o beware of cycles t hat m ay occur in t he visit ed obj ect m odel. This t ype of difficult y leads som e developers t o eschew VI SI TOR, regularly applying alt ernat ives inst ead. Using VI SI TOR should probably be a t eam decision t hat depends on your m et hodology and t he specifics of your applicat ion.
Part VI: Appendixes Appendix A. Directions Appendix B. Solutions Appendix C. UML AT A GLANCE Glossary Bibliography
Appendix A. Directions I f you have read t he book up t o t his point , allow m e t o say, " Congrat ulat ions! " I f you have w orked t hrough all t he challenges, I salut e you! I feel confident t hat if you have read t his book and worked t he challenges, you have learned a lot about pat t erns. Now w here can you go from here?
Get the Most from This Book I f you have not worked t hrough t he challenges in t his book, you are not alone! We are all busy, and it is quit e t em pt ing t o t hink about a challenge m om ent arily and t o t hen glance at t he solut ion. That is cert ainly an ordinary experience, but you have t he pot ent ial t o becom e an ext raordinary developer. Go back and rework t he challenges, t urning t o t he solut ions only when you t hink you've got a correct answer or when you're com plet ely st um ped. Work t hrough t he challenges now ; don't kid yourself t hat you'll som ehow have m ore t im e lat er. By exercising your pat t erns knowledge on t hese challenges, you'll build t he confidence you need t o st art applying pat t erns in your work. I n addit ion t o working t he challenges in t his book, I suggest t hat you download t he code from www.oozinoz.com and ensure t hat you can repeat t he result s of t his book's exam ples on your own syst em . Knowing t hat you can get t he code t o run will give you m ore confidence t han sim ply working exam ples on paper. You m ay also want t o set up new challenges for yourself. Perhaps you will want t o com bine decorat or filt ers in a new way or im plem ent t he TableModel int erface wit h a class t hat shows dat a from a fam iliar dom ain.
As you build fluency wit h design pat t erns, you should st art t o see t hat you underst and classic exam ples of design pat t erns. You will also begin t o find ways t o incorporat e design pat t erns in your own code.
Understand the Classics Design pat t erns oft en m ake a design st ronger. This is not a new idea, so it is no surprise t hat m any design pat t erns are built int o t he Java class libraries. I f you can spot t he design pat t ern in a body of code you can grasp t he design yourself and com m unicat e it t o ot hers who underst and design pat t erns. For exam ple, if a developer underst ands t he DECORATOR pat t ern, it is m eaningful t o explain t hat Java st ream s are decorat ors. Here is a t est of your underst anding of som e of t he classic exam ples of design pat t erns t hat appear in Java. • • •
How do Swing listeners use the OBSERVER pattern? Why do menus often use the COMMAND pattern? How can you use the COMMAND pattern with Swing menus? Why are drivers a good example of the BRIDGE pattern? Is each particular driver an instance of the ADAPTER pattern?
•
What does it mean to say that Java streams use the DECORATOR pattern?
•
Why is the PROXY pattern fundamental to the design of RMI?
•
If sorting provides a good example of the TEMPLATE METHOD pattern, which step of the algorithm is left unspecified?
A good goal is t o be able t o answer t hese quest ions wit hout referring t o t his book. I t is also good exercise t o writ e down your answers and t o share your answers wit h a colleague.
Weave Patterns into Your Code A prim ary purpose for learning design pat t erns is t o becom e a bet t er developer. Think about how t o use pat t erns in t he code base you work wit h m ost oft en. Here, you have t wo choices: Apply design pat t erns as you add new code, or apply design pat t erns t hrough refact oring. I f part of your code is com plex and difficult t o m aint ain, you m ay be able t o im prove it by refact oring t he code and using a design pat t ern. Before diving int o such a proj ect , m ake sure t hat you have a cust om er for t he result . Also, before you change t he code, be sure t o creat e an aut om at ed t est suit e for t he code t hat you are refact oring. Now, suppose t hat you underst and t he pat t erns you have st udied and are det erm ined t o use t hem carefully and appropriat ely. How do you find an opport unit y? Som e opport unit ies arise fairly frequent ly. I f you're looking for a chance t o apply design pat t erns, consider t he following. •
Does your code base have any complex code that deals with the state of a system or the state of the application user? If so, you may be able to improve the code by applying the STATE pattern.
•
•
•
•
•
Does your code combine the selection of a strategy with the execution of that strategy? If it does, you may be able to make the code better by using the STRATEGY pattern. Does your customer or analyst supply you with flowcharts that translate into code that is difficult to comprehend? If so, you can apply the INTERPRETER pattern, letting each node of the flowchart become an instance of a class in the interpreter hierarchy. You can thus provide a direct translation from the flowchart to the code. Does your code contain a weak composite that doesn't allow children to be composites themselves? You may be able to strengthen such code with the COMPOSITE pattern. Have you encountered relational-integrity errors in your object model? You may be able to prevent them by applying the MEDIATOR pattern to centralize the modeling of object relations. Does your code have places where clients are using information from a service to decide which class to instantiate? You may be able to improve and to simplify such code by applying the FACTORY METHOD pattern.
By learning design pat t erns, you have developed a rich vocabulary of design ideas. I f you are on t he lookout for opport unit ies, it probably won't be long before you find a design t hat you can im prove by applying a pat t ern.
Keep Learning Som ehow, you had t he opport unit y, drive, and am bit ion t o acquire and t o read t his book. All I can say is, keep it up! I t hink t he best advice for developers is t o decide how m any hours a w eek you w ant t o spend on your career. Take five hours off t he t op and pay yourself first . Spend t hat t im e away from t he office, reading books and m agazines or writ ing soft ware relat ed t o any t opic t hat int erest s you. Make t his pract ice as regular as your office hours. Treat t his aspect of your career seriously, and you'll becom e a m uch bet t er developer, and you'll probably find t hat you enj oy your j ob m ore. Now, suppose t hat you have given yourself plent y of t im e and sim ply need t o set your direct ion. Before learning m ore about pat t erns, you m ay want t o m ake sure t hat you underst and t he basics. I f you haven't read The Java™ Program m ing Language ( Arnold and Gosling 1998) or The Unified Modeling Language User Guide ( Booch, Rum baugh, and Jacobson 1999) , let m e highly recom m end t hem . I f you want t o learn m ore about pat t erns, you have a lot of choices. For a walkt hrough of realist ic exam ples of applying design pat t erns, I recom m end Pat t ern Hat ching ( Vlissides 1998) . I f you want t o add t o your pat t erns vocabulary, m y experience is t hat m ost shops would benefit from having a local expert on concurrency pat t erns. To learn about t his im port ant and oft en neglect ed aspect of developm ent , I recom m end reading Concurrent Program m ing in Java™ ( Lea 2000) . Of t he m any direct ions t hat you can go, t he m ost im port ant pract ice is t o keep going. Make learning a part of your career, and pursue t he t opics t hat int erest you m ost . Think of how st rong you can becom e as a developer, and becom e t hat st rong. Keep up t he good work!
Appendix B. Solutions Introducing Interfaces (Chapter 2) Adapter (Chapter 3) Facade (Chapter 4) Composite (Chapter 5) Bridge (Chapter 6) Introducing Responsibility (Chapter 7) Singleton (Chapter 8) Observer (Chapter 9) Mediator (Chapter 10) Proxy (Chapter 11) Chain of Responsibility (Chapter 12) Flyweight (Chapter 13) Introducing Construction (Chapter 14) Builder (Chapter 15) Factory Method (Chapter 16) Abstract Factory (Chapter 17) Prototype (Chapter 18) Memento (Chapter 19) Introducing Operations (Chapter 20) Template Method (Chapter 21) State (Chapter 22) Strategy (Chapter 23) Command (Chapter 24) Interpreter (Chapter 25) Introducing Extensions (Chapter 26) Decorator (Chapter 27)
Iterator (Chapter 28) Visitor (Chapter 29)
Introducing Interfaces (Chapter 2)
SOLUTION 2.1
An abst ract class wit h no nonabst ract m et hods is sim ilar t o an int erface in t erm s of it s ut ilit y. However, not e t he following. • •
•
• •
•
•
SOLUTION 2.2
A class can implement any number of interfaces but can subclass at most one abstract class. An abstract class can have nonabstract methods, which are usually instances of the TEMPLATE METHOD pattern. All the methods of an interface are abstract, whether or not this declaration is explicit. An abstract class can declare instance variables that its subclasses inherit. An interface cannot declare instance variables, although it can establish static final fields. An abstract class can define constructors; an interface cannot. An abstract class's visibility can be public, protected, private, or none (package); an interface's visibility must be public or none (package). An abstract class can have methods whose visibility is protected, private, or none (package); every method in an interface must be public. An abstract class inherits from Object, including such methods as clone() and equals().
A. True. Interface methods are always abstract, whether or not they declare it. B. True. Interface methods are also always public, whether or not they declare it. C
False! An interface's visibility may be limited to the
package in which the interface resides. The RocketSim interface does not declare itself to be public, so it is package protected. Classes outside com.oozinoz.sim cannot access or implement this interface. D. True. For example, the List and the Set interfaces both extend the Collection interface in java.util. E. False. An interface with no methods is a marker interface. Sometimes, a method high in a class hierarchy, such as Object.clone(), is not appropriate for every subclass. You can create a marker interface that requires subclasses to opt in or to opt out of participation in such a method. The clone() method on Object requires subclasses to opt in, declaring that they implement the Cloneable marker interface. F. False. An interface cannot declare instance fields, although it can create constants by declaring fields that are static and final. G. False. It might be a good idea, but there is no way for a Java interface to require implementing classes to provide any particular constructors.
SOLUTION 2.3
An int erface does not define a cont ract bet ween t he caller and t he im plem ent ing class when t he int erface is used t o regist er for not ificat ion. I n t his case, t he im plem ent ing class m ay need t o t ake an act ion in response t o a m et hod call, but it is generally not perform ing a service for t he caller. For exam ple, a class t hat im plem ent s t he MouseMotionListener int erface from java.awt.event m ust im plem ent t he m et hods:
publ i c voi d mouseDr agged( MouseEvent e) publ i c voi d mouseMoved( MouseEvent e) The presence of t hese m et hods does not im ply t hat t he m et hods will t ake an act ion when a client calls t hem . I n fact , it would not be uncom m on t o react t o t he mouseDragged() m et hod but t o ignore t o calls t o t he mouseMoved() m et hod.
SOLUTION 2.4
A class t hat im plem ent s WindowListener m ust im plem ent all t he m et hods t he int erface declares even if t he class will ignore t hese m et hods when
called. The WindowAdapter class ignores all t he m et hods in WindowList ener. This let s a subclass im plem ent only t he m et hods t hat it want s t o react t o, as Figure B.1 shows. The anonym ous class in Figure B.1 can react t o only t he windowClosing() event , wit hout having t o im plem ent dum m y m et hods for t he ot her m et hods specified in t he WindowListener int erface. Figure B.1. An anonymous subclass of WindowAdapter can provide a Window-Listener object that ignores all events except a window closing.
Here is a m et hod t hat accept s a JFrame obj ect and arranges for t he Java virt ual m achine t o t erm inat e when t he user closes t he fram e's window:
publ i c st at i c voi d l i st en( Fr ame f ) { f . addWi ndowLi st ener
( new Wi ndowAdapt er ( ) { publ i c voi d wi ndowCl osi ng( Wi ndowEvent e) { Syst em. exi t ( 0) ; } } ); } This code regist ers wit h t he fram e an inst ance of an anonym ous WindowAdapter subclass t hat overrides t he windowClosing() m et hod. The value of t he WindowAdapter class is t hat it m akes it easy t o creat e a subclass t hat react s t o only t he relevant window event s.
SOLUTION 2.5
The advant age of placing const ant s in an int erface is t hat a class t hat im plem ent s t he int erface can use t he const ant s as if it had inherit ed t hem from a superclass. To m ake t he classificat ion const ant s available t o a class, declare t hat t he class im plem ent s t he ClassificationConstants int erface. Then you can change secureOrder() t o:
publ i c voi d secur eOr der ( Fi r ewor k f / *, et c. */ ) { //. . . i f ( f . cl assi f i cat i on == DI SPLAY) { / / i ssue war ni ng } el se { / / pr oceed } } The difference is t hat you can now refer t o t he const ant as DISPLAY inst ead of Classification.DISPLAY.
Adapter (Chapter 3)
SOLUTION 3.1
Your solut ion should look som et hing like Figure B.2. Figure B 2
This ShellAsRocket class relies on fireworks physics that
can model a shell's apogee and "thrust" from its muzzle velocity.
SOLUTION 3.2
One set of solut ions for t he m issing m et hods is:
package com. oozi noz. appl i cat i ons; i mpor t j avax. swi ng. t abl e. *; i mpor t com. oozi noz. f i r ewor ks. *; publ i c cl ass Rocket Tabl e ext ends Abst r act Tabl eModel { pr ot ect ed Rocket [ ] r ocket s; pr ot ect ed St r i ng[ ] col umnNames = new St r i ng[ ] { "Name", "Pr i ce", "Apogee" } ; publ i c Rocket Tabl e( Rocket [ ] r ocket s) { t hi s. r ocket s = r ocket s; } publ i c i nt get Col umnCount ( ) { r et ur n col umnNames. l engt h; } publ i c St r i ng get Col umnName( i nt i ) { r et ur n col umnNames[ i ] ; }
publ i c i nt get RowCount ( ) { r et ur n r ocket s. l engt h; } publ i c Obj ect get Val ueAt ( i nt r ow, i nt col ) { swi t ch ( col ) { case 0 : r et ur n r ocket s[ r ow] . get Name( ) ; case 1 : r et ur n new Doubl e( r ocket s[ r ow] . get Pr i ce( ) ) ; case 2 : r et ur n new Doubl e( r ocket s[ r ow] . get Apogee( ) . get Magni t ude( ) ) ; def aul t : r et ur n nul l ; } } } The TableModel int erface provides a good exam ple of t he power of planning ahead for adapt at ion. This int erface, along wit h it s part ial im plem ent at ion by AbstractTableModel, reduces t he work of showing dom ain obj ect s in a st andard GUI t able. Your solut ion should show how easy it is t o adapt when adapt at ion is support ed wit h an int erface.
SOLUTION 3.3
SOLUTION 3.4
The problem wit h subclassing PrintStream and overriding only one of it s m any m et hods is t hat in a lat er version, t he client code m ight st art using m et hods t hat you don't support . The com piler won't det ect a problem , because t he client st ill needs a PrintStream obj ect and you will st ill be supplying one.
You should ask t he client code developers t o specify a Java int erface, such as:
publ i c i nt er f ace MessageChannel { voi d pr i nt ( Obj ect o) ; } The client developers also need t o change t he setOutput() m et hod t o accept an inst ance of MessageChannel inst ead of an inst ance of PrintSt I n t his scenario you can define M Ad t t o subclass
Text-Area and im plem ent MessageChannel. The advant age is t hat t he client developers can't change t he calls t he applicat ion m akes wit hout changing t he int erface. I f t he client developers change t he int erface, t he worst possible result is t hat your code won't com pile. This is great ly preferable t o t he earlier design, in which a change on t he client side w ould not be not iceable unt il runt im e.
SOLUTION 3.5
I f you have t he opport unit y, t ry t o convince a colleague of your view. •
•
Sam ple argum ent : The int ent of t he Adapt er pat t ern is t o convert t he int erface of a class int o anot her int erface t hat client s expect . The MessageAdapter class achieves t his, let t ing a TextArea obj ect pose as a PrintStream obj ect . Thus, MessageAdapter is a valid, if dangerous, exam ple of Adapt er. On t he ot her side: The MessageAdapter class is sim ply delegat ing a call t o a PrintStream obj ect . We don't want t o consider t hat every inst ance of delegat ion is an inst ance of Adapt er. The Adapt er pat t ern's int ent is t o t ranslat e an int erface. This exam ple has no int erface, j ust a part icular m et hod call t hat we need t o override.
I f you can clearly art iculat e your own views and list en t hought fully t o opposit ion at t he sam e t im e, you win.
SOLUTION 3.6
•
•
One argum ent : When t he user clicks t he m ouse, I need t o t ranslat e, or adapt , t he result ing Swing call int o an appropriat e act ion. I n ot her words, when I need t o adapt GUI event s t o m y applicat ion's int erface, I use Swing adapt er classes. I am t ranslat ing from one int erface t o anot her, fulfilling t he int ent of t he Adapt er pat t ern. A count erargum ent : The " adapt er" classes in Swing are st ubs. They don't t ranslat e or adapt anyt hing. You subclass t hese classes, overriding t he m et hods you need t o do som et hing. I t is t hus your m et hods and your class t hat form an exam ple of ADAPTER.
Facade (Chapter 4)
SOLUTION 4.1
Your diagram should look som et hing like Figure B.3. Figure B.3. This diagram shows the flight path application factored into one class for calculating a flight path one for displaying a flight path
and one that simplifies use of the Swing libraries.
Not e t hat all t he m et hods of SwingFacade are st at ic m et hods. Does your solut ion m ake t hese m et hods st at ic? I f not , why not ?
SOLUTION 4.2
Som e differences t o not e bet ween dem os and facades follow. • • •
A demo is usually a standalone application, whereas a facade is usually not. A demo usually includes sample data; a facade does not. A facade is usually configurable, whereas a demo is not.
•
A facade is intended for reuse, whereas a demo is not.
•
A facade is intended for use in production; a demo is
not.
SOLUTION 4.3
The JOptionPane class is one of t he few exam ples of a facade in t he Java class libraries. I t is product ion wort hy, configurable, and designed for reuse. Above all else, t he JOptionPane class fulfills t he int ent of t he FACADE pat t ern by providing a sim ple int erface t hat m akes it easy t o use t he JDialog class. You m ight argue t hat a facade sim plifies a " subsyst em " and t hat t he solit ary JDialog class does not qualify as a subsyst em . But it is exact ly t he richness of t his class's feat ures t hat m akes a facade valuable. Sun Microsyst em s bundles m any dem os in wit h t he JDK. However, t hese classes are never part of t he Java class libraries. That is, t hese classes do not appear in packages wit h a java prefix. A facade m ay belong on t he Java class libraries, but dem os do not . The JOptionPane has dozens of st at ic m et hods t hat effect ively m ake it a ut ilit y. St rict ly speaking, t hough, it does not m eet t he UML definit ion t hat requires a ut ilit y t o possess solely st at ic m et hods.
SOLUTION 4.4
Here are a few reasonable—but opposing—views about t he paucit y of facades in t he Java class libraries. •
•
•
As a Java developer, you are well advised to develop a thorough knowledge of the Java class libraries. Facades necessarily limit the way you might apply any system. They would be a distraction and a potentially misleading element of the class libraries in which they might appear. A facade lies somewhere between the richness of a toolkit and the specificity of a particular application. To create a facade requires some notion of the types of applications the facade will support. This predictability is impossible, given the huge and diverse audience of the Java class libraries. The scarcity of facades in the Java class libraries is a weakness. Adding more facades would be a big help.
Composite (Chapter 5)
SOLUTION 5.1
SOLUTION 5.2
One answer is t hat Design Pat t erns ( Gam m a et al. 1995) shows Component as an abst ract class, so Java developers m ay im plem ent it t hat way wit hout considering t he use of a Java int erface. Anot her answer is t hat t he Component superclass oft en has fields and m et hods t hat it s subclasses can share. For exam ple, t he Component class m ay have a name inst ance variable and a concret e toString() m et hod t hat uses it .
For t he Machine class, getMachineCount() should be som et hing like:
publ i c i nt get Machi neCount ( ) { r et ur n 1; } The class diagram shows t hat MachineComposite uses a List obj ect t o t rack it s com ponent s. To count t he m achines in a com posit e, you m ight writ e:
publ i c i nt get Machi neCount ( ) { i nt count = 0; I t er at or i = component s. i t er at or ( ) ; whi l e ( i . hasNext ( ) ) { Machi neComponent mc = ( Machi neComponent ) i . next ( ) ; count += mc. get Machi neCount ( ) ; } r et ur n count ; }
SOLUTION 5.3
Definition Return the sum of the counts for each component in components. Machine Return 1. isCompletelyUp() MachineComposite Return if all components are "completely up." Machine Return true if this machine is up. stopAll() MachineComposite Tell all components to "stop all." Method
Class
getMachineCount() MachineComposite
Machine getOwners()
getMaterial()
SOLUTION 5.4
Stop this machine. MachineComposite Create a set, not a list, add the owners of all components, and return the set. Machine Return this machine's owners. MachineComposite Return a collection of all the material on components. Machine Return the material that is on this machine
The program print s out t he num ber 4. Only t hree m achines are in t he plant fact ory, but m achine m is count ed by bot h plant and bay. Bot h of t hese obj ect s cont ain list s of m achine com ponent s t hat refer t o m achine m. The result s could be worse. I f, say, an engineer adds t he plant obj ect as a com ponent of t he bay com posit e, a call t o getMachineCount() will ent er an infinit e loop.
SOLUTION 5.5
A reasonable im plem ent at ion of MachineComposite.isTree() is:
pr ot ect ed bool ean i sTr ee( Set vi si t ed) { vi si t ed. add( t hi s) ; I t er at or i = component s. i t er at or ( ) ; whi l e ( i . hasNext ( ) ) { Machi neComponent c = ( Machi neComponent ) i . next ( ) ; i f ( vi si t ed. cont ai ns( c) || ! c. i sTr ee( vi si t ed) ) { r et ur n f al se; } } r et ur n t r ue; }
SOLUTION 5.6
Your solut ion should show t he links in Figure B.4. Figure B 4
The cycle in processing aerial shells shows up as a cycle in
this object diagram.
Bridge (Chapter 6)
SOLUTION 6.1
Figure B.5 shows a com plet ed sequence diagram t hat illust rat es t he norm al st eps in an applicat ion t hat uses a JDBC driver. Figure B.5. A typical JDBC application creates a Statement object and uses it to execute one or more queries.
SOLUTION 6.2
You have m any choices about how t o illust rat e t he class relat ionships in t his exam ple. Figure B.6 shows one answer. Your figure should show t he relat ionship bet ween JDBCAdapter and Statement. I n UML, t he open arrowhead on t he associat ion bet ween JDBCAdapter and Statement indicat es t hat a JDBCAdapter obj ect can use or navigat e t o a Statement obj ect , but a Statement obj ect cannot use a JDBCAdapter obj ect . Figure B.6. An OzJDBCAdapter object will throw a NoQuery exception if the adapter is asked for data before a query has made data available.
Not e t hat you cannot show t he class t hat im plem ent s Statement, as t he JDBC archit ect ure isolat es you from knowing how a driver writ er has nam ed t his class. An illust rat ion of t he OzJDBCAdapter class should show t he m et hods it overrides. Figure B.6 also indicat es t hat OzJDBCAdapter obj ect s use a hasQuery Boolean t o keep t rack of whet her t hey have received a query.
SOLUTION 6.3
Figure B.7 shows a solut ion. Figure B.7. The abstraction—a hierarchy of types of controllers—is separated from implementations of the abstract driver object that the abstraction uses.
SOLUTION 6.4
Figure B.8 shows one solut ion. Figure B.8. The line forms at the rear: Objects enqueue at the tail of the list; dequeued objects come from the "head" of the list, at index 0.
Introducing Responsibility (Chapter 7)
SOLUTION 7.1
Som e problem s wit h t he given diagram follow: • •
•
• •
•
•
•
• •
The Rocket.thrust() method returns a Rocket instead of a number or a physical quantity. The LiquidRocket class has a getLocation() method, although nothing in the diagram or in the problem domain suggests that we model rockets as having a location. Even if we did, there is no reason for liquid-fueled rockets to have a location, whereas other Rocket objects do not. The isLiquid() method may be an acceptable alternative to using the instanceof operator, but then we'd expect the superclass to also have an isLiquid() method that would return false. CheapRockets
is plural, although class names are conventionally singular. We could model cheapness with attributes alone, so there is no justification for creating a class just for cheap rockets. The CheapRockets class also introduces a factoring that conflicts with factoring the rocket model as liquid or solid. For example, it is not clear how to model a cheap liquid rocket. The CheapRockets class implements Runnable, although this interface has nothing to do with cheap rocket objects from the problem domain. The model shows that Firework is a subclass of LiquidRocket, declaring that all fireworks are liquid rockets, which is false. The model shows a direct relation between reservations and types of firework, although no such relation exists in the problem domain. The Reservation class has its own copy of city, which it should get by delegation to a Location object. A CheapRocket is composed of Runnable objects, which is simply bizarre.
SOLUTION 7.2
The value of t his challenge is not t o get t he right answer but rat her t o exercise your t hinking about what m akes up a good class. Consider whet her your definit ion addresses t he following point s. •
•
• •
•
• •
SOLUTION 7.3
A nuts-and-bolts description of a class is, "A class is a named collection of fields that hold data values and methods that operate on those values" (Flanagan 1999b, p. 61). A class establishes a collection of fields: that is, it defines the attributes of an object. The attribute types are other classes, primitive data types, such as boolean and int, or interfaces. A class designer should be able to justify how a class's attributes are related. The name of a class should reflect the meaning of the class both as a collection of attributes and with respect to the class's behavior. A class must support all the behaviors it defines, as well as all those in superclasses, and all methods in interfaces that the class implements. (A decision to not support a superclass or an interface method is occasionally justifiable.) A class should have a justifiable relationship to its superclass. The name of each of a class's methods should be a good commentary on what the method does.
At least t wo earlier challenges have involved cases in which t he effect of calling a m et hod is not com plet ely predict able from t he m et hod nam e. •
Challenge 2.1 on page 14 asks about methods that do not imply responsibility on the part of the implementing class to take action or to return a value. As the solution (page 359) suggests, when a class registers for notification calls, such as mouseDragged(), the methods do not imply an obligation to the caller.
•
Chapter 6, Bridge, gives several examples showing that the effect of calling an abstraction's methods depends on which driver or implementation is in place.
The effect s of calling a m et hod are purposely unpredict able when t he m et hod is part of an abst ract ion and when t he m et hod exist s t o receive not ificat ion. I n ot her cases, a m et hod wit h unforeseeable result s m ay sim ply be m isnam ed. The Java class libraries cont ain ot her exam ples of when you m ight not predict a m et hod's behavior from knowing it s nam e. I n part icular, > Chapt er 18, Prot ot ype, asks you t o t ake a hard look at t he behavior of Object.clone() in Challenge 18.3 on page 186.
SOLUTION 7.4
To keep anot her developer from burrowing int o your package wit h subclasses t hat m anipulat e protected m em bers of your classes, you m ight • • •
Offer to add the function the developer needs Modify your instance variables with private visibility and use get- methods within your own package Declare classes like SolidRocket to be final, so that they cannot be subclassed
Singleton (Chapter 8)
SOLUTION 8.1
SOLUTION 8.2
To prevent ot her developers from inst ant iat ing your class, creat e a single const ruct or wit h privat e visibilit y. Not e t hat if you creat e ot her, nonprivat e const ruct ors or creat e no const ruct ors at all, ot her developers will likely be able t o reinst ant iat e your class.
As Design Pat t erns says, " You m ight not have enough inform at ion t o inst ant iat e every singlet on at st at ic init ializat ion t im e. A singlet on m ight require values t hat are com put ed lat er in t he program 's execut ion" ( p. 130) . When a Factory singlet on is born, for exam ple, it m ight have t o est ablish connect ions wit h m achine drivers, t o set up planners, and t o init ialize a sim ulat ion of it self.
SOLUTION 8.3
Your solut ion should elim inat e t he possibilit y of confusion t hat can occur when t wo t hreads call t he recordWipMove() m et hod at approxim at ely t he sam e t im e:
publ i c voi d r ecor dWi pMove( ) { synchr oni zed ( cl assLock) { wi pMoves++; } } I s it possible t hat a t hread m ight act ivat e in t he m iddle of an increm ent operat ion? Even if you're cert ain t hat t he answer is no, it 's a good policy t o carefully rest rict access t o a singlet on's dat a in a m ult it hreaded applicat ion. Mult it hreaded applicat ions oft en fail because t heir developers do not underst and t he m echanics of synchronizat ion or do not foresee subt le problem s t hat can occur. I f you are working wit h a m ult it hreaded Java applicat ion, I heart ily recom m end fam iliarizing yourself wit h t he inform at ion in Concurrent Program m ing in Java™ ( Lea 2000) .
SOLUTION 8.4
OurBiggestRocket
This class has an inappropriate name. You should model attributes, such as "biggest," with attributes, not with class names. If a developer must sustain this class, perhaps it is a singleton. TopSalesAssociate This class has the same problem as OurBiggestRocket. Math This class is a utility, with all static methods and no instances. It is not a singleton. System This is a utility. PrintStream Although the System.out object is a PrintStream object with unique responsibilities, it is not a unique instance of PrintStream, which is not a singleton class. PrintSpooler: If you really have only one printer in your company, PrintSpooler might be a singleton. PrinterManager: At Oozinoz, you have a lot of printers, and you can look up their addresses through the PrinterManager singleton.
Observer (Chapter 9)
SOLUTION 9.1
One solut ion is:
publ i c J Sl i der sl i der ( ) { i f ( sl i der == nul l ) { sl i der = new J Sl i der ( ) ; sl i der Max = sl i der . get Maxi mum( ) ; sl i der Mi n = sl i der . get Mi ni mum( ) ; sl i der . addChangeLi st ener ( t hi s) ; sl i der . set Val ue( sl i der . get Mi ni mum( ) ) ; } r et ur n sl i der ; } publ i c voi d st at eChanged( ChangeEvent e) { doubl e val = sl i der . get Val ue( ) ; doubl e t p = ( val - sl i der Mi n) / ( sl i der Max - sl i der Mi n) ; bur nPanel ( ) . set TPeak( t p) ; t hr ust Panel ( ) . set TPeak( t p) ; val ueLabel ( ) . set Text ( "" + t p) ; }
SOLUTION 9.2
One solut ion is shown in Figure B.9. To allow a label t o regist er for slider event s, t he design in Figure B.9 creat es a subclass of JLabel t hat im plem ent s ChangeListener. The new design let s t he com ponent s t hat depend on t he slider regist er t heir int erest and updat e t hem selves. This is arguably an im provem ent , but we will refact or t he design again, t o a m odel/ view/ cont rol archit ect ure. Figure B.9. In this design, components that depend on the slider implement ChangeListener so that they can register for slider events.
SOLUTION 9.3
One solut ion is:
package com. oozi noz. appl i cat i ons; i mpor t j avax. swi ng. *; i mpor t j ava. ut i l . *; i mpor t com. oozi noz. bal l i st i cs. Tpeak; publ i c cl ass Bal l i st i csLabel ext ends J Label i mpl ement s Obser ver { publ i c Bal l i st i csLabel ( Tpeak t Peak) { t Peak. addObser ver ( t hi s) ; } publ i c voi d updat e( Obser vabl e o, Obj ect ar g) { set Text ( "" + ( ( Tpeak) o) . get Val ue( ) ) ; r epai nt ( ) ; } } Not e t hat t his solut ion places t he Tpeak class in t he ballistics package and t he BallisticsLabel class in t he applications package.
SOLUTION 9.4
A solut ion is:
publ i c J Sl i der sl i der ( ) { i f ( sl i der == nul l ) { sl i der = new J Sl i der ( ) ; sl i der Max = sl i der . get Maxi mum( ) ; sl i der Mi n = sl i der . get Mi ni mum( ) ; sl i der . addChangeLi st ener ( new ChangeLi st ener ( ) { publ i c voi d st at eChanged( ChangeEvent e) { t Peak. set Val ue( ( sl i der . get Val ue( ) - sl i der Mi n) / ( sl i der Max - sl i der Mi n) ) ; } } ); sl i der . set Val ue( sl i der . get Mi ni mum( ) ) ; } r et ur n sl i der ; }
SOLUTION 9.5
Figure B.10 shows t he calls t hat flow when a user m oves t he slider in t he ballist ics applicat ion. Figure B.10. MVC causes the path of change to loop through a business layer.
SOLUTION 9.6
Your diagram should look som et hing like Figure B.11. Not e t hat you can apply t he sam e design wit h Observer and Observable. The key t o t he design is t hat t he int erest ing class Tpeak m akes it self observable by m aint aining an obj ect wit h good list ening skills. Figure B.11. The Tpeak class can add in listening behaviors by delegating listening-oriented calls to a PropertyChange- Support object.
Mediator (Chapter 10)
SOLUTION 10.1
Figure B.12 shows a solut ion. The arrowheads in t he link bet ween PlaceATub and PlaceATubMediator em phasize t wo- way navigabilit y bet ween t hese classes. A PlaceATub obj ect has a m ediat or t hat it regist ers for event s:
publ i c J Text Fi el d t ext Fi el d( ) { i f ( t ext Fi el d == nul l ) { t ext Fi el d = new J Text Fi el d( ) ; t ext Fi el d. set Font ( f ont ( ) ) ; t ext Fi el d. addAct i onLi st ener ( medi at or ( ) ) ; t ext Fi el d. addFocusLi st ener ( medi at or ( ) ) ; } r et ur n t ext Fi el d; } Figure B.12. The PlaceATub class handles component building, and the PlaceATubMediator class handles events.
The const ruct or for PlaceATubMediator accept s a PlaceATub obj ect t o which it passes back inform at ion:
publ i c voi d f ocusGai ned( FocusEvent e) { gui . t ext Fi el d( ) . sel ect Al l ( ) ; }
SOLUTION 10.2
Your solut ion should look som et hing like Figure B.13. Not e t hat alt hough t his diagram shows only a Tub dom ain obj ect , t he m ediat or m ust also updat e t he affect ed m achines when a user m oves a t ub. Figure B.13. When you move event handling into a mediator class, components register a mediator object for their events When a user clicks a button
the event goes to the mediator, where domain-specific knowledge about the action to perform resides.
SOLUTION 10.3
Figure B.14 shows an updat ed obj ect diagram . The problem t hat t he developer's code int roduces is t hat StarPress-2402 st ill t hinks t hat it has t ub T308. I n a relat ional t able, changing t he m achine at t ribut e of a row aut om at ically rem oves t he t ub from t he prior m achine. This aut om at ed rem oval does not occur when t he relat ion is dispersed across a dist ribut ed obj ect m odel. The proper m odeling of t he t ub/ m achine relat ion requires special logic t hat you can rem ove t o a separat e m ediat or obj ect . Figure B.14. Two machines think that they contain tub T308. The object model accepts a situation that neither a relational table nor reality will allow.
SOLUTION 10.4
The cont ent s of Tub.java should look som et hing like:
package com. oozi noz. chemi cal ; i mpor t com. oozi noz. machi ne. *; publ i c cl ass Tub { pr ot ect ed St r i ng i d; pr ot ect ed Medi at or medi at or ; publ i c Tub( St r i ng i d, Medi at or medi at or ) { t hi s. i d = i d; } publ i c Machi ne get Machi ne( ) { r et ur n medi at or . get Machi ne( t hi s) ; } publ i c voi d set Machi ne( Machi ne machi ne) { medi at or . set ( t hi s, machi ne) ; } publ i c St r i ng t oSt r i ng( ) { r et ur n i d; } }
SOLUTION 10.5
• • • • • •
The FACADE pattern may help to refactor a large application. The BRIDGE pattern moves abstract operations to an interface. The OBSERVER pattern may appear as you refactor code to support an MVC architecture. The FLYWEIGHT pattern extricates the immutable part of an object so that this part can be shared. The BUILDER pattern moves the construction logic for an object outside the class to instantiate. The FACTORY METHOD pattern lets you reduce the amount of responsibility in a class hierarchy by moving an
aspect of behavior to a parallel hierarchy. •
The STATE and STRATEGY patterns let you move statespecific and strategy-specific behavior into separate classes.
Proxy (Chapter 11)
SOLUTION 11.1
The im age- display m et hods of ImageIconProxy forward t heir calls t o t he current im age:
publ i c i nt get I conHei ght ( ) { r et ur n cur r ent . get I conHei ght ( ) ; } publ i c i nt get I conWi dt h( ) { r et ur n cur r ent . get I conWi dt h( ) ; } publ i c synchr oni zed voi d pai nt I con( Component c, Gr aphi cs g, i nt x, i nt y) { cur r ent . pai nt I con( c, g, x, y) ; }
SOLUTION 11.2
The load() m et hod set s t he im age t o Loading…, whereas t he run() m et hod, execut ing in a separat e t hread, loads t he desired im age:
publ i c voi d l oad( J Fr ame cal l backFr ame) { t hi s. cal l backFr ame = cal l backFr ame; set I mage( LOADI NG. get I mage( ) ) ; cal l backFr ame. r epai nt ( ) ; new Thr ead( t hi s) . st ar t ( ) ; } publ i c voi d r un( ) { set I mage( new I mageI con( f i l ename) . get I mage( ) ) ; cal l backFr ame. pack( ) ;
}
SOLUTION 11.3
As t he class diagram shows, a RocketImpl const ruct or accept s a price and an apogee:
Rocket bi ggi e = new Rocket I mpl ( 29. 95, 820) ; You could declare biggie t o be of t ype RocketImpl. However, what is im port ant about biggie is t hat it fulfills t he Rocket int erface t hat a client will look for.
SOLUTION 11.4
The com plet ed diagram should appear as in Figure B.15. A legit im at e but less inform at ive labeling would nam e bot h receivers of getApogee() as being of t ype Rocket. I n fact , bot h t he server and t he client program s refer t o t hese obj ect s as inst ances of t he Rocket int erface. Figure B.15. A proxy forwards a client's calls so that to the client, the remote object appears as if it were local.
Chain of Responsibility (Chapter 12)
SOLUTION 12.1
Two possible answers follow. •
Method lookup searches across a well-defined series of
classes. The CHAIN OF RESPONSIBILITY pattern directs the search for a responsible method to occur across a series of objects. •
SOLUTION 12.2
The mechanics of method lookup are part of the Java language specification, whereas CHAIN OF RESPONSIBILITY is under your control as a developer.
Your diagram should looking sim ilar t o Figure B.16. Wit h t his design, any client of any sim ulat ed it em can sim ply ask t he it em for t he responsible engineer. As t he next challenge shows, t he code for t he various im plem ent at ions of getResponsible() get s sim pler, t oo. Figure B.16. Every SimlulatedItem object can respond to a getResponsible() call. Internally, a Simulated-Item object may forward the request to its parent.
SOLUTION 12.3
A. A MachineComponent object may have an explicitly assigned responsible person. If it doesn't, it passes the request to its parent: B. publ i c Engi neer get Responsi bl e( ) C. { D. i f ( r esponsi bl e ! = nul l ) E. { F. r et ur n r esponsi bl e; G. } H. i f ( par ent ! = nul l ) I. { J. r et ur n par ent . get Responsi bl e( ) ; K. } L. r et ur n nul l ; M. }
N. The code for Tool.getResponsibility() reflects the statement that "tools are always assigned to tool carts": O. publ i c Engi neer get Responsi bl e( ) P. { Q. r et ur n t ool Car t . get Responsi bl e( ) ; R. }
S. The ToolCart code reflects the statement that "tool carts have a responsible engineer": T. publ i c Engi neer get Responsi bl e( ) U. { V. r et ur n r esponsi bl e; W. }
SOLUTION 12.4
Your solut ion should look som et hing like Figure B.17. Your const ruct ors should allow Machine and MachineComposite obj ect s t o be inst ant iat ed wit h or wit hout an assigned engineer. Whenever a MachineComponent obj ect does not have an assigned engineer, it can get a responsible engineer from it s parent . Figure B.17. The constructors in the MachineComponent hierarchy support the rules that a MachineRoot object must have a responsible engineer, and every MachineComponent object except a root must have a parent.
SOLUTION 12.5
Two exam ples of when CHAI N OF REPONSI BI LI TY m ight apply t o obj ect s t hat do not form a com posit e are •
•
A chain of on-call engineers that follows a standard rotation. If the primary on-call engineer does not answer a production support page in a specific amount of time, the notification system pages the next engineer in the chain. When users enter such information as the date of an event, a chain of parsers can take turns trying to decode the user's text.
Flyweight (Chapter 13)
SOLUTION 13.1
SOLUTION 13.2
The BorderFactory class in javax.swing is a good exam ple of FLYWEI GHT. As it s class com m ent says, " Wherever possible, t his fact ory will hand out references t o shared Border inst ances." When a Border obj ect can be safely shared, BorderFactory will ret urn t he sam e border m ore t han once. For exam ple, every call t o createEmptyBorder() will ret urn t he sam e, em pt y border.
•
•
SOLUTION 13.3
An argument for the immutability of strings: In practice, strings are frequently shared between clients and thus frequently the crux of defects that emerge when one client inadvertently affects another. For example, a method that returns a customer's name as a string will typically retain its reference to the name. If the client, say, uppercases the string to use it in a hash table, the Customer object's name would change as well, if not for the immutability of strings. In Java, you can produce an uppercase version of a string, but this must be a new object, not an altered version of the initial string. The immutability of strings makes them safe to share among multiple clients. Against: The immutability of strings protects us from certain errors but at a heavy price. First, developers are cut off from any ability to change a string, regardless of how we might justify this need. Second, adding special rules to a language makes the language more difficult to learn and to use. Java is far, far more difficult to learn than the equally powerful Smalltalk language. Finally, no computer language can keep me from making errors. I'd be much better off if you let me learn the language quickly, so that I have time to also learn how to set up and use a testing framework.
The nam e, sym bol, and at om ic weight of chem icals do not change, so you can refact or t he Substance class as Figure B.18 shows. The refact oring m oves t he im m ut able, or int rinsic, aspect of a part icular subst ance int o t he Ch i l class Not e t hat t he Ch i l class's im m ut abilit y relies on
t he fact t hat it s at t ribut es cannot change once t he Chemical obj ect is creat ed. The class's im m ut abilit y also relies on t he im m ut abilit y of it s at t ribut es. I f Chemical cont ained, say, a List obj ect , a chem ical could change if it s list changed. Figure B.18. This diagram shows the immutable part of the Substance class extracted into a separate class, Chemical.
Client s of t he Chemical class can pass in t he m ass of a bat ch of a given chem ical t o det erm ine it s m olalit y. The code for getMoles() in t he Chemical class accept s a m ass param et er:
publ i c doubl e get Mol es( doubl e gr ams) { r et ur n gr ams / at omi cWei ght ; } The Substance version of getMoles() applies t he m ass of t he chem ical bat ch it represent s:
publ i c doubl e get Mol es( ) { r et ur n chemi cal . get Mol es( gr ams) ; }
SOLUTION 13.4
SOLUTION 13.5
To prevent developers from inst ant iat ing t he Chemical class t hem selves, you can place Chemical and ChemicalFactory in t he sam e package and give t he Chemical class's const ruct or package visibilit y. Not e t hat if you m ake it s const ruct or privat e, even ChemicalFactory won't be able t o creat e Chemical obj ect s.
Cont rolling t he inst ant iat ion of Chemical obj ect s by applying an inner class is a m ore com plex but m ore t horough approach. The result ing code will look som et hing like t his:
package com. oozi noz. chemi cal ; i mpor t j ava. ut i l . *; publ i c cl ass Chemi cal Fact or y
{ pr i vat e st at i c Map chemi cal s = new HashMap( ) ; st at i c { chemi cal s. put ( "car bon", new Chemi cal I mpl ( "Car bon", "C", 12) ) ; //. . . } pr i vat e st at i c cl ass Chemi cal I mpl i mpl ement s Chemi cal { pr i vat e St r i ng name; pr i vat e St r i ng symbol ; pr i vat e doubl e at omi cWei ght ; // pr i vat e Chemi cal I mpl ( St r i ng name, St r i ng symbol , doubl e at omi cWei ght ) { t hi s. name = name; t hi s. symbol = symbol ; t hi s. at omi cWei ght = at omi cWei ght ; } publ i c doubl e get Mol es( doubl e gr ams) { r et ur n gr ams / at omi cWei ght ; } publ i c St r i ng get Name( ) { r et ur n name; } publ i c St r i ng get Symbol ( ) { r et ur n symbol ; } publ i c doubl e get At omi cWei ght ( ) { r et ur n at omi cWei ght ; } } publ i c st at i c Chemi cal get Chemi cal ( St r i ng name) { r et ur n ( Chemi cal ) chemi cal s. get ( name. t oLower Case( ) ) ;
} }
Introducing Construction (Chapter 14)
SOLUTION 14.1
Special rules regarding const ruct ors include t he following. • •
• •
You must use new to invoke a constructor. Constructor names must match the class name. This results in the oddity that, unlike most methods, constructor names normally begin with an uppercase letter. If you do not supply a constructor for a class, Java will provide a default. You can invoke other constructors with this() and super() so long as this invocation is the first statement in a constructor.
There are also several rules governing t he order of field init ializat ion and special rules for t he inst ant iat ion of arrays.
SOLUTION 14.2
A const ruct or t hat allows Fountain t o com pile is:
publ i c Fount ai n( St r i ng name) { super ( name) ; } This const ruct or allows t he Fountain class t o com pile, as it invokes t he superclass's const ruct or in it s first st at em ent . We have also correct ed an im port ant inconsist ency in our classes. By int roducing a Firework const ruct or t hat accept s a nam e, we est ablished t hat all Firework obj ect s m ust have a nam e. Logically, all fount ains are fireworks, and all Fountain obj ect s m ust have a nam e if all Firework obj ect s have a nam e. The const ruct or in t his solut ion respect s t he design of it s superclass, accept ing a nam e and passing it up so t hat t he superclass can uniform ly apply it .
SOLUTION 14.3
One way t o provide default values for an obj ect 's at t ribut es is t o provide a const ruct or t hat om it s t he at t ribut e. That const ruct or can fill in t he default value, passing cont rol t o anot her const ruct or w it h this():
publ i c Fi r ewor k( St r i ng name) { t hi s( name, DI SPLAY) ; } publ i c Fi r ewor k( St r i ng name, Cl assi f i cat i on cl assi f i cat i on) { t hi s. name = name; t hi s. cl assi f i cat i on = cl assi f i cat i on; } You can also define default values in t he declarat ions of t he at t ribut es for t he class. For exam ple, you could declare t he classificat ion variable wit h:
pr ot ect ed Cl assi f i cat i on cl assi f i cat i on = DI SPLAY; Est ablishing default s wit h const ruct ors has t he advant age of placing all an obj ect 's init ializat ion in one place—it s const ruct ors—rat her t han part ly in it s const ruct ors and part ly in it s at t ribut e declarat ions.
SOLUTION 14.4
Here are a pair of Fountain const ruct ors t hat m irror t he const ruct ors in t he Firework superclass:
publ i c Fount ai n( St r i ng name) { super ( name) ; } publ i c Fount ai n( St r i ng name, Cl assi f i cat i on cl assi f i cat i on) { super ( name, cl assi f i cat i on) ; } The first const ruct or could invoke this() wit h t he nam e param et er and wit h a default classificat ion. The code here has t he advant age of leaving t he decision of which classificat ion is t he default in one place, t he superclass. On t he ot her hand, you m ay have picked up on t he fact t hat fount ains are always " consum er" fireworks in t he Unit ed St at es, and you m ay have built t hat int o your solut ion, which is arguably an im provem ent on t he code shown here.
Builder (Chapter 15)
SOLUTION 15.1
SOLUTION 15.2
The point of m aking t he Reservation const ruct or protected is t hat it lim it s t he abilit y t o inst ant iat e your class. You want t o com pel ot her developers t o creat e Reservation obj ect s wit h a builder rat her t han wit h t he Reservation const ruct or. You could m ake t he const ruct or package prot ect ed, which would ensure t hat only ot her classes wit hin your package can const ruct Reservation obj ect s. Making t he const ruct or protected leaves open t he possibilit y of subclassing Reservation from anot her package. Not e t hat m aking t he const ruct or's visibilit y private would break t he builder classes' abilit y t o inst ant iat e t he Reservation class.
The build() m et hod of UnforgivingBuilder t hrows an except ion if any at t ribut e is invalid and ot herwise ret urns a valid Reservation obj ect . Here is one im plem ent at ion:
publ i c Reser vat i on bui l d( ) t hr ows Bui l der Except i on { i f ( dat e == nul l ) { t hr ow new Bui l der Except i on( "Val i d dat e not f ound") ; } i f ( ci t y == nul l ) { t hr ow new Bui l der Except i on( "Val i d ci t y not f ound") ; } i f ( headcount < MI NHEAD) { t hr ow new Bui l der Except i on( "Mi ni mum headcount i s " + MI NHEAD) ; } i f ( dol l ar sPer Head * headcount < MI NTOTAL) { t hr ow new Bui l der Except i on( "Mi ni mum t ot al cost i s " + MI NTOTAL) ; } r et ur n new Reser vat i on( dat e, headcount , ci t y, dol l ar sPer Head, hasSi t e) ; } Not e t hat UnforgivingBuilder " inherit s" const ant s, such as MINHEAD, from t he ReservationConstants int erface.
SOLUTION 15.3
Here is one solut ion for a builder t hat creat es a reasonable count eroffer from an incom plet e request :
publ i c Reser vat i on bui l d( ) t hr ows Bui l der Except i on { bool ean noHeadcount = ( headcount == 0) ; bool ean noDol l ar sPer Head = ( dol l ar sPer Head == 0. 0) ; bool ean count er of f er = noHeadcount || noDol l ar sPer Head; i f ( noHeadcount && noDol l ar sPer Head) { headcount = MI NHEAD; dol l ar sPer Head = MI NTOTAL / headcount ; } el se i f ( noHeadcount ) { headcount = ( i nt ) Mat h. cei l ( MI NTOTAL / dol l ar sPer Head) ; headcount = Mat h. max( headcount , MI NHEAD) ; } el se i f ( noDol l ar sPer Head) { dol l ar sPer Head = MI NTOTAL / headcount ; } check( ) ; r et ur n new Reser vat i on( dat e, headcount , ci t y, dol l ar sPer Head, hasSi t e, count er of f er ) ; } This solut ion fact ors out a check() m et hod t hat verifies t he validit y of a pot ent ially m odified request :
pr ot ect ed voi d check( ) t hr ows Bui l der Except i on { i f ( dat e == nul l ) { t hr ow new Bui l der Except i on( "Val i d dat e not f ound") ; } i f ( ci t y == nul l ) { t hr ow new Bui l der Except i on( "Val i d ci t y not f ound") ; } i f ( headcount < MI NHEAD)
{ t hr ow new Bui l der Except i on( "Mi ni mum headcount i s " + MI NHEAD) ; } i f ( dol l ar sPer Head * headcount < MI NTOTAL) { t hr ow new Bui l der Except i on( "Mi ni mum t ot al cost i s " + MI NTOTAL) ; } }
Factory Method (Chapter 16)
SOLUTION 16.1
Many answers are possible, but toString() is probably t he m ost com m only used m et hod t hat creat es a new obj ect . For exam ple, t he following code creat es a new String obj ect :
St r i ng s = new j ava. ut i l . Dat e( ) . t oSt r i ng( ) ; The creat ion of st rings oft en happens behind t he scenes. Consider:
Syst em. out . pr i nt ( new Dat e( ) ) ; This m et hod creat es a String obj ect from t he Date obj ect , ult im at ely by calling t he toString() m et hod of class Date. I t is int erest ing t o walk t hrough t his in debugger, t o see t he det ails of what happens wit hin a com m on print() m et hod. Anot her m et hod t hat creat es a new obj ect is clone(). You m ay discover ot her m et hods t hat are " fact ories" for creat ing obj ect s wit hout necessarily exem plifying t he FACTORY METHOD pat t ern.
SOLUTION 16.2
SOLUTION 16.3
The int ent of t he FACTORY METHOD pat t ern is t o let an obj ect provider det erm ine which class t o inst ant iat e when creat ing an obj ect . By com parison, client s of BorderFactory know exact ly what obj ect t ypes t hey're get t ing. The pat t ern at play in BorderFactory is FLYWEI GHT, in t hat BorderFactory uses sharing t o efficient ly support large num bers of borders. The BorderFactory class isolat es client s from m anaging t he reuse of obj ect s, whereas FACTORY METHOD isolat es client s from knowing which class t o inst ant iat e.
A good answer, perhaps, is t hat you do not need t o know what class of obj ect an it t () m et hod ret urns What is im port ant is t hat you
know t he int erface t hat t he it erat or support s, which let s you walk t hrough t he elem ent s of a collect ion. However, if you m ust know t he class, you can print out it s nam e, wit h a line like:
Syst em. out . pr i nt l n( i . get Cl ass( ) . get Name( ) ) ; I n t he version of Java used in t his book, t his st at em ent print s out :
j ava. ut i l . Abst r act Li st $I t r The class Itr is an inner class of AbstractList. You should probably never see t his class in your work wit h Java.
SOLUTION 16.4
Figure B.19 shows t hat t he t wo credit check classes im plem ent t he CreditCheck int erface. The fact ory class provides a m et hod t hat ret urns a CreditCheck obj ect . The client t hat calls createCreditCheck() does not know t he precise class of t he obj ect it receives. Figure B.19. Two classes implement the CreditCheck interface. The decision of which class to instantiate lies with the service provider rather than with the client that needs a credit check.
The createCreditCheck() m et hod is a st at ic m et hod, so client s need not inst ant iat e t he CreditCheckFactory class in order t o get a CreditCheck obj ect . You can m ake t his class abst ract or give it a privat e const ruct or if you want t o act ively prevent ot her developers from inst ant iat ing it .
SOLUTION 16.5
I f you t ake t he leap of fait h t hat t he st at ic m et hod isAgencyUp() accurat ely reflect s realit y, t he code for createCreditCheck() is sim ple:
publ i c st at i c Cr edi t Check cr eat eCr edi t Check( ) { i f ( i sAgencyUp( ) ) { r et ur n new Cr edi t CheckOnl i ne( ) ;
} el se { r et ur n new Cr edi t CheckOf f l i ne( ) ; } }
SOLUTION 16.6
Figure B.20 shows a reasonable diagram for t he Machine/ MachinePlanner parallel hierarchy. This diagram indicat es t hat subclasses of MachinePlanner m ust im plem ent t he m et hod getAvailable(). The diagram also depict s MachinePlanner as an abst ract class. Alt ernat ively, you m ight m ake Ma-chinePlanner concret e, let t ing it t ake t he role of t he BasicPlanner class. Figure B.20. Planning logic is now in a separate hierarchy. Each subclass of Machine knows which planner to instantiate in response to a createPlanner() call.
The diagram also indicat es t hat classes in t he MachinePlanner hierarchy accept a Machine obj ect in t heir const ruct ors. This allow s t he planner t o int errogat e t he obj ect it is planning for, regarding such crit eria as t he m achine's locat ion and t he am ount of m at erial it is current ly processing.
SOLUTION 16.7
A createPlanner() m et hod for t he Fuser class m ight look like:
publ i c Machi nePl anner cr eat ePl anner ( ) { r et ur n new Basi cPl anner ( t hi s) ; } For StarPress, t he createPlanner() m et hod m ight be:
publ i c Machi nePl anner cr eat ePl anner ( ) { r et ur n new St ar Pr essPl anner ( t hi s) ; } These m et hods shows t he FACTORY METHOD pat t ern at work. When we need a planner obj ect , we call t he createPlanner() m essage of t he m achine we want t o plan for. The specific planner t hat we receive depends on t he m achine.
Abstract Factory (Chapter 17)
SOLUTION 17.1
Figure B.21 shows a solut ion t o providing concret e com.oozinoz.check.canada classes t hat im plem ent t he int erfaces and abst ract class in com.ooz-inoz.check. One subt let y is t hat you need only one concret e class for offline credit checking, because at Oozinoz, offline checking is t he sam e for calls from t he Unit ed St at es and Canada. Figure B.21. The canada package provides a family of concrete classes that conduct a variety of checks for Canadian calls.
SOLUTION 17.2
Here is one solut ion:
package com. oozi noz. check. canada; i mpor t com. oozi noz. check. *; publ i c cl ass CheckFact or yCanada ext ends CheckFact or y { publ i c Bi l l i ngCheck cr eat eBi l l i ngCheck( ) { r et ur n new Bi l l i ngCheckCanada( ) ; } publ i c Cr edi t Check cr eat eCr edi t Check( ) { i f ( i sAgencyUp( ) ) { r et ur n new Cr edi t CheckCanadaOnl i ne( ) ;
} el se { r et ur n new Cr edi t CheckOf f l i ne( ) ; } } publ i c Shi ppi ngCheck cr eat eShi ppi ngCheck( ) { r et ur n new Shi ppi ngCheckCanada( ) ; } } Your solut ion should •
Have create- methods for each interface in com.oozinoz.check
• •
SOLUTION 17.3
Have the proper interface for the return type of each create- method Return a CreditCheckOffline object if the agency is down
A version of CheckFactory.java t hat m akes U.S. and Canada fact ories readily available is:
package com. oozi noz. check; i mpor t com. oozi noz. check. us. CheckFact or yUS; i mpor t com. oozi noz. check. canada. CheckFact or yCanada; publ i c abst r act cl ass CheckFact or y { publ i c st at i c f i nal CheckFact or y US = new CheckFact or yUS( ) ; publ i c st at i c f i nal CheckFact or y CANADA = new CheckFact or yCanada( ) ; publ i c abst r act Bi l l i ngCheck cr eat eBi l l i ngCheck( ) ; publ i c abst r act Cr edi t Check cr eat eCr edi t Check( ) ; publ i c abst r act Shi ppi ngCheck cr eat eShi ppi ngCheck( ) ; } A user of your soft ware can im port com.oozinoz.check.* and t hen refer t o whichever fact ory he or she needs For exam ple applicat ion code
m ight cont ain:
// . . . CheckFact or y f = CheckFact or y. CANADA; Bi l l i ngCheck b = f . cr eat eBi l l i ngCheck( ) ; // . . .
SOLUTION 17.4
•
•
SOLUTION 17.5
An exam ple j ust ificat ion: Placing count ry- specific classes in separat e packages helps our developers at Oozinoz t o organize our soft ware and our developm ent effort s. By placing t he classes for each count ry in a separat e package, we keep count ry- specific packages independent . We can be confident , for exam ple, t hat U.S.- specific classes have no im pact on Canada- specific classes. We can also add support for new count ries easily. For exam ple, when we st art doing business wit h Mexico, we can creat e a new package t hat provides t he check services we need in a way t hat m akes sense in t hat count ry. This has t he furt her advant age of let t ing us assign t he mexico package t o a developer who has expert ise in working wit h services and dat a from Mexico. An argum ent against : Alt hough t his separat ion is nice in t heory, it 's overwrought in pract ice. I 'd rat her have one package wit h all t he classes in it , at least unt il we expand t o nine or t en count ries. I n m y developm ent environm ent , t his let s m e see and com pare all t he classes of a given t ype at once.
Reasons for specifying different look- and- feel st andards include t he following. • •
•
We might want to have one set of components for new users and one for power users. We might have different display types, such as handheld and full-screen displays. For a smaller display, our standard components should typically be smaller. Even the difference between, say, a 15-inch and a 19-inch display might warrant different standard components. We might want to distinguish major release versions of our application suite by including changes in look-andfeel. For example, we might change the standard background color from ivory to canary yellow when a user upgrades to version 3.0 of our application.In the call center, it will be instantly recognizable which version of our application a user is running.
•
SOLUTION 17.6
For beta software, we might want to, say, put a red stripe around the outermost panel.
Figure B.22 shows a sam ple solut ion: Figure B.22. The abstract ComponentKit class establishes the palette of components that concrete subclasses must supply.
The word k it , a synonym for an abst ract fact ory, is oft en used when t he fact ory creat es user int erface com ponent s. For t his reason, Figure B.22 nam es t he com ponent fact ory ComponentKit.
Prototype (Chapter 18)
SOLUTION 18.1
Figure B.23 shows a solut ion. Not e t hat t his class is concret e. You will soon est ablish inst ances of t his class as com plet e user int erface fact ories. Figure B.23. A UIKit object is a wellspring of GUI components that have a consistent look.
SOLUTION 18.2
Here is an exam ple solut ion:
publ i c st at i c UI Ki t handhel d( ) { UI Ki t k = new UI Ki t ( ) ; Font f = new Font ( "Di al og", Font . PLAI N, 8) ; k. but t on. set Font ( f ) ; k. t ext Ar ea. set Font ( f ) ; Cur sor c = new Cur sor ( Cur sor . HAND_CURSOR) ; k. t ext Ar ea. set Cur sor ( c) ; r et ur n k; } The obj ect k is a kit t hat can creat e com ponent s suit able for a handheld display.
SOLUTION 18.3
The com m ent for Object.clone() in JDK 1.2.2 says t hat t his m et hod " creat es a new inst ance of t he class of t his obj ect and init ializes all it s fields wit h exact ly t he cont ent s of t he corresponding fields of t his obj ect , as if by assignm ent ; t he cont ent s of t he fields are not t hem selves cloned. Thus, t his m et hod perform s a 'shallow copy' of t his obj ect , not a 'deep copy' operat ion." To rem em ber t he precise funct ion of Object.clone(), it helps t o t hink of t his m et hod as if it were nam ed Object.newObjectSameFields().
SOLUTION 18.4
One solut ion is:
publ i c Obj ect cl one( ) { OzText Ar ea t a = new OzText Ar ea( ) ; t a. set Font ( t ext Ar ea( ) . get Font ( ) ) ; t a. set Cur sor ( get Cur sor ( ) ) ; r et ur n t a; } To copy an OzTextArea obj ect , t his code creat es a new obj ect and explicit ly set s t he inst ance variables t hat t he new obj ect m ust reproduce from it s prot ot ype.
SOLUTION 18.5
The code given in t he challenge produces t hree obj ect s, as Figure B.24 shows. The current version of t he clone() m et hod for MachineSimulator calls super.clone(), which class Object im plem ent s. This m et hod creat es a new obj ect wit h t he sam e fields. Prim it ive t ypes, such as t he int inst ance fields in a MachineSimulator, are copied. I n addit ion, obj ect references, such as t he Location field in MachineSimulator, are copied. Not e t hat t he reference is copied, not t he obj ect . This m eans t hat Object.clone() produces t he sit uat ion t hat Figure B.24 shows. Figure B.24. An insufficient design for cloning can create an incomplete copy that shares some objects with its prototype.
The code in t he challenge changes t he bay and t he coordinat es of t he second m achine's locat ion. However, because t here is only one Location obj ect , t hese m odificat ions change t he locat ion of bot h sim ulat ions. Thus, t he println() st at em ent displays 2 as t he value of m1.location.bay.
SOLUTION 18.6
A reasonable solut ion is as follows:
publ i c Obj ect cl one( )
{ t ry { Machi neSi mul at or copy = ( Machi neSi mul at or ) super . cl one( ) ; copy. l ocat i on = ( Locat i on) l ocat i on. cl one( ) ; r et ur n copy; } cat ch ( Cl oneNot Suppor t edExcept i on e) { / / t hi s shoul dn' t happen, si nce we ar e Cl oneabl e t hr ow new I nt er nal Er r or ( ) ; } } Not e t hat t he ret urn t ype of Object.clone() is Object, so your clone() m et hod m ust also ret urn t his t ype. You also have t o cast t he ret urn value of t he clone() m et hod t o be t he t ype of t he field t o which you assign it s result s.
Memento (Chapter 19)
SOLUTION 19.1
St oring a m em ent o as an obj ect assum es t hat t he applicat ion will st ill be running w hen t he user want s t o rest ore t he original obj ect . Reasons t hat will force you t o save a m em ent o t o persist ent st orage follow. • • •
SOLUTION 19.2
The ability to restore an object's state has to survive a system crash. You anticipate that the user will exit the system and will want to resume work later. You need to reconstruct an object on another computer.
Here is an im plem ent at ion of createMemento() for FactorySimulator:
publ i c Li st cr eat eMement o( ) { Li st l i st = new Ar r ayLi st ( ) ; I t er at or i = machi nes. i t er at or ( ) ; whi l e ( i . hasNext ( ) ) { Machi neI mage mi = ( Machi neI mage) i . next ( ) ;
l i st . add( mi . cl one( ) ) ; } r et ur n l i st ; } When you writ e a createMemento() m et hod, you should convince yourself or your colleagues t hat t he m et hod ret urns all t he inform at ion necessary t o reconst ruct t he receiving obj ect . I n t his exam ple, a m achine sim ulat or can reconst ruct it self from a clone, and a fact ory sim ulat or can reconst ruct it self from a list of m achine sim ulat or clones.
SOLUTION 19.3
Here is a solut ion t hat perform s t he described behavior of t he Undo but t on:
pr ot ect ed voi d undo( ) { i f ( mement os. si ze( ) > 1) { mement os. pop( ) ; Li st s = ( Li st ) mement os. peek( ) ; f act or y( ) . r est or e( s) ; undoBut t on( ) . set Enabl ed( mement os. si ze( ) > 1) ; vi zPanel ( ) . r epai nt ( ) ; } } Not e t hat t he factory(),undoButton(), and vizPanel() m et hod nam es m at ch t he ones given in Figure 19.2. I f t he ot her m et hods in t he GUI applicat ion work correct ly, t his m et hod should not have t o check t hat t he mementos st ack has at least one m em ent o. But it doesn't hurt .
SOLUTION 19.4
SOLUTION 19.5
The lat est m em ent o cont ains t he st at e t o save, so t he obj ect t o writ e t o disk is mementos.peek().
The disadvant ages of st oring an obj ect in t ext ual form at follow. • •
Encapsulation flies out the window. Anyone with a text editor can manipulate the object's data. For this approach to work, you must be able to parse the text either using an XML parser or writing your
own parser for a proprietary format. •
A textual representation may be much larger than an object serialization with the same information.
On t he ot her hand, t here are m any advant ages t o st oring obj ect s as t ext . • • •
Anyone with a text editor can verify an object's data. Anyone with a text editor can manipulate an object's data—an advantage in some contexts. It is often easier to pass text between systems than it is to set up interchange with RMI, CORBA, or other protocols.
Introducing Operations (Chapter 20)
SOLUTION 20.1
SOLUTION 20.2
SOLUTION 20.3
SOLUTION 20.4
CHAI N OF RESPONSI BI LI TY dist ribut es an operat ion across a chain of obj ect s. Each m et hod im plem ent s t he operat ion's service direct ly or forwards calls t o t he next obj ect in t he chain.
The figure shows one algorit hm —t he procedure t o det erm ine whet her an obj ect m odel is a t ree—t wo operat ions,which appear as t wo signat ures in t he MachineComponent class, and four m et hods.
No. I f you change t he ret urn value of MachineSimulator.clone(), it won't com pile. The clone() signat ure m at ches t he signat ure of Object.clone(), so t he ret urn t ype m ust m at ch as well.
The program print s "false". St at ic m et hods are invoked wit h reference t o an obj ect 's declared t ype, not wit h reference t o t he part icular obj ect . The t ype of r is Firework, so r.flies() ret urns false.
SOLUTION 20.5
•
•
One argum ent for t he presence of checked and unchecked except ions: The Java designers had it right : List ing NullPointerException and pot ent ially dozens of ot her com m on except ions on every m et hod would needlessly clut t er m y code wit h dreck t hat I 'd event ually learn t o ignore. On t he ot her hand: I t hink we can agree t hat we need an archit ect ural approach t o handling except ions, and we need a solid t est environm ent t hat m inim izes t he chances of runt im e problem s. Assum ing t hat we had t hose facilit ies, what do we need t he com piler t o do for us, regarding except ions? My answer is, " not hing." I 'd prefer t o have m y I DE let m e right - click on a m et hod t o see all t he except ions t hat can em erge, along wit h where t hey're caught in m y archit ect ure and what t est cases cover t hese except ions. Sound advanced? Maybe, but specifying except ions as checked and unchecked m erely papers over a com plex t opic t hat deserves m uch deeper considerat ion. While we await bet t er I DE support , I 'd vot e for no having no " checked" except ions and, as I say, an archit ect ure t hat com prehends except ion handling and a good t est environm ent .
Template Method (Chapter 21)
SOLUTION 21.1
Your com plet ed program should look som et hing like:
package com. oozi noz. appl i cat i ons; i mpor t j ava. ut i l . *; i mpor t com. oozi noz. uni t s. *; i mpor t com. oozi noz. f i r ewor ks. *; publ i c cl ass ShowCompar at or i mpl ement s Uni t Const ant s { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Rocket r 1 = new Rocket ( "Mach- i t ", 22. 95, ( Lengt h) METER. t i mes( 1000) ) ; Rocket r 2 = new Rocket ( "Pocket ", 2. 95, ( Lengt h) METER. t i mes( 12) ) ; Rocket r 3 = new Rocket ( "Sock- i t ", 9. 95, ( Lengt h) METER. t i mes( 100) ) ; Rocket r 4 = new Rocket ( "Spr ocket ", 3. 95, ( Lengt h) METER. t i mes( 50) ) ; Li st r ocket s = Ar r ays. asLi st ( new Rocket [ ] { r 1, r 2, r 3, r 4 } ) ; Compar at or c = new Compar at or ( ) { publ i c i nt compar e( Obj ect o1, Obj ect o2)
{ Rocket r 1 = ( Rocket ) o1; Rocket r 2 = ( Rocket ) o2; doubl e apogee1 = r 1. get Apogee( ) . get Magni t ude( ) ; doubl e apogee2 = r 2. get Apogee( ) . get Magni t ude( ) ; r et ur n ( i nt ) ( apogee1 - apogee2) ; } }; Col l ect i ons. sor t ( r ocket s, c) ; I t er at or i = r ocket s. i t er at or ( ) ; whi l e ( i . hasNext ( ) ) { Syst em. out . pr i nt l n( i . next ( ) ) ; } } }
SOLUTION 21.2
The code for markMoldIncomplete() passes t he inform at ion about an incom plet e m old t o t he m at erial m anager:
package com. oozi noz. ast er . cl i ent ; i mpor t com. oozi noz. ast er . *; publ i c cl ass OzAst er St ar Pr ess ext ends Ast er St ar Pr ess { publ i c Mat er i al Manager get Manager ( ) { r et ur n Mat er i al Manager . get Manager ( ) ; } publ i c voi d mar kMol dI ncompl et e( i nt i d) { get Manager ( ) . set Mol dI ncompl et e( i d) ; } } Not e t hat t he work involved in com plet ing a TEMPLATE METHOD is sim ilar t o t he work in supplying an ADAPTER. The difference bet ween t hese pat t erns is t hat ADAPTER calls for you t o t ranslat e an int erface, whereas TEM- PLATE METHOD calls for you t o supply a st ep of an algorit hm .
SOLUTION 21.3
What you want is a hook. You m ight phrase your request som et hing like t his: " I wonder whet her you could be so kind as t o add a call in your
shutDown() m et hod, aft er discharging t he past e and before flushing? I f you call it som et hing like collectPaste(), I can use it t o save t he past e t hat w e reuse here at Oozinoz." The developers are likely t o negot iat e wit h you about t he nam e of t he m et hod. The point is t hat by request ing a hook in a TEMPLATE METHOD, you can m ake your code m uch st ronger t han you can by w orking around an inadequacy in t he exist ing code.
SOLUTION 21.4
The getPlanner() m et hod in t he Machine class should t ake advant age of t he abst ract createPlanner() m et hod:
publ i c Machi nePl anner get Pl anner ( ) { i f ( pl anner == nul l ) { pl anner = cr eat ePl anner ( ) ; } r et ur n pl anner ; } Aft er adding t his m et hod, you can delet e t he getPlanner() m et hods in t he subclasses of Machine. This refact oring creat es an inst ance of TEMPLATE METHOD. The getPlanner( ) m et hod lazy- init ializes t he planner variable, relying on t he createPlanner() st ep t hat subclasses m ust supply.
State (Chapter 22)
SOLUTION 22.1
As t he st at e m achine shows, when t he door is open, a click will t ake t he door t o t he StayOpen st at e, and a second click will st art t he door closing.
SOLUTION 22.2
Your code should look som et hing like:
publ i c voi d compl et e( ) { i f ( st at e == OPENI NG) { set St at e( OPEN) ; }
el se i f ( st at e == CLOSI NG) { set St at e( CLOSED) ; } } publ i c voi d t i meout ( ) { set St at e( CLOSI NG) ; }
SOLUTION 22.3
The code for status() relies on t he st at e's class nam e and will report , for exam ple, t he st at us of an open door as "DoorOpen", inst ead of "Open". You can, of course, t rim off t he "Door" prefix, if you like.
SOLUTION 22.4
Your code should look som et hing like:
package com. oozi noz. car ousel ; publ i c cl ass Door Cl osi ng ext ends Door St at e { publ i c Door Cl osi ng( Door _2 door ) { super ( door ) ; } publ i c voi d cl i ck( ) { door . set St at e( door . OPENI NG) ; } publ i c voi d compl et e( ) { door . set St at e( door . CLOSED) ; } }
SOLUTION 22.5
Figure B.25 shows a reasonable diagram . The code for t his refact oring is in t he com.oozinoz.carousel_sol package. Figure B 25
This diagram shows the Door and DoorState classes sharing
constants that DoorConstants defines.
The code for DoorConstants is:
package com. oozi noz. car ousel _sol ; publ i c i nt er f ace Door Const ant s { Door St at e Door St at e Door St at e Door St at e Door St at e
CLOSED = OPENI NG = OPEN = CLOSI NG = STAYOPEN =
new Door Cl osed( ) ; new Door Openi ng( ) ; new Door Open( ) ; new Door Cl osi ng( ) ; new Door St ayOpen( ) ;
} The Door class now ret ains a single state obj ect :
package com. oozi noz. car ousel _sol ; i mpor t j ava. ut i l . *;
publ i c cl ass Door ext ends Obser vabl e i mpl ement s Door Const ant s { pr i vat e Door St at e st at e = CLOSED; //. . . . } The Door obj ect passes it self as an argum ent when it forwards st at e t ransit ion calls t o it s state variable. This let s t he receiving st at e updat e t he Door obj ect 's st at e wit h setState(). For exam ple, Door.click() is:
publ i c voi d cl i ck( ) { st at e. cl i ck( t hi s) ; } And DoorClosing.click(door:Door) is:
publ i c voi d cl i ck( Door door ) { door . set St at e( OPENI NG) ; }
Strategy (Chapter 23)
SOLUTION 23.1
SOLUTION 23.2
The GroupAdvisor and ItemAdvisor classes are inst ances of ADAPTER, providing t he int erface a client expect s, using t he services of a class wit h a different int erface.
Figure B.26 shows one solut ion. Figure B.26. The advertising policy at Oozinoz includes four strategies that appear as four implementations of the Advisor interface.
SOLUTION 23.3
The code for ItemAdvisor.java should look som et hing like:
package com. oozi noz. r ecommend; i mpor t com. oozi noz. f i r ewor ks. *; publ i c cl ass I t emAdvi sor i mpl ement s Advi sor { publ i c st at i c f i nal I t emAdvi sor si ngl et on = new I t emAdvi sor ( ) ; pr i vat e I t emAdvi sor ( ) { } publ i c Fi r ewor k r ecommend( Cust omer c) { r et ur n ( Fi r ewor k) Li keMySt uf f . suggest ( c) ; } } This code creat es a singleton obj ect t hat uses t he LikeMyStuff engine and t hat client s can use as an inst ance of t he Advisor int erface.
SOLUTION 23.4
Your code should look som et hing like:
publ i c Fi r ewor k get Recommended( ) {
r et ur n get Advi sor ( ) . r ecommend( t hi s) ; } The getAdvisor() m et hod st ill has a com plex if st at em ent , but once t he advisor is known, polym orphism does all t he work.
SOLUTION 23.5
SOLUTION 23.6
The presence of m ult iple, sim ilar singlet ons is rem iniscent of t he FLYWEI GHT pat t ern, which organizes a group of sim ilar, shareable, im m ut able obj ect s. To t his point , Design Pat t erns ( Gam m a et al. 1995) m ent ions t hat " STRATEGY obj ect s oft en m ake good flyweight s" ( p. 323) . On t he ot her hand, t he int ent of FLYWEI GHT is t o use sharing t o support large num bers of fine- grained obj ect s efficient ly. I t 's a bit of a st ret ch t o im agine t hat you'll ever have " large num bers" of alt ernat ive st rat egies.
I s a reusable sort rout ine an exam ple of TEMPLATE METHOD or an exam ple of STRATEGY? •
•
For STRATEGY: According to Design Patterns, TEMPLATE METHOD lets "subclasses" redefine certain steps of an algorithm. But the Collections.sort() method doesn't work with subclasses; it requires a Comparator instance. Each instance of Comparator provides a new method and thus a new algorithm and a new strategy. The sort() method is a good example of STRATEGY. For TEMPLATE METHOD: There are many sorting algorithms, but Collections.sort() uses only one, a merge sort. Changing the algorithm would mean changing to, say, a heap sort or a bubble sort. The intent of STRATEGY is to let you plug in different algorithms. That doesn't happen here. The intent of TEMPLATE METHOD is to let you plug a step into an algorithm. That is precisely how the sort() method works.
Command (Chapter 24)
SOLUTION 24.1
Java Swing applicat ions com m only apply t he MEDI ATOR pat t ern, regist ering a single obj ect t o receive all GUI event s. This obj ect m ediat es t he int eract ion of t he com ponent s and t ranslat es user input int o
com m ands for business dom ain obj ect s.
SOLUTION 24.2
Your code should look som et hing like:
pr ot ect ed J Menu f i l eMenu( ) { i f ( f i l eMenu == nul l ) { f i l eMenu = new J Menu( "Fi l e") ; Font f = Swi ngFacade. get St andar dFont ( ) ; f i l eMenu. set Font ( f ) ;
J MenuI t em save = new J MenuI t em( "Save") ; save. set Font ( f ) ; f i l eMenu. add( save) ; save. addAct i onLi st ener ( new Act i onLi st ener ( ) { publ i c voi d act i onPer f or med( Act i onEvent e) { save( ) ; } } ); J MenuI t em l oad = new J MenuI t em( "Load") ; l oad. set Font ( f ) ; f i l eMenu. add( l oad) ; l oad. addAct i onLi st ener ( new Act i onLi st ener ( ) { publ i c voi d act i onPer f or med( Act i onEvent e) { l oad( ) ; } } ); } r et ur n f i l eMenu; } Alt hough t he actionPerformed() m et hod requires an ActionEvent argum ent , you can safely ignore it . The fileMenu() code regist ers a single inst ance of an anonym ous class wit h t he Save m enu it em and a single inst ance of anot her anonym ous class wit h t he Load m enu it em When
t hese m et hods are called, t here is no doubt about t he source of t he event .
SOLUTION 24.3
The testSleep() m et hod passes t he doze com m and t o t he time() ut ilit y m et hod of t he CommandUtil class:
publ i c voi d t est Sl eep( ) { Command doze = new Command( ) { publ i c voi d execut e( ) { t ry { Thr ead. sl eep( 2000) ; } cat ch ( I nt er r upt edExcept i on i gnor e) { } } }; l ong t = CommandUt i l . t i me( doze) ; asser t Equal s( 2000, t , 50) ; }
SOLUTION 24.4
You can creat e t he hook you need as an anonym ous subclass:
package com. oozi noz. ast er . cl i ent ; i mpor t com. oozi noz. ast er . *; publ i c cl ass ShowHook { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Ast er St ar Pr ess2 p = new Ast er St ar Pr ess2( ) ; Hook h = new Hook( ) { publ i c voi d execut e( Ast er St ar Pr ess2 p) { Mat er i al Manager m = Mat er i al Manager . get Manager ( ) ; m. set Mol dI ncompl et e( p. get Cur r ent Mol dI D( ) ) ; } }; p. set Mar kMol dI ncompl et eHook( h) ;
} }
SOLUTION 24.5
SOLUTION 24.6
I n FACTORY METHOD, a client knows when t o creat e a new obj ect but doesn't know what kind of obj ect t o creat e. FACTORY METHOD m oves obj ect creat ion t o a m et hod t hat isolat es a client from knowing which class t o inst ant iat e. This principle also occurs in ABSTRACT FACTORY.
The int ent of t he MEMENTO pat t ern is t o provide st orage and rest orat ion of an obj ect 's st at e. Typically, you can add a new m em ent o t o a st ack wit h each execut ion of a com m and, popping and reapplying t hese m em ent os when a user needs t o undo com m ands.
Interpreter (Chapter 25)
SOLUTION 25.1
The following program will shut down all t he m achines t hat MachineLine cont rols, except for t he unload buffer:
package com. oozi noz. r obot . i nt er pr et er ; i mpor t com. oozi noz. machi ne. *; publ i c cl ass ShowI f { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Cont ext c = Machi neLi ne. cr eat eCont ext ( ) ; Var i abl e m = new Var i abl e( "m") ; Const ant ub = new Const ant ( c. l ookup( "Unl oadBuf f er 1501") ) ; Ter m t = new Equal s( m, ub) ; I f Command i c = new I f Command( t, new Nul l Command( ) , new Shut downCommand( m) ) ; For Machi nes f c = new For Machi nes( m, i c) ; f c. execut e( c) ; } }
The fc obj ect is an int erpret er; it int erpret s execute() t o m ean shut down all m achines except t he unload buffer.
SOLUTION 25.2
One solut ion is:
package com. oozi noz. r obot . i nt er pr et er ; i mpor t com. oozi noz. machi ne. *; publ i c cl ass Whi l eCommand ext ends Command { pr ot ect ed Ter m t er m; pr ot ect ed Command body; publ i c Whi l eCommand( Ter m t er m, Command body) { t hi s. t er m = t er m; t hi s. body = body; } publ i c voi d execut e( Cont ext c) { whi l e ( t er m. eval ( c) ! = nul l ) { body. execut e( c) ; } } }
SOLUTION 25.3
SOLUTION 25.4
The int ent of I NTERPETER is t o let you define com posit e classes t hat int erpret , or bring m eaning t o, an operat ion, based on t he t ype of com posit ion t hey represent . This let s you creat e a " language," or infinit e pat t ern of execut able operat ions. The int ent of COMMAND is m erely t o encapsulat e a request in an obj ect . Can an int erpret er work as a com m and? Sure. The quest ion of which pat t ern applies depends on your int ent . Are you defining an infinit e pat t ern of execut able operat ions, or are you encapsulat ing request s in obj ect s?
One solut ion is: I f t he possible com posit ions of an int erpret er hierarchy are a " language," t he j ob of a parser is t o t ranslat e elem ent s of a t ext ual language int o corresponding elem ent s of t he int erpret er language.
Introducing Extensions (Chapter 26)
SOLUTION 26.1
The com plet e code should look som et hing like:
package com. oozi noz. machi ne; i mpor t j ava. ut i l . *; publ i c cl ass Bi nSt ack { publ i c st at i c f i nal i nt STACK_LI MI T = 3; pr i vat e St ack st ack = new St ack( ) ; synchr oni zed publ i c Bi n pop( ) { whi l e ( st ack. si ze( ) == 0) { t ry { wai t ( ) ; Thr ead. sl eep( 500) ; } cat ch ( I nt er r upt edExcept i on i gnor e) { } } i f ( st ack. si ze( ) == STACK_LI MI T) { not i f y( ) ; } r et ur n ( Bi n) st ack. pop( ) ; } synchr oni zed publ i c voi d push( Bi n b) { whi l e ( st ack. si ze( ) == STACK_LI MI T) { t ry { wai t ( ) ; Thr ead. sl eep( 500) ; } cat ch ( I nt er r upt edExcept i on i gnor e) { } } i f ( st ack. si ze( ) == 0) { not i f y( ) ;
} st ack. push( b) ; } publ i c i nt si ze( ) { r et ur n st ack. si ze( ) ; } }
SOLUTION 26.2
SOLUTION 26.3
The call t o notify() in t he pop() m et hod will wake up a t hread t hat was wait ing for t he st ack t o m ake som e room . The awakened t hread m ust t hen wait t o obt ain t he m onit or. Because t he pop() m et hod is synchronized, it holds t he m onit or unt il t he m et hod com plet es.
The problem s in t he m odel are as follows. • •
• •
SOLUTION 26.4
The Rocket class can't extend two classes. The Rocket.clone() method can't have the same signature as and a different return type from the method it overrides. The Rocket.clone() method can't reduce visibility from protected to private. The Rocket.clone() method can't throw an extra exception. You could cure this problem if you made RocketCloneException an extension of CloneNotSupportedException.
Quest ionable aspect s of t he m odel follow. •
•
The FireworkCollection class is useful as a collection that contains only a certain type of object. However, such a collection is either a list or a set, and it should be named accordingly. The FireworkCollection class stores Firework objects in
a set instead of a list. This makes it difficult to imagine what a call to, say, get(0) will return. •
•
The FireworkCollection class has an iterator() function, which is good, but it returns Iterator, which is questionable. The returned iterator will have a next() method that returns an Object that the caller will have to cast to Firework. This defeats the idea of having a separate collection class for fireworks. The fireworks hierarchy comprehends the fact that a firework's type does not always indicate whether the firework will explode. For example, rockets may or may not explode, although firecrackers always explode and sparklers never do. The setExplodes() method allows flexibility in modeling firework types but also allows setting a Sparkler object to be an exploding type of firework. You should avoid this type of modeling error. In this case, it might work to create an ExplodingFirework subclass of Firework.
•
•
SOLUTION 26.5
The setExplodes() method of Firecracker throws an IllegalArgumentException if the argument is false, as all firecrackers explode. It is inconsistent to have this method throw an exception without extending this thought to Sparkler, whose instances should never explode. The hierarchy partitions fireworks into types of fireworks but also includes the FireworkSimulator subclass. A class's subclasses should usually not overlap. In this example, it's easy to imagine a class that is both a simulator and, say, a rocket.
Whet her LSP applies in t his case is a m at t er of j udgm ent . LSP specifically m aint ains t hat an obj ect should funct ion as an inst ance of it s superclass. I f you t hink of int erfaces as pure abst ract classes, LSP im plies t hat an inst ance of a class t hat im plem ent s an int erface should funct ion as an " inst ance" of t he int erface. Given t his int erpret at ion of LSP, t he Arrays.asList() m et hod, at least t hrough JDK 1.4, breaks LSP. I t seem s inconsist ent t o m e t o declare t hat a class im plem ent s an int erface but t hen t hrow an except ion when som e m et hods of t he int erface are called An int erface is a cont ract for t he behavior of t he
class. Except ions t o LSP should be rare, as should t he use of UnsupportedOperationException.
SOLUTION 26.6
To add observabilit y t o a class, you can provide it wit h an at t ribut e of t ype Observable from t he java.util package or t ype PropertyChangeSupport from t he java.beans package. Add t he operat ions you need t o your class, and im plem ent t hem by forwarding each call t o t he at t ribut e. For exam ple, if you call your class's at t ribut e changeSupport and init ialize it t o a PropertyChangeSupport obj ect , you can im plem ent t he m et hod:
pr ot ect ed voi d f i r ePr oper t yChange( St r i ng pr oper t yName, Obj ect ol dVal ue, Obj ect newVal ue) { changeSuppor t . f i r ePr oper t yChange( pr oper t yName, ol dVal ue, newVal ue) ; } Not e t hat you have no obligat ion t o reim plem ent every operat ion from t he class t o which you delegat e.
SOLUTION 26.7
A. True. To provide operat ions t hat m anipulat e st rings, t he sim plest approach is t o creat e a ut ilit y class wit h operat ions t hat accept a st ring and ret urn a t ransform ed version of it . B. True. Mult iple references t o OoString obj ect s would be affect ed by one anot her's changes t o t he obj ect . C. False. You can generally creat e sim ple subclasses of classes t hat expect st rings. For exam ple,
D. E. F. G. H. I. J. K.
i mpor t j ava. i o. *; publ i c cl ass OoSt r i ngReader ext ends St r i ngReader { publ i c OoSt r i ngReader ( OoSt r i ng ooSt r i ng) { super ( ooSt r i ng. get St r i ng( ) ) ; } }
Note that operations that expect a StringReader object will accept an instance of OoStringReader.
L. False. There is no obligat ion t o support all t he operat ions of t he class you delegat e t o. The getString() m et hod of OoString let s client s ext ract t he underlying String obj ect if t hey need an operat ion t hat OoString does not supply. M. True. I n part icular, Java com pilers will int erpret a sequence of charact ers bet ween double quot es as an inst ance of String:
N.
St r i ng answer = "Yep, st r i ngs ar e speci al ";
Decorator (Chapter 27)
SOLUTION 27.1
One answer is:
Wr i t er out = new Pr i nt Wr i t er ( Syst em. out ) ; out = new Wr apFi l t er ( new Buf f er edWr i t er ( out ) , 15) ; ( ( Wr apFi l t er ) out ) . set Cent er ( t r ue) ; out = new RandomCaseFi l t er ( out ) ; Alt ernat ively:
Wr apFi l t er out = new Wr apFi l t er ( new Buf f er edWr i t er ( new RandomCaseFi l t er ( new Pr i nt Wr i t er ( Syst em. out ) ) ) , 15) ; out . set Cent er ( t r ue) ; The com.oozinoz.applications package includes classes ShowFilters2 and ShowFilters3, which apply t hese t wo m et hods, respect ively:
package com. oozi noz. appl i cat i ons; i mpor t j ava. i o. *; i mpor t com. oozi noz. i o. *; publ i c cl ass ShowFi l t er s3 { publ i c st at i c voi d mai n( St r i ng ar gs[ ] ) t hr ows I OExcept i on { Buf f er edReader i n = new Buf f er edReader ( new Fi l eReader ( ar gs[ 0] ) ) ; Wr apFi l t er out = new Wr apFi l t er ( new Buf f er edWr i t er ( new RandomCaseFi l t er ( new Pr i nt Wr i t er ( Syst em. out ) ) ) , 15) ; out . set Cent er ( t r ue) ;
whi l e ( t r ue) { St r i ng s = i n. r eadLi ne( ) ; i f ( s == nul l ) { br eak; } out . wr i t e( s + "\ n") ; } out . cl ose( ) ; i n. cl ose( ) ; } } Running t his program looks som et hing like:
> j ava - cl asspat h \ oozi noz\ cl asses com. oozi noz. appl i cat i ons. ShowFi l t er s3 demo. t xt
ThE "SPacesHot " SheLl hOVer s aT 100 Met er SFor 2 t o 3 MI NUTeS, ERupt i nG St Ar BURSt S EVEr Y 10 sEconds THat gENeRat E abUnDant REAdI ng- LEvEL l i GhT f or a TypI caL sTaDi um.
SOLUTION 27.2
One solut ion is:
package com. oozi noz. i o; i mpor t j ava. i o. *; publ i c cl ass RandomCaseFi l t er ext ends Oozi nozFi l t er { pr ot ect ed RandomCaseFi l t er ( Wr i t er out ) { super ( out ) ; } publ i c voi d wr i t e( i nt c) t hr ows I OExcept i on {
out . wr i t e( Mat h. r andom( ) > . 5 ? Char act er . t oLower Case( ( char ) c) : Char act er . t oUpper Case( ( char ) c) ) ; } }
SOLUTION 27.3
Your solut ion should be pret t y close t o:
package com. oozi noz. f unct i on; publ i c cl ass Exp ext ends Funct i on { publ i c Exp( Funct i on f ) { super ( f ) ; } publ i c doubl e f ( doubl e t ) { r et ur n Mat h. exp( sour ce[ 0] . f ( t ) ) ; } }
SOLUTION 27.4
You m ight build up t he equat ion by using several Function variables or by defining all in one shot :
package com. oozi noz. appl i cat i ons; i mpor t com. oozi noz. f unct i on. *; publ i c cl ass ShowBr i ght ness { publ i c st at i c voi d mai n( St r i ng ar gs[ ] ) { FunPanel p = new FunPanel ( ) ; p. set Pr ef er r edSi ze( new j ava. awt . Di mensi on( 200, 200) ) ; Funct i on br i ght ness = new Ar i t hmet i c( ' *' , new Exp( new Ar i t hmet i c( ' *' , new Const ant ( - 5) , new T( ) ) ) , new Si n( new Ar i t hmet i c( ' *' ,
new Const ant ( Mat h. PI ) , new T( ) ) ) ) ;
p. set XY( new T( ) , br i ght ness) ; com. oozi noz. ui . Swi ngFacade. l aunch( p, " Br i ght ness") ; } }
SOLUTION 27.5
List eners provide flexibilit y in how an applicat ion behaves, but t hey do not include t he idea of com posit ion. The int ent of DECORATOR is t o let you com pose an obj ect 's behavior, not m erely alt er it .
Iterator (Chapter 28)
SOLUTION 28.1
A working program is:
package com. oozi noz. appl i cat i ons; i mpor t com. oozi noz. f i r ewor ks. *; publ i c cl ass ShowFi r ewor kLi st { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Fi r ewor kLi st f l i st = Pr omo. get Pr omot i onal Fi r ewor ks( ) ; Fi r ewor kLi st . I t r i = f l i st . i t er at or ( ) ; whi l e ( i . hasNext ( ) ) { Fi r ewor k f = i . next ( ) ; Syst em. out . pr i nt l n( f . get Name( ) + ", $" + f . get Pr i ce( ) + ", " + f . get Type( ) ) ; } } }
SOLUTION 28.2
SOLUTION 28.3
As Chapt er 16, Fact ory Met hod, describes, it erat ors provide a classic exam ple of t he FACTORY METHOD pat t ern. A client t hat want s an it erat or for an inst ance of a ProcessComponent knows when t o creat e t he it erat or, but t he receiving class knows which class t o inst ant iat e.
You need t o change t he next() code t o not ret urn t he node of a com posit e t hat you're it erat ing over. You also need t o pass along t he value of t he showInterior Boolean t o subit erat ors you creat e:
publ i c Obj ect next ( ) { i f ( peek ! = nul l ) { Obj ect o = peek; peek = nul l ; r et ur n o; } i f ( ! vi si t ed. cont ai ns( node) ) { vi si t ed. add( node) ; i f ( showI nt er i or ) { r et ur n node; } } r et ur n next Descendant ( ) ; } pr ot ect ed Obj ect next Descendant ( ) { whi l e ( t r ue) { i f ( subi t er at or ! = nul l ) { i f ( subi t er at or . hasNext ( ) ) { r et ur n subi t er at or . next ( ) ; } } i f ( ! chi l dr en. hasNext ( ) ) { r et ur n nul l ; }
I t er abl e i = ( I t er abl e) chi l dr en. next ( ) ; i f ( ! vi si t ed. cont ai ns( i ) ) { subi t er at or = i . i t er at or ( vi si t ed) ; subi t er at or . set ShowI nt er i or ( showI nt er i or ) ; } } }
SOLUTION 28.4
The go() rout ine launches a new t hread t hat can wake up at any t im e. I f you run t he code repeat edly, you m ay get varying result s, depending on your com piler, your virt ual m achine, and t he environm ent in w hich you are running. The out put indicat es t hat in t he given run, t he go() m et hod ret ains cont rol t hrough t hree it erat ions, print ing t he list from index 0 t o 2:
Mi xer 1201 Shel l Assembl er 1301 St ar Pr ess1401 At t his point , t he second t hread wakes up and places "Fuser1101" at t he beginning of t he list , bum ping all t he ot her m achine nam es down one slot . I n part icular, "StarPress1401" m oves from index 2 t o index 3. When t he prim ary t hread regains cont rol, t he go() m et hod print s t he rem ainder of t he list , from index 3 t o t he end:
St ar Pr ess1401 Unl oadBuf f er 1501
SOLUTION 28.5
To m ake a clone of a collect ion before it erat ing over it , you have t o ensure t hat t he source collect ion is cloneable. Creat e a synchronized version of t he collect ion for general access, and synchronize on t his collect ion when cloning t he underlying source collect ion:
package com. oozi noz. appl i cat i ons; i mpor t j ava. ut i l . *; publ i c cl ass ShowConcur r ent Mut ex2 i mpl ement s Runnabl e { pr i vat e Ar r ayLi st sour ceLi st ; / / not e t he t ype change pr i vat e Li st synchLi st ;
pr ot ect ed st at i c Ar r ayLi st upMachi neNames( ) / / her e t oo! { r et ur n new Ar r ayLi st (
Ar r ays. asLi st ( new St r i ng[ ] { "Mi xer 1201", "Shel l Assembl er 1301", "St ar Pr ess1401", "Unl oadBuf f er 1501" } ) ); } pr ot ect ed voi d go( ) { sour ceLi st = upMachi neNames( ) ; synchLi st = Col l ect i ons. synchr oni zedLi st ( sour ceLi st ) ; Li st copy; synchr oni zed ( synchLi st ) { copy = ( Li st ) sour ceLi st . cl one( ) ; }
I t er at or i = copy. i t er at or ( ) ; i nt j = 0; whi l e ( i . hasNext ( ) ) { j ++; i f ( j == 1) { new Thr ead( t hi s) . st ar t ( ) ; } Syst em. out . pr i nt l n( i . next ( ) ) ; } } publ i c voi d r un( ) { synchLi st . add( 0, "Fuser 1101") ; } publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { new ShowConcur r ent Mut ex2( ) . go( ) ; } }
I t is im port ant t o synchronize on t he right m ut ex obj ect when cloning t he underlying collect ion. The synchronizedList() m et hod creat es an obj ect t hat wraps t he source collect ion. This obj ect synchronizes on it self when accessing t o t he source list . To clone t he source safely, you need t o synchronize on t his sam e obj ect : t he synchList obj ect in t he program . As before, t his program print s out a valid list of " up" equipm ent . The difference is t hat now, t he second t hread is not blocked while t he prim ary t hread it erat es over it s clone—a crit ical dist inct ion in pract ice!
Visitor (Chapter 29)
SOLUTION 29.1
The difference is in t he t ype of t he this obj ect . The accept() m et hod calls t he visit() m et hod of a MachineVisitor obj ect . The accept() m et hod in t he Machine class will look up a visit() m et hod wit h t he signat ure visit(Machine), whereas t he accept() m et hod in t he MachineComposite class will look up a m et hod wit h t he signat ure visit(MachineComposite).
SOLUTION 29.2
One solut ion is:
package com. oozi noz. appl i cat i ons; i mpor t com. oozi noz. machi ne. *; i mpor t com. oozi noz. dubl i n. *; publ i c cl ass ShowFi nd { publ i c st at i c voi d mai n( St r i ng[ ] ar gs) { Machi neComponent f = Oozi nozFact or y. dubl i n( ) ; Syst em. out . pr i nt l n( new Fi ndVi si t or ( ) . f i nd( f , 3404) ) ; } } This program successfully print s out :
St ar Pr ess3404
SOLUTION 29.3
A solut ion is:
package com. oozi noz. dubl i n; i mpor t com. oozi noz. machi ne. *; i mpor t j ava. ut i l . *; publ i c cl ass RakeVi si t or ext ends Machi neVi si t or
{ pr i vat e Set l eaves; publ i c Set get Leaves( Machi neComponent mc) { l eaves = new HashSet ( ) ; mc. accept ( t hi s) ; r et ur n l eaves; } publ i c voi d vi si t ( Machi ne m) { l eaves. add( m) ; } publ i c voi d vi si t ( Machi neComposi t e mc) { I t er at or i = mc. get Component s( ) . i t er at or ( ) ; whi l e ( i . hasNext ( ) ) { ( ( Machi neComponent ) i . next ( ) ) . accept ( t hi s) ; } } }
SOLUTION 29.4
One solut ion is t o add a Set argum ent t o all t he accept() and visit() m et hods, so t hat t he set of visit ed nodes get s passed around. The Process-Component class should t hen have a concret e accept() m et hod t hat calls it s abst ract accept() m et hod, passing it a new Set obj ect :
publ i c voi d accept ( Pr ocessVi si t or v) { accept ( v, new HashSet ( ) ) ; } This design is sim ilar t o t he approach t hat Chapt er 28, I TERATOR, used t o creat e an it erat or for t he ProcessComponent hierarchy. The ProcessAlternation, ProcessSequence, and ProcessStep subclasses in t he ProcessComponent hierarchy pass t he set t o a visit or's visit() m et hod:
publ i c voi d accept ( Pr ocessVi si t or v, Set vi si t ed) { v. vi si t ( t hi s, vi si t ed) ; } Now visit or developers m ust creat e classes wit h visit() m et hods t hat accept t he visited set . This is a significant hint t hat using t he set is a good idea alt hough t he visit or developer ret ains t he responsibilit y for
populat ing t he set .
SOLUTION 29.5
Alt ernat ives t o applying VI SI TOR follow. •
•
•
Add the behavior you need to the original hierarchy. You can achieve this if you are in good communication with the hierarchy developers or if you are in a shop that does not recognize code ownership. You can let a class that must operate on a machine or a process structure just traverse the structure. Note that you don't need visitor to extend, say, a list of items. The reason VISITOR pops up with hierarchies is that the hierarchy contains various types. But you can detect an object's specific type with instanceof or by building in Booleans, such as isLeaf() and isComposite(). If the behavior you want to add is of a significantly different thrust than the existing behavior, you can create a parallel hierarchy. For example, the MachinePlanner class in Chapter 16, Factory Method, places a machine's planning behavior in a separate hierarchy.
Appendix C. UML AT A GLANCE This appendix briefly explains t he feat ures of t he Unified Modeling Language ( UML) t hat t his book uses. UML provides convent ional not at ion t hat t his book applies t o illust rat e t he design of obj ect - orient ed syst em s. Alt hough UML is not overly com plex, you can easily underest im at e t he richness of t he feat ures it provides. For a rapid int roduct ion t o m ost of t he feat ures of t he UML, read UML Dist illed ( Fowler wit h Scot t 2000) . For a m ore t horough review, read The Unified Modeling Language User Guide ( Booch, Rum baugh, and Jacobson 1999) . By learning t o use st andard nom enclat ures and not at ions, we learn t o com m unicat e at a design level, m aking us all m ore product ive.
Classes Figure C.1 applies som e of t he UML feat ures for illust rat ing classes. Following are not es on class diagram s. Figure C.1. The fireworks package includes the Firework and Classification classes.
•
•
• •
•
Indicate a package by placing the name of the package in a rectangle left-aligned with a larger box that may show classes and interfaces. Figure C.1 shows a portion of the com.oozinoz.fireworks package. UML does not require that a diagram show everything about a portrayed element, such as the complete contents of a package or all the methods of a class. Draw a class by placing the name of a class centered in a rectangle. Figure C.1 shows two classes: Classification and Firework. You can show a class's instance variables in a rectangle beneath the class name. The Firework class has instance variables name, price, and classification. Follow the variable's name by a colon and the variable's type. You can show a class's methods in a second rectangle beneath the class name. The Firework class has a constructor, a method with the same name as the class. The class also has at least three other methods: flies(), getName(), and setClassification().
• •
•
• •
When a method accepts parameters, you should usually show them, as the setClassification() method does. Variables in method signatures usually appear as the name of the variable, a colon, and the type of the variable. You may omit or abbreviate the variable name if its type implies the variable's role. You may indicate that an instance variable or a method is protected by preceding it with a pound sign (#). A plus sign (+) indicates that a variable or a method is public, and a minus sign (-) indicates that a variable or a method is private. Indicate that an instance variable is static—and thus has class scope—by underlining it, as the flies() method shows. Make notes by drawing a dog-eared rectangle. The text in notes may contain comments, constraints, or code. Use a dashed line to attach notes to other diagram elements. Notes can appear in any UML diagram, although this book uses notes only in class diagrams.
Class Relationships Figure C.2 shows a few of UML's feat ures for m odeling class relat ionships. Following are not es on class relat ionship not at ion. Figure C.2. A MachineComposite object contains either Machine objects or other composites. The Customer class depends on the LikeMyStuff class without instantiating it.
• • •
• •
•
Show a class name or a method name in italics to indicate that the class or method is abstract. Use a closed, hollow arrowhead to point to a class's superclass. Use a line between classes to indicate that instances of the classes are connected. Most commonly, a line on a class diagram means that one class has an instance variable that refers to the other class. The classes MachineComposite class, for example, uses a List variable that contains references to other machine components. Use a diamond to show that instances of a class contain a collection of instances of another class. An open arrowhead indicates navigability. Use it to emphasize that one class has a reference to another and that the pointed-to class does not have a back reference. A multiplicity indicator, such as 0..1, indicates how many connections may appear between objects. Use an asterisk (*) to
indicate that zero or more instances of an object of a class may be connected to objects of an associated class. •
•
To show that a method may throw an exception, use a dashed arrow pointing from the method to the exception class. Label the arrow with a «throws» stereotype. Use a dashed arrow between classes to show a dependency that does not use an object reference. For example, the Customer class uses a static method from the LikeMyStuff recommendation engine.
Interfaces Figure C.3 shows t he basic feat ures for illust rat ing int erfaces. Following are not es on int erfaces. Figure C.3. You can indicate an inter-face with either an «interface» stereotype or a lollipop.
•
• •
You can draw an interface by placing the text «interface» and the name of the interface in a rectangle, as Figure C.3 shows. You can use a dashed line and a closed, hollow arrowhead to show that a class implements the interface. You can also show that a class implements an interface by showing a line and circle, or "lollipop," and the name of the interface. Interfaces and their methods are always abstract in Java. Oddly enough, interfaces and their methods do not appear in italics, unlike abstract classes and abstract methods in classes.
Objects
An obj ect diagram illust rat es specific inst ances of classes, as Figure C.4 shows. Following are not es on obj ect diagram s. Figure C.4. Depictions of objects indicate the objects' names and/or types. A sequence diagram shows a succession of method calls.
•
•
•
• •
You can show an object by giving its name and type, separated by a colon. You may optionally show just the name, or just a colon and the type. In any case, underline the name and/or type of the object. Use a line between objects to indicate that one object has a reference to another. You can use an open arrowhead to emphasize the direction of the reference. You can show a sequence of objects calling methods of other objects, as the lower part of Figure C.4 shows. The order of method calls is top to bottom, and the dashed lines indicate the existence of the object over time. Use the «create» stereotype to show that one object creates another. Draw a bold outline box around an object to indicate that it is active in another thread, process, or computer.
States Figure C.5 shows a UML st at echart diagram . Following are not es on illust rat ing st at es.
Figure C.5. A statechart diagram shows transitions from state to state.
•
Show a state in a rectangle with rounded corners.
•
Show state transitions with open arrows.
•
A statechart need not map directly to a class diagram or an object diagram, although you may arrange for a direct translation, as Figure 22.3 in Chapter 22, State, shows on page 230.
Glossary ABSTRACT CLASS A class t hat cannot be inst ant iat ed because it eit her cont ains abst ract m et hods or is declared t o be abst ract .
ABSTRACT METHOD A declarat ion of an operat ion t hat concret e subclasses m ust im plem ent .
ABSTRACT SYNTAX TREE A st ruct ure, creat ed by a parser, t hat organizes input t ext according t o a language's gram m ar.
ABSTRACTION A class t hat depends on abst ract m et hods t hat are im plem ent ed in subclasses or in im plem ent at ions of an int erface.
AERIAL SHELL
A firework t hat is fired from a m ort ar and t hat explodes m idflight , ej ect ing and ignit ing st ars.
ALGORITHM A procedure, or sequence of inst ruct ions, t hat accept s input s and produces out put s.
API See [APPLICATION PROGRAMMING INTERFACE] APOGEE The great est height t hat a fired or flying firework achieves.
APPLICATION PROGRAMMING INTERFACE The int erface, or set of calls, t hat a syst em m akes publicly available.
ASSAY An analysis, usually of a chem ical m ixt ure.
BUSINESS OBJECT An obj ect t hat m odels an ent it y or a process in a business.
CAROUSEL A large, sm art rack t hat accept s m at erial t hrough a doorway and st ores it .
CLASS DIAGRAM A drawing t hat shows definit ions of and relat ionships am ong classes and int erfaces.
COMPOSITE A group of obj ect s in which som e obj ect s m ay cont ain ot hers, so t hat som e obj ect s represent groups and ot hers represent individual it em s, or leaves.
CONCRETE CLASS A class t hat can be inst ant iat ed; unlike an abst ract class.
CONSOLIDATION LANGUAGE
A com put er language, such as Java, t hat absorbs t he st rengt hs and discards t he weaknesses of it s predecessors.
CONSTANT A field t hat is st at ic and t hus widely accessible and final, wit h a fixed value.
CONTEXT An obj ect t hat cont ains inform at ion about t he set t ing in which a group of ot her obj ect s are operat ing. I n part icular, a cont ext m ay record which obj ect of a group is current or act ive.
CONTEXT-FREE LANGUAGE A language t hat can be described wit h a gram m ar.
CORBA Com m on obj ect request broker archit ect ure; a st andard design, or com m on archit ect ure, for facilit at ing, or brokering, obj ect request s t hat pass bet ween syst em s. I n m any applicat ions, t he use of CORBA has been displaced by RMI .
CYCLE A pat h in which a node, or obj ect , appears t wice.
DEEP COPY A com plet e copy of an obj ect in which t he new obj ect 's at t ribut es are com plet e copies of t he original obj ect 's at t ribut es.
DEMO An exam ple t hat shows how t o use a class or a subsyst em .
DESIGN PATTERN A pat t ern—a way t o pursue an int ent —t hat operat es at about a class level.
DIMENSION An aggregat ion of a physical m easure's exponent s of lengt h, m ass, and t im e.
DIRECTED GRAPH A graph in which edges have a direct ion; t hey point .
DOUBLE DISPATCH A design in which a class B obj ect dispat ches a request t o a class A obj ect , and t he class A obj ect im m ediat ely dispat ches a request back t o t he class B obj ect , wit h addit ional inform at ion about t he class A obj ect 's t ype.
DRIVER An obj ect t hat operat es a com put er syst em , such as a dat abase, or an ext ernal device, such as a line plot t er, according t o a well- specified int erface.
DUD A firework t hat does not work correct ly, part icularly a firework t hat is designed t o explode but doesn't .
ENTERPRISE JAVABEANS A specificat ion of an n- t ier, com ponent - based archit ect ure.
EXTENSIBLE MARKUP LANGUAGE A t ext ual language t hat relies on t ags, or m arkup, t o cont ain inform at ion about t he t ext and t hat specifically separat es classes or t ypes of docum ent s from t heir inst ances.
FAIL-FAST An algorit hm t hat fails as soon as possible when it cannot adj ust t o a problem in it s input s; specifically, w hen t he input s change during t he algorit hm 's execut ion.
FIREWORK Any com bust ible com posit ion t hat m ay burn, fly, or explode, providing ent ert aining visual and aural effect s.
FOUNTAIN A ground- based firework t hat em it s a spray of sparks.
GRAMMAR A set of com posit ion rules.
GRAPH A collect ion of nodes and edges.
GRAPH THEORY A m at hem at ical concept ion of nodes and edges. When applied t o an obj ect m odel, a graph's nodes are usually obj ect s, and a graph's edges are usually obj ect references.
GRAPHICAL USER INTERFACE I n an applicat ion, a layer of soft ware t hat let s a hum an int eract wit h ( graphical depict ions of) but t ons, m enus, sliders, t ext areas, and ot her com ponent s.
GUI See [GRAPHICAL USER INTERFACE] HOOK A provision t hat a developer places in his or her code t o give ot her developers a chance t o insert code at a specific spot in a procedure.
HOPPER A cont ainer t hat dispenses chem icals, usually int o a m achine.
HYPE Overexcit em ent about a t echnology's pot ent ial value.
IMMUTABLE Unchangeable; specifically, an obj ect wit h values t hat cannot change.
IMPLEMENTATION The Java st at em ent s t hat m ake up t he bodies of a class's m et hods.
INTERFACE The collect ion of m et hods and fields t hat a class perm it s obj ect s of ot her classes t o access. Also, a Java int erface t hat defines t he m et hods t hat an im plem ent ing class m ust provide.
INTERPRETER An obj ect com posed from a com posit ion hierarchy in which each class represent s a com posit ion rule t hat det erm ines how t he class im plem ent s, or int erpret s, an operat ion t hat occurs t hroughout t he hierarchy.
JAVA DEVELOPMENT KIT A collect ion of soft ware t hat includes t he Java class libraries, a com piler, and ot her support ing t ools; usually refers specifically t o kit s available at j ava.sun.com .
JDBC An applicat ion program m ing int erface for execut ing SQL st at em ent s. JDBC is a t radem arked nam e, not an acronym .
JDK See [JAVA DEVELOPMENT KIT] JUNIT A t est ing fram ework, writ t en by Erich Gam m a and Kent Beck, t hat let s you im plem ent aut om at ed regression t est s in Java. Available at www.j unit .org.
KIT A class wit h create- m et hods t hat ret urn inst ances of a fam ily of obj ect s.
LANGUAGE Usually, a set of st rings but generally a set of obj ect s t hat follow a pat t ern est ablished by a collect ion of rules. These rules t ypically include com posit ion rules, or gram m ar, and m ay include ot her rules, such as t he rule t hat ident ifiers m ust be declared before t hey are used. A language t hat can be fully described wit h a gram m ar is a cont ext - free language.
LAYER A group of classes wit h sim ilar responsibilit ies, usually collect ed in a single Java package, and usually wit h well- defined dependencies on ot her layers.
LAZY-INITIALIZE To inst ant iat e an obj ect when it is first needed.
LEAF An individual it em wit hin a com posit e.
LOOSE COUPLING A com parat ively sm all and well- defined am ount of responsibilit y t hat int eract ing obj ect s bear t o one anot her.
MEAN TIME BETWEEN FAILURE
The am ount of t im e t hat is expect ed t o elapse from t he t im e a m achine com es up t o t he t im e it fails t o operat e correct ly.
MEASURE A com binat ion of a m agnit ude ( a num ber) and a dim ension.
MESSAGE Usually, a m et hod call but generally a port rayal of com m unicat ion bet ween obj ect s, as in a sequence diagram .
METHOD An im plem ent at ion of an operat ion.
METHOD LOOKUP The algorit hm for deciding which definit ion of a m et hod t o use when a client calls an obj ect 's m et hod.
MODEL/VIEW/CONTROLLER A design t hat separat es an int erest ing obj ect —t he m odel—from user int erface elem ent s t hat port ray it —t he view and t he cont roller.
MOLALITY A count of t he num ber of m olecules in a bat ch of a chem ical subst ance.
MOLE A num ber—Avogadro's num ber—defined as t he num ber of at om s in 12 gram s of carbon 12. The beaut y of t his num ber is t hat it let s you apply chem ical equat ions while working wit h m easurable quant it ies of chem ical bat ches. I f you know t hat t he m olecular weight of a chem ical is mw, mw gram s of t he chem ical will cont ain one m ole of t he chem ical.
MONITOR A lockable obj ect aspect t hat represent s possession of t he obj ect by a t hread.
MORTAR A t ube from which an aerial shell is fired.
MTBF
See [MEAN TIME BETWEEN FAILURE] MUTEX An obj ect shared by t hreads t hat cont end for cont rol of t he obj ect 's m onit or. The word m ut ex is a cont ract ion of t he words m ut ual exclusion.
MUZZLE VELOCITY The speed at which an aerial shell leaves t he m ort ar from which it fires.
N-TIER A t ype of syst em t hat assigns layers of responsibilit y t o obj ect s running on different com put ers.
OOZINOZ A fict ional fireworks com pany nam ed for t he sound of an audience at an exhibit ion.
OPERATION A specificat ion of a service t hat can be request ed from an inst ance of a class.
PARALLEL HIERARCHY A pair of class hierarchies in which each class in one hierarchy has a corresponding class in t he ot her hierarchy.
PARAMETRIC EQUATIONS Equat ions t hat define a group of variables, such as x and y, in t erm s of a st andard param et er,such as t.
PARSER An obj ect t hat can recognize elem ent s of a language and decom pose t heir st ruct ure, according t o a set of rules, int o a form suit able for furt her processing.
PATH I n an obj ect m odel, a series of obj ect s such t hat each obj ect in t he series has a reference t o t he next obj ect in t he series.
PATTERN A way of doing som et hing; a way of pursuing an int ent .
PERSISTENT STORAGE St orage of inform at ion on a device, such as a disk, t hat ret ains t he inform at ion even when powered down.
POLYMORPHISM The principle t hat m et hod invocat ion depends on bot h t he operat ion invoked and t he class of t he invocat ion receiver.
RANDOM CASE A STRiNG like t hI S wHOSE ChARAct Ers MAY bE uPPeR OR LOwERcaSe, AT RANdom .
REFACTOR To change code t o im prove it s int ernal st ruct ure wit hout changing it s ext ernal behavior.
RELATION The way in which obj ect s st and wit h regard t o one anot her. I n an obj ect m odel, t he subset of all possible references from obj ect s of one t ype t o obj ect s of a second t ype.
REMOTE METHOD INVOCATION A Java facilit y t hat let s obj ect s on different com put ers com m unicat e.
RICH COMPOSITE A com posit e t hat includes m ult iple ways for grouping subcom posit es.
RMI See [REMOTE METHOD INVOCATION] ROLLOVER A GUI com ponent effect t hat changes t he look of t he com ponent when t he cursor is over it .
ROMAN CANDLE A st at ionary t ube t hat cont ains a m ixt ure of explosive charges, sparks, and st ars.
ROOT I n a t ree, a dist inguished node or obj ect t hat has no parent .
SEQUENCE DIAGRAM A drawing t hat shows a flow of m essages bet ween obj ect s.
SESSION The event of a user running a program , conduct ing t ransact ions w it hin t he program , and exit ing.
SHALLOW COPY As opposed t o deep copy, a shallow copy lim it s t he dept h t o which it copies an obj ect 's at t ribut es, let t ing t he new obj ect share subordinat e obj ect s wit h t he original.
SHELL See [AERIAL SHELL] SIGNATURE A com binat ion of a m et hod's nam e and t he num ber and t ypes of it s form al param et ers.
SPARKLER A wire coat ed wit h a com bust ible past e sat urat ed wit h iron filings t hat , do, in fact , sparkle.
SQL See [STRUCTURED QUERY LANGUAGE] STAR A com pressed pellet of an explosive m ixt ure, usually part of an aerial shell or a Rom an candle.
STATE A com binat ion of t he current values of an obj ect 's at t ribut es.
STATIC METHOD A m et hod t hat is bound t o a class and t hat can be invoked against t he class or against an obj ect whose declared t ype is t he class.
STRATEGY A plan, or approach, for achieving an aim given cert ain input condit ions.
STREAM A serial collect ion of byt es or charact ers, such as t hose t hat appear in a docum ent .
STRUCTURED QUERY LANGUAGE A com put er language for querying relat ional dat abases.
STUB A m et hod t hat does not hing; a class t hat im plem ent s an int erface wit h m et hods t hat do not hing.
TIER A layer t hat execut es on a com put er.
TITLE CASE A St ring Like This Whose Charact ers Are Uppercase I f They Follow Whit espace.
TREE An obj ect m odel t hat cont ains no cycles.
UML See [UNIFIED MODELING LANGUAGE] UNIFIED MODELING LANGUAGE A not at ion for illust rat ing design ideas.
UNIFORM RESOURCE LOCATOR A point er t o a resource on t he World Wide Web. ( See java.net.URL for m ore inform at ion.)
UNIT A st andard m easure; a st andard m agnit ude or am ount of a part icular physical dim ension.
URL See [UNIFORM RESOURCE LOCATOR] UTILITY A class t hat has all st at ic m et hods.
WORKBOOK A st udent 's book of problem s and exercises.
WIP See [WORK IN PROCESS] WORK IN PROCESS Part ially m anufact ured goods in a fact ory.
XML See [EXTENSIBLE MARKUP LANGUAGE]
Bibliography Alexander, Christ opher. 1979. The Tim eless Way of Building. Oxford, England: Oxford Universit y Press. Alexander, Christ opher, Sara I shikawa, and Murray Silverst ein. 1977. A Pat t ern Language: Towns, Buildings, Const ruct ion. Oxford, England: Oxford Universit y Press. Alpert , Sherm an R., Kyle Brown, and Bobby Woolf. 1998. The Design Pat t erns Sm allt alk Com panion. Reading, MA: Addison- Wesley. Alur, Deepak, John Crupi, and Dan Malks. 2001. Core J2EE Pat t erns: Best Pract ices and Design St rat egies. Upper Saddle River, NJ: Prent ice- Hall. Am bler, Scot t W. 1999. More Process Pat t erns: Delivering Large- Scale Syst em s Using Obj ect Technology. Cam bridge, England: Cam bridge Universit y Press. Am bler, Scot t W. 1999. Process Pat t erns: Building Large- Scale Syst em s Using Obj ect Technology. Cam bridge, England: Cam bridge Universit y Press. Arnold, Ken, and Jam es Gosling. 1998. The Java™ Program m ing Language, Second Edit ion. Reading, MA: Addison- Wesley. Beck, Kent . 1996. Sm allt alk Best Pract ice Pat t erns. Upper Saddle River, NJ: Prent ice- Hall. Booch, Grady, Jam es Rum baugh, and I var Jacobson. 1999. The Unified Modeling Language User Guide. Reading, MA: Addison- Wesley. Brown, William J., Raphael C. Malveau, William H. Brown, Hays W. McCorm ick I I I , and Thom as J. Mowbray. 1998. Ant iPat t erns: Refact oring Soft ware, Archit ect ures, and Proj ect s in Crisis. New York: John Wiley & Sons. Buschm ann, Frank, Regine Meunier, Hans Rohnert , Pet er Som m erlad, and Michael St al. 1996. Pat t ern- Orient ed Soft ware Archit ect ure, Volum e 1: A Syst em of Pat t erns. New York: John Wiley & Sons. Carey, Jam es, Brent Carlson, and Tim Graser. 2000. SanFranciso™ Design Pat t erns: Blueprint s for Business Soft ware. Bost on, MA: Addison- Wesley. Chan, Pat rick, Rosanna Lee, and Doug Kram er. 1998. The Java™ Class Libraries, Second Edit ion, Volum e 1. Reading, MA: Addison- Wesley. Coad, Pet er, Mark Mayfield, and David Nort h. 1996. Obj ect Models: St rat egies, Pat t erns, and Applicat ions. Upper Saddle River, NJ: Prent ice- Hall.
Cooper, Jam es W. 2000. Java™ Design Pat t erns. Bost on, MA: Addison- Wesley. Coplien, Jam es O. 1992. Advanced C+ + Program m ing St yles and I diom s. Reading, MA: Addison- Wesley. Coplien, Jam es O., and Douglas C. Schm idt , eds. Pat t ern Languages of Program Design. 1995. Reading, MA: Addison- Wesley. Corm en, Thom as H., Charles E. Leiserson, and Ronald L. Rivest . 1990. I nt roduct ion t o Algorit hm s. Cam bridge, MA: The MI T Press. Flanagan, David. 1997. Java™ Exam ples in a Nut shell. Sebast apol, CA: O'Reilly. Flanagan, David. 1999a. Java™ Foundat ion Classes in a Nut shell. Sebast apol, CA: O'Reilly. Flanagan, David. 1999b. Java™ in a Nut shell, 3rd ed. Sebast apol, CA: O'Reilly. Flanagan, David, Jim Farley, William Crawford, and Kris Magnusson. 1999. Java™ Ent erprise in a Nut shell. Sebast apol, CA: O'Reilly. Fowler, Mart in. 1996. Analysis Pat t erns: Reusable Obj ect Models. Reading, MA: AddisonWesley. Fowler, Mart in, wit h Kendall Scot t . 2000. UML Dist illed, Second Edit ion. Bost on, MA: Addison- Wesley. Fowler, Mart in, Kent Beck, John Brant , William Opdyke, and Don Robert s. 1999. Refact oring. Reading, MA: Addison- Wesley. Gam m a, Erich, Richard Helm , Ralph Johnson, and John Vlissides. Design Pat t erns. 1995. Reading, MA: Addison- Wesley. Gosling, Jam es, Bill Joy, Guy St eele, and Gilad Bracha. 2000. The Java™ Language Specificat ion, Second Edit ion. Bost on, MA: Addison- Wesley. Grand, Mark. 1998. Pat t erns in Java™. New York: John Wiley. Ham ilt on, Graham , Rick Cat t ell, and Maydene Fisher. 1997. JDBC™ Dat abase Access wit h Java™. Reading, MA: Addison- Wesley. Harrison, Neil, Brian Foot e, and Hans Rohnert . 1999. Pat t ern Languages of Program Design 4. Reading, MA: Addison- Wesley. Honderich, Ted, ed. 1995. The Oxford Com panion t o Philosophy. New York: Oxford Universit y Press. Larm an, Craig. 2002. Applying UML and Pat t erns, Second Edit ion. Upper Saddle River, NJ: Prent ice- Hall. Lea, Doug. 2000. Concurrent Program m ing in Java™, Second Edit ion. Bost on, MA: Addison- Wesley. Liskov, Barbara. May, 1987. Dat a Abst ract ion and Hierarchy. SI GPLAN Not ices, volum e 23, num ber 5. Mart in, Jam es, and Jam es J. Odell. 1995. Obj ect Orient ed Met hods—A Foundat ion. Englewood Cliffs, NJ: Prent ice- Hall. Mart in, Robert C., Dirk Riehle, and Frank Buschm ann, eds. 1997. Pat t ern Languages of Program Design 3. Reading, MA: Addison- Wesley.
Met sker, St even J. 2001. Building Parsers wit h Java™. Bost on, MA: Addison- Wesley. Mowbray, Thom as J, and Raphael C. Malveau. 1997. CORBA Design Pat t erns. New York: John Wiley & Sons. Plat o. 1991. The Republic of Plat o, Second Edit ion. Allan Bloom , ed. New York: Basic Books. Pree, Wolfgang. 1994. Design Pat t erns for Obj ect - Orient ed Soft ware Developm ent . Reading, MA: Addison- Wesley. Rising, Linda. 2000. The Pat t ern Alm anac 2000. Bost on, MA: Addison- Wesley. Russell, Michael S. 2000. The Chem ist ry of Fireworks. Cam bridge, England: Royal Societ y of Chem ist ry. Schm idt , Douglas, Michael St al, Hans Rohnert , and Frank Buschm ann. 2000. Pat t ernOrient ed Soft ware Archit ect ure, Volum e 2, Pat t erns for Concurrent and Net worked Obj ect s. New York: John Wiley & Sons. Vlissides, John. 1998. Pat t ern Hat ching. Reading, MA: Addison- Wesley. Vlissides, John M., Jam es O. Coplien, and Norm an Kert h, eds. Pat t ern Languages of Program Design 2. 1996. Reading, MA: Addison- Wesley. Weast , Robert C., ed. 1983. CRC Handbook of Chem ist ry and Physics, 63rd ed. Boca Rat on, FL: CRC Press. Wolczko, Mario, and Randall B. Sm it h. 1996. Prot ot ype- Based Applicat ion Const ruct ion Using SELF 4.0. ht t p: / / www.cs.ucsb.edu/ oocsb/ self/ . Here is a list of errors in "Design Patterns Java Workbook" that you should correct in your copy of the book. (And please accept my apologies for these errors.) •
• • • • • • •
On page 27, Adapter: In Figure 3.6, move the i:int argument from getColumnCount() to getColumnName(). (Thanks to Simon Bennett for pointing this out.) On page 53, Composite: In Figure 5.2, change the MachineComponent class's method to be getMachineCount(). (Thanks to Wagner Truppel for this catch.) On page 58, Composite: In the last line of code on the page, change the public isTree(Set s) method to have protected visibility. (Thanks to Wagner Truppel.) On page 62, Composite: In Figure 5.8 and in the corresponding Figure 8.4 in the Solutions (page 370), change the Alternation class to ProcessAlternation. (Thanks to Claus Nielsen.) On page 91, Observer: In Figure 9.2, modify the burnPanel() and thrustPanel() methods in the ShowBallistics_1 class to have type return type BallisticsPanel_1. (Thanks to Wagner Truppel.) On page 112, Mediator: In Figure 10.6, modify the getTubs() method in the Machine class to have return type Set. (Thanks to Wagner Truppel.) Also change the Mediator class's setMachine() method name to just set. (Thanks to Thierry Matusiak.) On page 128, Proxy: Near the end of the first paragraph, change Rocket_Impl to be RocketImpl_Stub. (Thanks to Thierry Matusiak.) On page 251, Command: Drop the first line of code on the page that sets an unused local fileMenu variable. (Thanks to Wagner Truppel.)
• • • • •
On page 256, Command: In Challenge 24.4, change the method name from setShutdownHook() to setMarkMoldIncompleteHook(). (This one I found myself!) On page 265, Interpreter: In the second paragraph, change HasMoreMaterial to HasMaterial. (Thanks to Wagner Truppel.) On page 359, Solutions: Strike the third-to-last bullet that erroneously suggests that an abstract class can be protected or private. (Thanks to Shun Nin Lau for pointing this out.) On page 373, Solution 6.3: Change the MachineDriver interface and its implementors to have methods getQueueMax():int, start(), and stop(). Add a getQueue() method to the MachineController class. (Thanks to Alec Noronha for finding this.) On page 384, Solution 10.4: Add to the Tub() constructor the statement this.mediator = mediator; (Thanks to Wagner Truppel.)