Software Arch & Design
Software Arch & Design CS 6310
Popular in Course
Popular in ComputerScienence
This 0 page Class Notes was uploaded by Alayna Veum on Monday November 2, 2015. The Class Notes belongs to CS 6310 at Georgia Institute of Technology - Main Campus taught by Staff in Fall. Since its upload, it has received 58 views. For similar materials see /class/234135/cs-6310-georgia-institute-of-technology-main-campus in ComputerScienence at Georgia Institute of Technology - Main Campus.
Reviews for Software Arch & Design
Report this Material
What is Karma?
Karma is the currency of StudySoup.
Date Created: 11/02/15
11 Heuristics and Coffee i39USE 1quot Pt39szr Pager 7 13m add mm mm brews I A No me mh f 3qu m lHn m5 quot1 rbrcwll 39Eii 1 5quot ug EHms MM M Tu Wquot i 4H M Over the past dozen years I have taught and continue to teach 00 design to professional software developers My courses are divided into morning lectures and afternoon exer cises For the exercises I will divide the class up into teams and have them solve a design problem using UML The next morning we choose one or two teams to present their solu tions on a white board and we critique their designs I have taught these courses hundreds of times and have noticed that there is a group of design mistakes that are commonly made by the studenm This chapter presenm a few of the most common errors shows why they are errors and addresses how they can be cor rected Then it goes on to solve the problem in a way that I think resolves all the design forces nicely 129 130 Chapter 11 Heuristics and Coffee The Mark IV Special Coffee Maker During the first morning of an OOD class I present the basic definitions of classes objects relationships methods polymorphism and so on At the same time I present the basics of UML Thus the students learn the fundamental concepts vocabulary and tools of object oriented design During the afternoon I give the class the following exercise to work on I ask them to design the so ware that controls a simple coffee maker Here is the specificationl give them1 The Mark IV Special Co ee Maker The Mark IV Special makes up to 12 cups of coffee at a time The user places a filter in the filter holder fills the filter with coffee grounds and slides the filter holder into its receptacle The user then pours up to 12 cups of water into the water strainer and presses e Brew button The water is heated until boiling The pressure of the evolving steam forces the water to be sprayed over the coffee grounds and coffee drips through the filter into the pot The pot is kept warm for extended periods by a warmer plate which only turns on if there is coffee in the pot If the pot is removed from the warmer plate while water is being sprayed over the grounds the flow of water is stopped so that brewed coffee does not spill on the warmer plate The following hardware needs to be monitored or con trolled The heating element for the boiler It can be turned on or off 0 The heating element for the warmer plate It can be turned on or off 0 The sensor for the warmer plate It has three states warme rEmp ty potEmpty po tNotEmpty 0 A sensor for the boiler which determines whether there is water present It has two states boi lerEmpty or boi lerNotEmpty The Brew button This is a momentary button that starts the brewing cycle It has an indicator that lights up when the brewing cycle is over and the coffee is ready 0 A pressurereliefvalve that opens to reduce the pres sure in the boiler The drop in pressure stops the flow of water to the filter It can be opened or closed The hardware for the Mark IV has been designed and is currently under development The hardware engineers have even provided a lowlevel API for us to use so we don t have to write any bittwiddling lO driver code The code for these interface functions is shown in Listing 1171 lfthis code looks strange to you just keep in mind that it was writ ten by hardware engineers 1 This problem comes from my first book Martin1995 p 60 The Mark IV Special Coffee Maker LE ngll l CofeeMakerAPIjaVa public interface CoffeeMakerAPI public static CoffeeMakerAPI api null set by main This function returns the status of the warmeriplate sensor This sensor detects the presence of the pot and whether it has coffee in it public int getWarmerPlateStatus final int WARMERiEMPTY m final int POTiEMPTY 1 final int POTiNOTiEMPTY 2 public static public static public static ww This function returns the status of the boiler switch The boiler switc is a float switch that detects if there is more than 12 cup of water in the boiler public int getBoilerStatus public static final int BOILERiEMPTY m public static final int BOILERiNOTiEMPTY h ww This function returns the status of the brew button The brew button is its state remembered state and then resets that state to BREW BUTTONiNOTipUSHED Thus even if this function is polled at a Very slow rate it will still detect when the brew button is pus e public int getBrewButtonStatus public static final int BREWiBUTTONipUSHED m public static final int BREWiBUTTONiNOTipUSHED h ww This function turns the heating element in the boiler on or o public Void setBoilerStateint boilerStatus public static final int BOILERioN 0 public static final int BOILERioFF l ww This function turns the heating element in the warmer plate on 0 off public Void setWarmerStateint warmerState public static final int WARMERioN 0 132 Chapter 11 Heuristics and Coffee Listing 11 1 Continued CofeeMakerABI java public static final int WARMERioFF l This function turns the indicator light on or off The indicator light should be turned on at the end of the brewing cycle It should be turned off when the user presses the brew button public void setlndicatorStateint indicatorState public static final int lNDlCATORioN m public static final int lNDlCATORioFF l This function opens and closes the pressureerelief valve When this valve is closed steam pressure in the coffee filter When the valve is open the steam in the boiler escapes into the environment and the water in the boiler will not spray out over the filter public void setRelieralveStateint relieralveState public static final int VALVEiopEN public static final int VALVEicLOSED A challenge If you want a challenge stop reading here and try to design this software yourself Remember that you are designing the software for a simple embedded realtime system What I expect of my students is a set of class diagrams sequence diagrams and state machines A common but hideous coffee maker solution By far the most common solution that my students present is the one in Figure 1171 In this diagram we see the central CoffeeMaker class surrounded by minions that control the various devices The CoffeeMaker contains a Boiler a WarmerBlate a Button and a Light The Boiler contains a BoilerSensor and a BoilerHeater The WarmerBlate contains a PlateSensor and a BlateHeater Finally there are two base classes Sensor and Heater that act as par ents to the Boiler and WarmerBlate elemenm respectively The Mark IV Special Coffee Maker 133 CoffeeMaker Boiler WarmerPlate BoilerSensor PlaheHeaher PlateSensor BoilerHeater Figure Hyperconcrete coffee maker It is hard for beginners to appreciate just how hideous this structure is There are quite a few rather serious errors lurking in this diagram Many of these errors would not be noticed until you actually tried to code this design and found that the code was absurd But before we get to the problems with the design itself let s look at the problems with the way the UML is created Missing methods The biggest problem that Figure 1171 exhibim is a complete lack ofmethods We are writ ing a program here and programs are about behavior Where is the behavior in this dia gram When designers create diagrams without methods they may be partitioning the so ware on something other than behavior Partitionings that are not based upon behavior are almost always significant errors It is the behavior of a system that is the first clue to how e software should be partitioned 134 Chapter 11 Heuristics and Coffee Vapor classes We can see how poorly partitioned this particular design is if we consider the methods we might put in the class Light Clearly the Light object just wants to be turned on or turned off Thus we might put an on and off method in class Light What would the implementation of those function look like See Listing 1172 Listing 11 2 Light java CoffeeMakerAPIapi setIndicatorStateCoffeeMakerAPIINDICATOFgON public void off CoffeeMakerAPIapi setIndicatorStateCoffeeMakerAPIINDICATOFgoFF There are some peculiar things about class Light First it has no variables This is odd since an object usually has some kind of state that it manipulates What s more the on and off methods simply delegate to the set Indi catorstate method of the CoffeeMakerAPI So apparently the Light class is nothing more than a call translator It s not really doing anything useful This same reasoning can be applied to the Button Boiler and WarmerPlate classes They are nothing more than adapters that translate a function call from one form to another Indeed they could be removed from the design altogether without changing any of the logic in the CoffeeMaker class That class would simply have to call the Coff eeMakerAPI directly instead of through the adapters By considering the methods and then the code we have demoted these classes from the prominent position the hold in Figure 1171 to mere place holders without much rea son to exist For this reason I call them vapor classes Imaginary abstraction Notice the Sensor and Heater base classes in Figure 1171 The previous section should have convinced you that their derivatives were mere vapor but what about base classes themselves On the surface they seem to make a lot of sense And yet there doesn t seem to be any place for them Abstractions are tricky things We humans see them everywhere but many are not appropriate to be turned into base classes These in particular have no place in this esign We can see this by asking ourselves who uses No class in the system actually makes use of the Sensor or Heater class lfnobody uses them what reason do they have to exist Sometimes we might tolerate a base class The Mark IV Special Coffee Maker 135 that nobody uses if it supplied some common code to the derivatives but these bases have no code in them at all At best their methods are abstract Consider for example the Heater interface in Listing 1173 A class with nothing but abstract functions and that no other class uses is officially useless Listing 11 3 Heater java public interface Heater public void turnOn public void turnoff The Sensor class Listing 1174 is worse Like Heater it has abstract methods and no users What s worse is that the return value of its sole method is ambiguous What does the sense method return In the Boi lerS ensor it returns two possible values but in WarmerPlateSensor it returns three possible values In short we cannot specify the contract of the Sensor in the interface The best we can do is say that sensors may return ints This is pretty weak Listing 11 4 Sensor java public interface Sensor public int sense What really happened here is that we read through the specification found a bunch of likely nouns made some inferences about their relationships and then created a UML dia gram based on that reasoning If we accepted these decisions as an architecture and imple mented them the way they stand then we d wind up with an allpowerful CoffeeMaker class surrounded by vaporous minions We might as well program it in C God classes Everybody knows that god classes are a bad idea We don t want to concentrate all the intelligence of a system into a single object or a single function One of the goals of OOD is the partitioning and distribution of behavior into many classes and many functions It turns out however that many object models that appear to be distributed are really the abode of gods in disguise Figure 1171 is a prime example At first glance it looks like there are lots of classes with interesting behavior But as we dri down into the code that would imple 136 Chapter 11 Heuristics and Coffee ment those classes we find that only one of those classes CoffeeMaker has any interest ing behavior and the rest are all imaginary abstractions or vapor classes A Coffee Maker Solution Solving the coffee maker problem is an interesting exercise in abstraction Most develop ers new to 00 find themselves quite surprised by the result The trick to solving this problem is to step back and separate its details from its essen tial nature Forget about boilers valves heaters sensors and all the little details and con centrate on the underlying problem What is that problem The problem is how do you make coffee How do you make coffee The simplest and most common solution to this problem is to pour hot water over coffee grounds and to collect the resulting infusion in some kind of vessel Where do we get the hot water from Let s call it a HotWaterSource Where do we collect the coffee Let s call it a ContainmentVesselz Are these two abstractions really classes Does a HotWaterSource have behavior that could be captured in software Does a Conta inmentVessel do something that so ware could control If we think about the Mark IV unit we could imagine the boiler valve and boiler sensor playing the role ofthe HotWaterSource The HotWaterSource would be responsible for heating the water and delivering it over the coffee grounds to drip into the ContainmentVessel We could also imagine the warmer plate and its sen sor playing the role of the ContainmentVessel It would be responsible for keeping the contained coffee warm and also for letting us know whether there was any coffee left in the vessel Crossed wires How would you capture the previous discussion in a UML diagram Figure 1172 shows one possible schema HotWaterSource and ContainmentVessel are both represented as classes and are associated by the flow of coffee Coffee Flow Figure 11 2 Crossed wires 2 That name is particularly appropriate for the kind of coffee that I like to make A Coffee Maker Solution 137 The association shows an error that 00 novices commonly make The association is made with something physical about the problem instead of with the control of software behavior The fact that coffee flows from the HotWaterSource to the Containment Vessel is completely irrelevant to the association between those two classes For example what if the software in the ContainmentVessel told the HotWatere Source when to start and stop the flow of hot water into the vessel This might be depicted as shown in Figure 1173 Notice that the ContainmentVessel is sending the start message to the Ho tWaterSource This means that the association in Figure 1172 is backwards HotWaterSource does not depend upon the ContainmentVessel at all Rather the ContainmentVessel depends upon the HotWaterSource start lt Figure 11 3 Starting the flow of hot water The lesson here is simply this Associations are the pathways through which mes sages are sent between objects They have nothing to do with the flow of physical objects The fact that hot water flows from the boiler to the pot does not mean that there should be an association from the HotWaterSource to the ContainmentVessel I call this particular mistake crossed wires because the wiring between the classes has gotten crossed between the logical and physical domains The coffee maker user interface It should be clear that something is missing from our coffee maker model We have a HotWaterSource and a Conta inmentVessel but we don t have any way for a human to interact with the system Somewhere our system has to listen for commands from a human Likewise the system must be able to report is status to is human owners Cer tainly the Mark IV had hardware dedicated to this purpose The button and the light served as the user interface Thus we ll add a Userlnterface class to our coffee maker model This gives us a triad of class es interacting to create coffee under the direction of a user Use Case 1 User pushes brew button OK given these three classes how do their instances communicate Let s look at several use cases to see if we can find out what the behavior of these classes is 138 Chapter 11 Heuristics and Coffee Which one of our objects detects the fact that the user has pressed the Brew button Clearly it must be the Userlnterface object What should this object do when the Brew button is pushed Our goal is to start the ow of hot water However before we can do that we d better make sure that the ContainmentVessel is ready to accept coffee We d also better make sure that the HotWaterSource is ready If we think about the Marle we re making sure that the boiler is full and that the pot is empty and in place on the warmer So the first thing our Userlnterface object does is to send a message to the Hot WaterSource and the Conta inmentVessel to see if they are ready This is shown in Figure l 174 lsReady gt A lsReady Figure 11 4 Brew button pressed checking for ready If either of these queries returns false then we refuse to start brewing coffee The Userlnterface object can take care of letting the user know that his request was denied In the Mark IV case we might ash the light a few times If both queries return true then we need to start the ow of hot water Probably the Userlnterface object should send a Start message to the HotWaterSource The Ho tWaterSource will then start doing whatever it needs to do to get hot water owing In the case ofthe Mark IV it will close the valve and turn on the boiler Figure 1175 shows the completed scenario Figure 11 5 Brew button pressed complete A Coffee Maker Solution 139 Use Case 2 Containment vessel not ready In the Mark IV we know that the user can take the pot off the warmer while coffee is brew ing Which one of our objects would detect the fact that the pot had been removed Cer tainly it would be the ContainmentVessel The requirements for the Mark IV tell us that we need to stop the flow of coffee when this happens Thus the Containment Vessel must be able to tell the HotWaterSource to stop sending hot water Likewise it needs to be able to tell it to start again when the pot is replaced Figure 1176 adds the new methods 1alsReady gt 1b Pause 2b Resume gt ZalsReady ure 11 6 Pausing and resuming the flow of hot water Use Case 3 Brewing complete At some point we will be done brewing coffee and we ll have to turn off the flow of hot water Which one of our objects knows when brewing is complete In the Mark IV s case the sensor in the boiler tells us that the boiler is empty So our HotWaterSource would detect this However it s not hard to envision a coffee maker in which the Containment Vessel would be the one to detect that brewing was done For example what if our cof fee maker was plumbed into the water mains and therefore had an infinite supply of water What if the water was heated by an intense microwave generator3 as it flowed through the pipes into a thermally isolated vessel What if that vessel had a spigot from which users got their coffee In this case it would be a sensor in the vessel that would know that it was full and that hot water should be shut off The point is that in the abstract domain ofthe HotWaterSource and Containment Vessel neither is an especially compelling candidate for detecting completion of the brew My solution to that is to ignore the issue I ll assume that either object can tell the others that brewing is complete Which objects in our model need to know that brewing is complete Certainly the Userlnterface needs to know since in the Mark IV it must turn the light on It should 3 OKI m having abit of in But What if 140 Chapter 11 Heuristics and Coffee also be clear that the HotWaterSource needs to know that brewing is over because it ll need to stop the flow of hot water In the Mark IV it ll shut down the boiler and open the valve Does the ContainmentVesseI need to know that brewing is complete Is there anything special that the Conta inmentVesseI needs to do or to keep track of once the brewing is complete In the Mark IV it s going to detect an empty pot being put back on the plate signalling that the user has poured the last of the coffee This causes the Mark IV to turn the light out So yes the ContainmentVesseI needs to know that brewing is complete Indeed the same argument can be used to say that the Userlnterface should send the Start message to the ContainmentVesseI when brewing starts Figure 1177 shows the new messages Note that I ve shown that either HotWaterSource or Cont ai nmentVess 1 e 1 can send the Done message 2c Dorie lt 1alsReady gt HolWai er Source User Imerface 2d Dorie T gt 3aSiart 1b Pause 1c Donal T 2b Resume Com menl m Figure 1 7 Detecting when brewing is complete a 4 ZasReady 1d Dorie Use Case 4 Coffee all gone The Mark IV shuts off the light when brewing is complete and an empty pot is placed on the plate Clearly in our object model it is the ContainmentVesseI that should detect this It will have to send a Complete message to the UserInterface Figure llag shows the completed collaboration diagram From this diagram we can draw a class diagram with all the associations intact This diagram holds no surprises You can see it in Figure 1179 Implementing the abstract model Our object model is reasonably well partitioned We have three distinct areas of responsi bility and each seems to be sending and receiving messages in a balanced way There does not appear to be a god object anywhere Nor do there appear to be any vapor classes So far so good but how do we implement the Mark IV in this structure Do we just implement the methods of these three classes to invoke the Coff eeMake rAPI This A Coffee Maker Solution 141 2c Done 4 1b Pause 2b Resume gt gt ZasReady 1d Done gt 4aSlart Figure 11 8 Coffee all gone Hot Water user Interface Contai nment Vessel Figure 11 9 Class diagram would be a real shame We ve captured the essence of what it takes to make coffee It would be pitifully poor design if we were to now tie that essence to the Mark IV In fact I m going to make a rule right now None of the three classes we have created must ever know anything about the Mark IV This is the Dependency Inversion Principle DIP We are not going to allow the highlevel coffee making policy of this system to depend upon the lowlevel implementation OK then how will we create the Mark IV implementation Let s look at all the use cases again but this time let s look at them from the Mark IV point ofview Use Case 1 User pushes Brew button Looking at our model how does the UserInterface know that the Brew button has been pushed Clearly it must call the CoffeeMakerAPI getBrewButtonStatus function Where should it call this function We ve already decreed that the UserInterface class itself cannot know about the CoffeeMakerAPI So where does this call go 142 Chapter 11 Heuristics and Coffee We ll apply the DIP and put the call in a derivative of Userlnterface See Figure 1171 0 for details Hot Water Source User Interface slartBrewing Contai nment Vessel Figure 11 10 Detecting the Brew button M4Userlnterface checkBullon We ve derived M4Userlnterface from Userlnterface and we ve put a check Button method in M4Userlnterface When this function is called it will call the CoffeeMakerAPI getBrewButtonStatus function If the button has been pressed it will invoke the protected startBrewing method of Userlnterface Listings 1175 and 1176 show how this would be coded LE ngll S M4Userlnterface java public class M4Userlnterface extends Userlnterface private void checkButton buttonStatus CoffeeMakerAPlapigetBrewButtonStatus if buttonStatus of startBrewing eeMakerAPIBREWiBUTTONipUSHED l LE ngll G Userlnterface java public class Userlnterface private HotWaterSource hws private ContainmentVessel cv public void done public void complete protected void startBrewing if hws isReady ampamp cv isReady hws start cvstart7 A Coffee Maker Solution 143 You might be wondering why I created the protected startBrewing method at all Why didn thust call the start functions from M4UserInterface The reason is simple but significant The isReady tests and the consequential calls to the start methods of the HotWaterSource and the ContainmentVessel are highlevel policy that the UserInterface class should possess That code is valid irrespective of whether we are implementing a Mark IV and should therefore not be coupled to the Mark IV deriv ative You will see me make this same distinction over and over again in this example I keep as much code as I can in the highlevel classes The only code Iput into the deriva tives is code that is directly and inextricably associated with the Mark IV Implementing the isReady functions How are the isReady methods of HotWaterSource and ContainmentVessel implemented It should be clear that these are really just abstract methods and that these classes are therefore abstract classes The corresponding derivatives M4HotWaterSource and M4 ContainmentVessel will implement them by calling the appropriate Coffee Make rAPI functions Figure 11711 shows the new structure and Listings 1177 and 1178 show the implementation of the two derivatives User Interface on e Sianarewmg T A isReadyo Containment Vessel M4HotWater A IsReadyo checkBulton Source gt M4Comainment Vessel Figure 11 11 Implementing the isReady methods LE ngll 7 M4HotWaterSource java public class M4HotWaterSource extends HotWaterSource public boolean isReady int boilerStatus CoffeeMakerAPIapigetBoilerStatus return boilerStatus CoffeeMakerAPIBOILER NOT EMPTY 144 Chapter 11 Heuristics and Coffee LE ngll B M4ContainmentVessel java public class M4ContainmentVessel extends ContainmentVessel public boolean isReady int plateStatus CoffeeMakerAPlapigetWarmerPlateStatus return plateStatus CoffeeMakerAPlPOTiEMPTY Implementing the start functions The start method of HotWaterSource is just an abstract method that is implemented by M4HotWaterSource to invoke the CoffeeMakerAPI functions that close the valve and turn on the boiler As I wrote these functions I began to get tired of all the CoffeeMakerAPI api XXX structures 1 was writing so I did a little refactoring at the same time The result is in Listing 1179 Ij ingll 9 M4HotWaterSourcejava public class M4HotWaterSource extends HotWaterSource CoffeeMakerAPl api public M4HotWaterSourceCoffeeMakerAPl api this api api public boolean isReady int boilerStatus api getBoilerStatus return boilerStatus apiBOlLER7NOT7EMPTY public void startO apisetRelieralveStateapiVALVE7CLOSED apisetBoilerStateapiBOlLER70N The start method for the ContainmentVessel is a little more interesting The only action that the M4 ContainmentVessel needs to take is to remember the brewing state of the system As we ll see later this will allow it to respond correctly when pots are placed on or removed from the plate Listing 1171 0 shows the code LE ngll IO M4ContainmentVessel java public class M4ContainmentVessel extends ContainmentVessel private CoffeeMakerAPl api private boolean isBrewing public M4ContainmentVesselCoffeeMakerAPl api this api api isBrewing false public boolean isReady A Coffee Maker Solution 145 Listing 11 10 Continued M4Conta inmentVessel java int plateStatus api getWarmerPlateStatus return plateStatus apiPOT7EMPTY public void start isBrewing true How does M4 UserInterface checkButton get called This is an interesting point How does the flow of control ever get to a place at which the CoffeeMakerAPl getBrewButtonStatus function can be called For that matter how does the flow of control get to where any of the sensors can be detected Many of the teams who try to solve this problem get completely hung up on this point Some don t want to assume that there s a multithreading operating system in the coffee maker and so they want to use a polling approach to the sensors Others want to put multithreading in so that they don t have to worry about polling I ve seen this particular argument go back and forth for an hour or more in some teams The mistake that these teams are making whichl eventually point out to them after letting them sweat a bit is that the choice between threading and polling is completely irrelevant This decision can be made at the very last minute without harm to the design Therefore it is always best to assume that messages can be sent asynchronously as though there were independent threads and then put the polling or threading in at the last minute The design so far has assumed that somehow the flow of control will asynchronously get into the M4Userlnterface object so that it can call CoffeeMakerAPl getBrewe Buttonstatus Now let s assume that we are working in a very minimal JVM that does not support threading This means we re going to have to poll How can we make this work Consider the Pollable interface in Listing 11711 This interface has nothing but a poll method Now what if M4Userlnterface implemented this interface What if the main program hung in a hard loop just calling this method over and over again Then the flow of control would continuously be reentering M4 Userlnterface and we could detect the Brew button Listing 11 11 Pollable java public interface Pollable public void poll Indeed we can repeat this pattern for all three of the M4 derivatives Each has its own sensors it needs to check So as shown in Figure 11712 we can derive all of the M4 deriv atives from Pollable and call them all from main 146 Chapter 11 Heuristics and Coffee l Hot Water Source l Containnent Vessel User Interface aanBrewing checkBuIlon y M4HotWater Source Figure 11 12 Pollable coffee maker Listing 11712 shows what the main function might look like It is placed in a class called CoffeeMaker The main function creates the implemented version of the api and then creates the three M4 componenm It calls init functions to wire the compo nenm up to each other Finally it hangs in an infinite loop calling poll on each of the components in turn Listing 11 12 CoffeeMaker java public class CoffeeMaker public static Void mainStringl args CoffeeMakerAPl api new M4CoffeeMakerAPlImplementation M4Userlnterface ui new M4Userlnterfaceapi M4HotWaterSource hws new M4HotWaterSourceapi M4ContainmentVessel CV new M4ContainmentVesselapi uiinithwscv hwsinituicv cvinituihws whiletrue 1 poll hwspoll cvpoll A Coffee Maker Solution 147 Listing 11 12 Continued Cof feeMaker java l Now it should be clear how the M4 Userlnterf ace CheCkButton function gets called Indeed it should be clear that this function is really not called Che CkButton It is called poll Listing 11713 shows what M4Userlnterface looks like now LE ngll l3 M4Userlnterface jaVa public Class M4Userlnterface extends Userlnterface implements Pollable private CoffeeMakerAPl api priVate HotWaterSource hws private ContainmentVessel CV public Void initHotWaterSource hws ContainmentVessel CV this hws 5 this CV CV publiC M4UserlnterfaCeCoffeeMakerAPl api thi ap api priVate Void poll int buttonStatus 7 api getBrewButtonStatus if buttonStatus ii W ap1BREW7BUTTON7PUSHED startBrew1ng7 Completing the Coffee Maker The reasoning used in the previous sections can be repeated for each of the other compo nenm of the coffee maker The result is shown in Listings 11714 through 11721 The bene ts of this design Despite the trivial nature of the problem this design shows some very nice characteristics Figure 11713 shows the structure I have drawn a line around the three abstract classes These are the classes that hold the highlevel policy of the coffee maker Notice that all dependencies that cross the line point inward Nothing inside the line depends upon any thing outside Thus the abstractions are completely separated from the details The abstract classes know nothing of buttons lighm valves sensors nor any other of the detailed elements of the coffee maker By the same token the derivatives are dom1 nated by those details 148 Chapter 11 Heuristics and Coffee M4Userlnherface checkBuIlon Figure 11 13 Coffee maker components Note that the three abstract classes could be reused to make many different kinds of coffee machines We could easily use them in a coffee machine that is connected to the water mains and uses a tank and spigot It seems likely that we could also use them for a coffee vending machine Indeed I think we could use it in an automatic tea brewer or even a chicken soup maker This segregation between highlevel policy and detail is the essence of object oriented design H 0w did I really come up with this design I did not just sit down one day and develop this design in a nice straightfoward manner Indeed my very first design for the coffee maker looked much more like Figure 1171 How ever I have written about this problem many times and have used it as an exercise while teaching class after class So this design has been refined over time The code you see below was created test first using the unit tests in Listing 11722 I created the code based upon the structure in Figure 11713 but put it together incrementally one failing test case at a time 4 BeckZOOZ A Coffee Maker Solution 149 I am not convinced that the test cases are complete If this were more than an example program I d do a more exhaustive analysis on the test cases However I felt that such an analysis would have been overkill for this book LE ngll l4 Userlnterfacejava public abstract class Userlnterface private HotWaterSource hws private ContainmentVessel cv protected boolean isComplete public Userlnterface isComplete true thiscv public void initHotWaterSource hws ContainmentVessel cv this hw w 7 cv public void complete isComplete true completeCycle protected void startBrewing if hws isReady ampamp cv isReady e false hwsstart cvstart public abstract void do e public abstract void completeCycle LE ngll IS M4Userlnterface java public class M4Userlnterface extends Userlnterface implements Pollable private CoffeeMakerAPI api public M4UserlnterfaceCoffeeMakerAPI api t is api api public void poll in bu ttonStatus api getBrewButtonStatus if buttonStatus apiBREW7BUTTON7PUSHED startBrewing public void don api s etIndicatorStateapiINDICATORiON 150 Chapter 11 Heuristics and Coffee Listing 11 15 Continued M4Userlnterface java public void completeCycle apisetlndicatorStateapilNDlCATOR70FF LE ngll l HotWaterSource ja public abstract class HotWaterSource private Userlnterface ui private ContainmentVessel cv protected boolean isBrewing public HotWaterSource isBrewing fa 9 public void initUserlnterface ui ContainmentVessel cv this ui ui this cv cv public void start isBrewing true startBrewing7 public void done isBrewing false protected void declareDone uidone cvdone isBrewing false public public public public abstract boolean isReady abstract void startBrewing abstract void pause abstract void resume LE ngll l7 M4HotWaterSource java public class M4HotWaterSource extends HotWaterSour ce implements Pollable private CoffeeMakerAPl api public M4HotWaterSourceCoffeeMakerAPl api this api api public boolean isReady int boilerStatus api getBoilerStatus return boilerStatus apiBOlLER7NOT7EMPTY A Coffee Maker Solution Listing 11 17 Continued M4HotWaterSource java public void startBrewing apisetRelieralveStateapiVALVE7CLOSED apisetBoilerStateapiBOILER7 N public void poll int boilerStatus if isBrewing apigetBoilerStatus7 if boilerStatus apiBOILER7EMPTY apisetBoilerStateapiBOILER public void pause apisetBoilerStateapiBOILER70FF apisetRelieralveStateapiVALVE70PEN public void resume apisetBoilerStateapiBOILER70N apisetRelieralveStateapiVALVE7CLOSED 7 FF apisetRelieralveStateapiVALVE7CLOSED declareDone LE ngll ls ContainmentVessel java public void startO isBrewing e isComplete public void done isBrewing private Userlnterface ui private HotWaterSource hws public abstract class ContainmentVessel protected boolean isBrewing protected boolean isComplete public ContainmentVessel isBrewing alse isComplete true thi i this hws hws public void initUserInterface ui HotWaterSource hws s ui u false false Chapter 11 Heuristics and Coffee Listing 11 18 Continued Conta inmentVessel java protected void declareComplete isComplete true uicomplete protected void containerAvailable hwsresume protected void containerUnavailable hwspause public abstract boolean isReady LE ngll l9 M4ContainmentVessel java public class M4ContainmentVessel extends ContainmentVessel implements Pollable private CoffeeMakerAPl api private int lastPotStatus public M4ContainmentVesselCoffeeMakerAPl api thisapi api lastPotStatus apiPOT7EMPTY public boolean isReady int plateStatus api getWarmerPlateStatus return plateStatus apiPOT7E public void poll int potStatus api getWarmerPlateStatus lastPotStatus if potStatus i if isBrewing handleBrewingEventpotStatus else if isComplete alse handlelncompleteEventpotStatus lastPotStatus potStatus private void handleBrewingEventint potStatus if potStatus piPOT7NOT7EMPTY containerAvailable api setWarmerStateapiWARMER ON else if potStatus apiWARMER7EMPTY containerUnavailable apisetWarmerStateapiWARMER70FF else potStatus apiPOT7EMPTY containerAvailable apisetWarmerStateapiWARMER70FF A Coffee Maker Solution 153 Listing 11 19 Continued M4Conta inmentVessel java private Void handlelncompleteEVentint potStatus if potStatus apiPOT7NOT7EMPTY api setWarmerStateapiWARMER ON else if potStatus apiWARMER7EMPTY api setWarmerStateap WARMERioFF else potStatus a iPOT7EMPTY apisetWarmerStateapiWARMER70FF declareComplete Listing 11 20 Pol lable java public interface Pollable public Void poll LE ngll Zl CoffeeMaker java public class CoffeeMaker public static Void mainString CoffeeMakerAPl api new M4Coffee args MakerAPllmplementation M4Userlnterface ui new M4Userlnterfaceapi M4HotWaterSource hws new M4HotWaterSourceapi M4ContainmentVessel CV 7 new M4ContainmentVesseliapi uiinithwscv hwsinituicv cvinituihws whiletrue uipoll7 hwspoll7 cvpoll7 LE ngll ZZ TestCoffeeMaker java import junit framework TestCase import junitswinguiTestRunner class CoffeeMakerStub implements boolean buttonPressed boolean lightOn boolean boilerOn boolean ValVeClosed boolean plateOn boolean boilerEmpty boolean potPresent boolean potNotEmpty quotO s n CoffeeMakerAPl public 154 Chapter 11 Heuristics and Coffee Listing 11 22 Continued TestCoffeeMaker java public CoffeeMakerStub buttonPressed false lightOn false boilerOn false ValVeClosed true potNotEmpty false public int getWarmerPlateStatus if lpotPresent return WARMERiEMPTY else if potNotEmpty return POTiNOTiEMPTY else return POTiEMPTY public int getBoilerStatus return boilerEmpty BOILERiEMPTY BOILER NOT EMPTY public int getBrewButtonStatus if buttonPressed buttonPressed false return BREWiBUTTONipUSHED else return BREWiBUTTONiNOTipUSHED public Void setBoilerStateint boilerStatus boilerOn boilerStatus BOILERioN public Void setWarmerStateint warmerState plateOn warmerState WARMERioN public Void setlndicatorStateint indicatorState lightOn indicatorState lNDlCATORioN public Void setRelieralVeStateint relieralVeState ValVeClosed relieralVeState VALVEicLOSED public class TestCoffeeMaker extends TestCase public static Void mainStringl ar gs TestRunnermainnew StringquotTestCoffeeMakerquot A Coffee Maker Solution 155 Listing 11 22 Continued TestCoffeeMaker java public TestCoffeeMakerString name supername M4Userlnterface ui M4HotWaterSource hws M4ContainmentVessel cv CoffeeMakerStub api private private private private public void setUp throws Exception new CoffeeMakerStub api ui new M4Userlnterfaceapi hws new M4HotWaterSourceapi cv new M4ContainmentVesselapi ui inithws cv hwsinitui cv cv initui hws private void poll ui poll hwspoll cvpoll public void tearDown throws Exception void testlnitialConditions throws Exception public oll assert assert assert assertapi valveClosed true public void testStartNoPot throws Exception oll api buttonPressed true false api potPresent 7 poll assert assert assert assertapi valveClosed true public void testStartNoWater throws Exception oll api buttonPressed true api boilerEmpty true poll assertapi boilerOn false assert lightOn false alse assertapi valveClosed true Chapter 11 Heuristics and Coffee Listing 11 22 Continued TestCoffeeMaker java public Void testGoodStart throws Exception normalStartO assertapi boilerOn true assertapilightOn false assertapi plateOn false assertapiValVeClosed true private Void normalStart poll api boilerEmpty false api buttonPressed true poll public Void testStartedPotNotEmpty throws Exception normalStart apipotNotEmpty true poll assert assert api ValVeClosed true public Void testPotRemoVedAndReplacedWhileEmpty n throws Excep i normalStart api potPresent false poll assertapi boilerOn false assertapilightOn false assertapi plateOn false assertapiValVeClosed false api potPresent true poll assert assert assert assertapiValVeClosed true public Void test il N t L Ll Jquot p throws Exception normalEill api potPresent false api potPresent true apipotNotEmpty false ol p l A Coffee Maker Solution 157 Listing 11 22 Continued TestCoffeeMaker java assert assert api ValVeClosed true private Void normalFill normalStart apipotNotEmpty true poll public Void test il N t L Lla 4N tpmpt throws Exception normalFill api potPresent false poll api potPresent true poll assert api boilerOn apiplateOn 77 rue api ValVeClosed true assert public Void testBoilerEmptyPotNotEmpty throws Exception assertapi plateOn 77 rue assertapiValVeClosed true private Void normalBrew normalFill api boilerEmpty true poll public Void testBoilerEmptiesWhilePotRemoVed throws Exceptio normalFill api potPresent false poll api boilerEmpty true poll asserta i boilerOn false assertapilightOn true assertapiplateOn false assertapiValVeClosed rue api potPresent true poll assertap assert assertapiplateOn true assert api ValVeClosed rue 158 Chapter 11 Heuristics and Coffee Listing 11 22 Continued TestCoffeeMaker java public void testEmptyPotReturnedAfter throws Exception normalBrew apipotNotEmpty false po ll 7 assertapiboiler0n ii false assertapilight0n false assertapiplateOn false assertapivalveClosed true OOVerklll This example has certain pedagogical advantages It is small easy to understand and shows how the principles of OOD can be used to manage dependencies and separate con cerns On the other hand is very smallness means that the benefim of that separation probably do not outweigh the costs If we were to write the Mark IV coffee maker as a finite state machine we d find that it had seven states and 18 transitions5 We could encode this into 18 lines of SMC code A simple main loop that polls the sensors would be another ten lines or so and the action functions that the FSM would invoke would be another couple of dozen In short we could write the whole program in less than a page of code If we don t count the tests the 00 solution of the coffee maker is five pages of code There is no way that we can justify this disparity ln larger applications the benefits of dependency management and the separation of concerns clearly outweigh the costs of OOD However in this example the reverse is true 5 lVIartin1995 p 65 Notes 159 Notes Martin1995 RobeIt C Martin Designing Object Oriented C Applications using the Booch Method Upper Saddle River NI Prentice Hall 1995 Beck2002 Kent Beck TestDriven Development Reading Mass AddisonWesley 2002