unit 5 builder

105
Unit 5 Builder Summary prepared by Kirk Scott 1

Upload: jackie

Post on 24-Feb-2016

37 views

Category:

Documents


0 download

DESCRIPTION

Unit 5 Builder. Summary prepared by Kirk Scott. Design Patterns in Java Chapter 15 Builder. Summary prepared by Kirk Scott. The Introduction Before the Introduction. All patterns occur in some context The book’s example occurs in the context of parsing - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Unit 5 Builder

1

Unit 5Builder

Summary prepared by Kirk Scott

Page 2: Unit 5 Builder

2

Design Patterns in JavaChapter 15

Builder

Summary prepared by Kirk Scott

Page 3: Unit 5 Builder

3

The Introduction Before the Introduction

• All patterns occur in some context• The book’s example occurs in the context of

parsing• Parsing overall is not a small topic, so

explaining background takes up some time• I also present an example where input comes

through a GUI rather than through parsing

Page 4: Unit 5 Builder

4

Builder

• One explanation for the use of the builder class is that at a given time, not all of the information needed (construction parameters) may be available to do construction

• Construction parameters may have to be parsed from some input string

• Or they may come in from some kind of user interface

• Actual construction has to be delayed

Page 5: Unit 5 Builder

5

• Another explanation for using the Builder design pattern is that construction can be moderately complicated in some cases

• Instead of cluttering up the class code with these complexities, you want to have the class contain just the normal methods and simple constructors

• You can offload the more complex versions of construction into a builder class

Page 6: Unit 5 Builder

6

Book Definition of Pattern

• Book definition:• The intent of the Builder pattern is to move

the construction logic for an object outside the class to be instantiated.

• Comment mode on:• By definition, construction is being offloaded.• Offloading also makes it possible for actual

construction to be delayed.

Page 7: Unit 5 Builder

7

Ordinary Construction

• An ordinary constructor expects all of the construction parameters to be available at the time the constructor is called

• In particular, for an ordinary constructor, you would expect to have made sure that all of the construction parameters exist and are valid before calling the constructor

Page 8: Unit 5 Builder

8

Constructing when not all Parameters are Available

• In the book’s parsing example, the construction parameters have to be extracted from a String

• Not all parameters will be available up front• A builder is an intermediate object that can

hold input values until it is possible to actually construct the base object desired

Page 9: Unit 5 Builder

9

The Fireworks Reservation Example

• The book paints out the following scenario:• Suppose reservations for fireworks shows are

submitted as text strings like this example:• Date, November 5, Headcount, 250, City,

Springfield, DollarsPerHead, 9.95, HasSite, False

Page 10: Unit 5 Builder

10

• The syntax of the input string is clear• Items are separated by commas• The order may be important, or the fact that

each value is preceded by a label/variable name may mean that there is flexibility in the order

Page 11: Unit 5 Builder

11

Why a Builder Might Be Useful

• A simple approach to the construction of a reservation object illustrates the potential value of using the builder pattern

• Suppose you used a default constructor to construct an empty reservation

• Then, as the string was parsed, set methods could be called to set the instance variable values

Page 12: Unit 5 Builder

12

• The shortcoming to this approach is that midway through parsing you may encounter an error, an invalid parameter value, or a missing parameter value

• At this point the code has to be written to handle an error condition like this

• The builder design pattern gets around this potential problem by parsing and verifying before trying to construct

Page 13: Unit 5 Builder

13

• The point is that you don’t want to do ad hoc parsing and verifying in every program that may construct reservations

• The builder pattern gives an organized way of putting together the logic into a single class that can be re-used

Page 14: Unit 5 Builder

14

The Book’s Example

• The UML diagram on the next overhead shows some of the classes that will be needed for the book’s overall design

Page 15: Unit 5 Builder

15

Page 16: Unit 5 Builder

16

• The ReservationBuilder class is abstract• Obviously, this means that before we’re

finished with the example, we’ll need concrete builder classes

• In the meantime, we can see what all builder classes will have to contain

Page 17: Unit 5 Builder

17

• The ReservationBuilder class contains an abstract method named build()

• This is the method that is ultimately called that requests that an instance of ReservationBuilder construct an instance of Reservation based on/following its interaction with a parser object

• This interaction will be explained below

Page 18: Unit 5 Builder

18

• The diagram illustrates something else about the book’s scenario

• Not only has construction been offloaded• All of the logic for the Reservation class except

for a constructor has been moved to the ReservationBuilder class

Page 19: Unit 5 Builder

19

• All of the get and set methods you’d associate with the Reservation class have been moved into the ReservationBuilder class

• The idea is that the construction parameters for a reservation are fed piecemeal to a builder object by calling the set methods

• If it is possible to set each instance variable successfully, then it is possible to call build() on the builder in order to construct a reservation object

Page 20: Unit 5 Builder

20

Why is the ReservationBuilder Class Abstract?

• In general, there is no reason why the ReservationBuilder class couldn’t be concrete with a concrete build() method

• However, the authors want to illustrate several different builders with different characteristics

• The idea is that building can take on a life of its own• Because building can take on a life of its own, this

further justifies implementing it separately, not as ordinary construction

Page 21: Unit 5 Builder

21

What Does the ReservationParser Class Do?

• Before considering the concrete subclasses of the ReservationBuilder, it’s necessary to examine the role of the ReservationParser in the overall pattern

• When constructed, the ReservationParser accepts a reference to a builder object

• The ReservationParser class contains a parse() method

• This method accepts a string and tries to extract from it construction parameters for a reservation

Page 22: Unit 5 Builder

22

• The string input to the parse() method is named s

• The parse() method makes use of a method named split() from the String class

• A call to split() takes the form of s.split(“,”)• The method call returns an array of strings,

known as tokens, which are the substrings of s which are separated by commas

Page 23: Unit 5 Builder

23

• The parse() method also makes use of formatting and parsing characteristics of the Date class

• Among the things that happens with dates is that a month and day are always pushed into the next year so that reservations are for the future, not the past

Page 24: Unit 5 Builder

24

• The critical thing to observe about the parse() method is that tokens are examined one-by-one, and if they appear to be of the right type, the corresponding set() method is called on the builder object

• The code for the parse() method is shown beginning on the next overhead

Page 25: Unit 5 Builder

25

• public void parse(String s) throws ParseException• {• String[] tokens = s.split(",");• for(int i = 0; i < tokens.length; i += 2)• {• String type = tokens[i];• String val = tokens[i + 1];• if("date".comareToIgnoreCase(type) == 0)• {• Calendar now = Calendar.getInstance();• DateFormat formatter =

DateFormat.getDateInstance();• Date d = formatter.parse(val + ", “• + now.get(Calendar.YEAR));• builder.setDate(ReservationBuilder.futurize(d));• }

Page 26: Unit 5 Builder

26

• else if("headcount".compareToIgnoreCase(type) == 0)• builder.setHeadCount(Integer.parseInt(val));• else if("City".compareToIgnoreCase(type) == 0)• builder.setCity(val.trim());• else if("DollarsPerHead".compareToIgnoreCase(type) ==

0)• builder.setDollars(Double.parseDouble(val)));• else if("HasSite".compareToIgnoreCase(type) == 0)• builder.setHasSite(val.equalsIgnoreCase("true");•• /******* Observe that it's a great mystery to me how• the authors can end a sequence of if/else if statements• without a final else, but that's the way the code is• given in the book. *******/• }• }

Page 27: Unit 5 Builder

27

What Could Go Wrong with Parsing?

• Parsing could go wrong for several reasons• The first, simplest, and most obvious would be if

the values in the input string weren’t correctly separated by commas

• Things could also go awry in more devious ways• The motivation for having more than one

concrete builder class is that the builders could be designed to be more or less forgiving of faulty input

Page 28: Unit 5 Builder

28

Building under Constraints

• Suppose that every reservation had to have a non-null date and city

• Or suppose, at a more fine-grained level, there have to be at least 25 people in the audience and the total bill has to be at least $495.95.

• These two constraints could be recorded in code as shown on the following overhead

Page 29: Unit 5 Builder

29

• public abstract class ReservationBuilder• {• public static final int MINHEAD = 25;• public static final Dollars MINTOTAL = new Dollars(495.95);

• // …• }

Page 30: Unit 5 Builder

30

• Varying checks for validity can be put into builder classes rather than the base class

• The UML diagram on the following overhead shows two concrete subclasses of the abstract class ReservationBuilder, one a forgiving builder and one an unforgiving builder

Page 31: Unit 5 Builder

31

Page 32: Unit 5 Builder

32

• In the ReservationBuilder classes the build() method either returns a reference to a newly constructed Reservation object

• Or it throws an exception, in this case a BuilderException

• The UnforgivingBuilder and the ForgivingBuilder differ according to the conditions under which they throw a BuilderException

Page 33: Unit 5 Builder

33

• Before considering the implementation of one of the builder classes, the book shows some code illustrating how the parser and a builder would be related

• It is given on the next overhead• It will be followed by commentary

Page 34: Unit 5 Builder

34

• public class ShowUnforgiving• {• public static void main(String[] args)• {• String sample = “Date, November 5, Headcount, 250” + “City,

Springfield, DollarsPerHead, 9.95” + “HasSite, False”;• ReservationBuilder builder = new UnforgivingBuilder();• try• {• new ReservationParser(builder).parse(sample);• Reservation res = builder.build();• System.out.println(“Unforgiving builder: “ + res);• }• catch(Exception e)• {• Systemout.println(e.getMessage());• }• }• }

Page 35: Unit 5 Builder

35

• In the client, the builder is created up front• The parser is created, passing in the builder• parse() is then called on the parser, passing in

the string• Recall that inside the parse() method the set

methods are called on the builder object one-by-one

Page 36: Unit 5 Builder

36

• After the parsing is done, build() is called on the builder

• If the parameters weren’t right, the build() method will throw an exception

• If the parameters were all right, the build() method will construct a reservation object and return a reference to it

Page 37: Unit 5 Builder

37

• These are the critical lines of code:• ReservationBuilder builder = new UnforgivingBuilder();• try• {• new ReservationParser(builder).parse(sample);• Reservation res = builder.build();• System.out.println(“Unforgiving builder: “ +

res);

• Because the sample string is OK, this code will simply print out the message “Unforgiving builder: ” followed by the successfully built reservation

Page 38: Unit 5 Builder

38

• Challenge 15.2• The build() method of the UnforgivingBuilder

class thows a BuilderException if the date or city is null, if the headcount is too low, or if the total cost of the proposed reservation is too low.

• Write the code for the build() method according to these specifications.

Page 39: Unit 5 Builder

39

• Comment mode on:• In essence the build() method will turn out to

be a bunch of if statements potentially followed by construction of the desired reservation object.

Page 40: Unit 5 Builder

40

• Solution 15.2• The build() method of UnforgivingBuilder

throws an exception if any attribute is invalid and otherwise returns a valid Reservation object.

• Here is one implementation:• [See next overhead.]

Page 41: Unit 5 Builder

41

• public Reservation build() throws BuilderException• {• if(date == null)• throw new BuilderException(“Valid date not found”);• if(city == null)• throw new BuilderException(“Valid city not found”);• if(headcount < MINHEAD)• throw new BuilderException(“Minimum headcount is ”

+ MINHEAD);• if(dollarsPerHead.times(headcount).isLessThan(MINTOTAL))• throw new BuilderException(“Minimum total cost is ”

+ MINTOTAL);• return new Reservation(date, headCount, city,

dollarsPerHead, hasSite);• }

Page 42: Unit 5 Builder

42

• Solution 15.2, continued.• The code checks that date and city values are

set and checks that headcount and dollars/head values are acceptable.

• The ReservationBuilder superclass defines the constants MINHEAD and MINTOTAL.

• If the builder encounters no problems, it returns a valid Reservation object.

Page 43: Unit 5 Builder

43

• Comment mode on:• In order to understand the build() method,

you have to remember how the parser and the builder are related.

• The parser is passed the builder• As the parser runs, it calls the set methods for

the parameters of the builder

Page 44: Unit 5 Builder

44

• After the parser is finished, these instance variables are either set or unset

• In the build() method code, the if statements depend on the state that the parser left the instance variables of the builder object in

• End of Solution 15.2

Page 45: Unit 5 Builder

45

A Forgiving Builder

• The book completes its example by giving an implementation of the ForgivingBuilder class.

• In order to save some time, this will not presented.

• Instead, the rest of the overheads will cover a separate example which doesn’t rely on parsing.

Page 46: Unit 5 Builder

46

Another Example

• Another example, based on cups, will be given next

• Keep in mind what the pattern does• It offloads construction of an object of one

class to another class• That other class manages delayed

construction of the object

Page 47: Unit 5 Builder

47

• The code for these classes is given as the basis of the example:

• Cup.java and BuildFromTextFields.java.• The example will be completed with

implementations of these classes:• CupBuilder.java and ForgivingCupBuilder.java.

Page 48: Unit 5 Builder

48

• An instance of the ForgivingCupBuilder class has two instance variables of type String inherited from the CupBuilder class.

• The build() method uses these inherited instance variables to construct an instance of the Cup class.

Page 49: Unit 5 Builder

49

• The name of a Cup’s owner is a String, so the inherited instance variable that the builder works with is of the right type.

• The seedCount of a Cup is an int, so it's necessary to parse the String instance variable of the ForgivingCupBuilder class before using it as a construction parameter for a Cup.

Page 50: Unit 5 Builder

50

• The ForgivingCupBuilder class implements the following logic:

• A. An instance of the Cup class can't be constructed without an owner.

• The build() method should throw an exception if the value for the owner is the empty String ("").

• This part of the building process is "unforgiving".

Page 51: Unit 5 Builder

51

• B. The build() method should forgive cases where the String value for the seedCount is the empty String ("").

• In this case, an instance of Cup should be constructed with a seedCount of 0.

Page 52: Unit 5 Builder

52

• The ForgivingCupBuilder class is not intended to solve all of the problems of faulty input.

• For example, the code does not deal with the case where a user enters a value for the seedCount field which would not parse as an int.

• It simply deals with the case where the user doesn't enter anything at all for the seedCount field.

Page 53: Unit 5 Builder

53

• This logic of building closely parallels the book’s example, but with different classes

• This example does have a major difference from the book example in that it is graphical in nature

• Rather than parsing some random input string, it does construction based on the values interactively entered into JTextFields in the application

Page 54: Unit 5 Builder

54

• A screenshot of the application is shown on the next overhead

Page 55: Unit 5 Builder

55

Page 56: Unit 5 Builder

56

• A UML diagram for the application is given on the following overhead

Page 57: Unit 5 Builder

57

Page 58: Unit 5 Builder

58

• The code for the example is given on the following overheads.

Page 59: Unit 5 Builder

59

• /* This is the class you’re actually constructing instances of. */

• public class Cup• {• private String ownersName;• private int seedCount;

• public Cup()• {• }

• public Cup(String ownersNameIn, int seedCountIn)• {• ownersName = ownersNameIn;• seedCount = seedCountIn;• }

• public void setOwnersName(String ownersNameIn)• {• ownersName = ownersName;• }

Page 60: Unit 5 Builder

60

• public String getOwnersName()• {• return ownersName;• }

• public void setSeedCount(int seedCountIn)• {• seedCount = seedCountIn;• }

• public int getSeedCount()• {• return seedCount;• }

• public String toString()• {• return ("Cup[ownersName=" + ownersName• + ", seedCount=" + seedCount + "]");• }• }

Page 61: Unit 5 Builder

61

• /* This is the class that contains all of the graphical machinery—as well as the building. */

• import java.awt.*;• import java.awt.event.*;• import javax.swing.*;• import java.lang.*;• import java.util.*;

• public class BuildFromTextFields• {• public static void main(String[] args)• {• BuildFrame myframe = new BuildFrame();• myframe.setVisible(true);• }• }

Page 62: Unit 5 Builder

62

• class BuildFrame extends JFrame• {• private BuildPanel myPanel;• private final int FRAMEW = 700;• private final int FRAMEH = 700;

• public BuildFrame()• {• setTitle("Build Frame");• setSize(FRAMEW, FRAMEH);• setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

• myPanel = new BuildPanel();• Container contentPane = getContentPane();• contentPane.add(myPanel, "Center");• }• }

Page 63: Unit 5 Builder

63

• class BuildPanel extends JPanel• {• private JTextField ownersNameField;• private JTextField seedCountField;• private JButton buildCupButton;• private String ownersNameString;• private String seedCountString;• ArrayList<Cup> listOfCups;• JTextArea myTextArea;

Page 64: Unit 5 Builder

64

• public BuildPanel()• {• listOfCups = new ArrayList<Cup>();• JLabel label1 = new JLabel("Owner's Name:");• ownersNameField = new JTextField("", 12);• JLabel label2 = new JLabel("Seed Count:");• seedCountField = new JTextField("", 4);• buildCupButton = new JButton("Create Cup");• BuildListener myButtonListener = new

BuildListener();•

buildCupButton.addActionListener(myButtonListener);• myTextArea = new JTextArea(12, 24);

• JScrollPane myScrollPane = new JScrollPane(myTextArea,

• JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

Page 65: Unit 5 Builder

65

• JPanel subPanel1 = new JPanel();• JPanel subPanel2 = new JPanel();• JPanel subPanel3 = new JPanel();• JPanel subPanel4 = new JPanel();• JPanel subPanel5 = new JPanel();• JPanel subPanel6 = new JPanel();• subPanel1.add(label1);• subPanel2.add(ownersNameField);• subPanel3.add(label2);• subPanel4.add(seedCountField);• subPanel5.add(buildCupButton);• subPanel6.add(myScrollPane);• setLayout(new GridLayout(3, 2));• add(subPanel1);• add(subPanel2);• add(subPanel3);• add(subPanel4);• add(subPanel5);• add(subPanel6);• }

Page 66: Unit 5 Builder

66

• public void paintComponent(Graphics g)

• {• Graphics2D g2 = (Graphics2D) g;• super.paintComponent(g2);

• }

Page 67: Unit 5 Builder

67

• /* This is the listener attached to the button in the application. Clicking the button is supposed to construct a cup based on the values in the JTextFields. This is where the ForgivingCupBuilder is put into action. */

• private class BuildListener implements ActionListener

• {• public void actionPerformed(ActionEvent event)• {• ForgivingCupBuilder myBuilder;• Cup myCup = null;

• ownersNameString = ownersNameField.getText();• ownersNameField.setText("");• seedCountString = seedCountField.getText();• seedCountField.setText("");

Page 68: Unit 5 Builder

68

• myBuilder = new ForgivingCupBuilder(ownersNameString, seedCountString);

• try• {• myCup = myBuilder.build();• }• catch(Exception buildException)• {• myTextArea.append("\nCaught build exception.\

n");• }

Page 69: Unit 5 Builder

69

• if(myCup != null)• {• listOfCups.add(myCup);• }

• myTextArea.append("\nLatest contents of listOfSeeds.\n");

• for(Cup aCup: listOfCups)• {• myTextArea.append(aCup.toString() + "\n");• }• }• }• }

Page 70: Unit 5 Builder

70

• /* This is the abstract CupBuilder superclass. It contains the declaration of the abstract build() method. */

• public abstract class CupBuilder• {• private String ownersName;• private String seedCountString;

• public CupBuilder()• {• }

• public CupBuilder(String ownersNameIn, String seedCountStringIn)• {• ownersName = ownersNameIn;• seedCountString = seedCountStringIn;• }

• public void setOwnersName(String ownersNameIn)• {• ownersName = ownersName;• }

Page 71: Unit 5 Builder

71

• public String getOwnersName()• {• return ownersName;• }

• public void setSeedCount(String seedCountStringIn)• {• seedCountString = seedCountStringIn;• }

• public String getSeedCountString()• {• return seedCountString;• }

• public String toString()• {• return ("CupBuilder[ownersName=" + ownersName• + ", seedCount=" + seedCountString + "]");• }

• public abstract Cup build() throws Exception;• }

Page 72: Unit 5 Builder

72

• /* This is the ForgivingCupBuilder class, which implements a forgiving version of the build() method. */

• import java.lang.Exception;

• public class ForgivingCupBuilder extends CupBuilder• {• public ForgivingCupBuilder(String ownersNameIn,

String seedCountStringIn)• {• super(ownersNameIn, seedCountStringIn);• }

Page 73: Unit 5 Builder

73

• public Cup build() throws Exception• {• int seedCount;• String ownersNameHere = this.getOwnersName();• String seedCountStringHere = this.getSeedCountString();

• if(ownersNameHere.equals(""))• {• throw new Exception();• }• else• {• if(!seedCountStringHere.equals(""))• {• seedCount = Integer.parseInt(seedCountStringHere);• }• else• {• seedCount = 0;• }• }

• return new Cup(ownersNameHere, seedCount);• }• }

Page 74: Unit 5 Builder

74

UML for the Pattern

• There is no single, official UML diagram that represents the builder pattern

• At a minimum the pattern involves some client, the builder class, and the base class of the actual object that is to be created

• Things like a parser, a graphical user interface, and a hierarchy of abstract and concrete builders are not parts of the fundamental concept.

Page 75: Unit 5 Builder

75

• A simple static structure diagram and a sequence diagram are given on the following overheads to illustrate the basic design pattern

Page 76: Unit 5 Builder

76

Page 77: Unit 5 Builder

77

Page 78: Unit 5 Builder

78

• If you went back to the overall UML diagram for the example you would find that the BuildListener inner class plays the role of the client

• The ForgivingCupBuilder class plays the role of the builder

• And the Cup class is the base class in the design• The rest of the classes in the example just belong

to the application its graphical user interface

Page 79: Unit 5 Builder

79

• Lasater’s UML diagram is given on the next overhead.

• Using that author’s terminology, the pattern is recognizable by the use of the construct() method in the director class and the build methods in the builder classes.

Page 80: Unit 5 Builder

80

Page 81: Unit 5 Builder

81

(Book) Summary

• The Builder pattern separates the construction of an object from its other characteristics

• The builder class contains the construction logic, leaving the base class code simpler

• This can be useful when you want to make sure the construction parameters are valid before trying to construct an instance of a class

• The validity checking is offloaded into the builder class

Page 82: Unit 5 Builder

82

• The Builder pattern supports so-called step-by-step or gradual construction

• The builder object is constructed• Its instance variables are set step-by-step• Then if it is complete and correct, it can create an

instance of the corresponding base class• As illustrated in the book’s example, this can be

useful when using a parser to determine construction parameters

Page 83: Unit 5 Builder

83

• Not only does a builder accomplish the offloading of construction.

• Although it can be thought of as step-by-step, gradual construction, in reality, it’s delayed construction.

• The construction parameters can be tested, and only if they are OK does the build() method ultimately construct and return a reference to an object

Page 84: Unit 5 Builder

84

The End

Page 85: Unit 5 Builder

85

• The following blocks of stuff are stored here for reference.

• They come up in the book, but they will not be presented in class.

Page 86: Unit 5 Builder

86

• Challenge 15.1• The regular expression object used by the

split(s) call divides a comma-separated list into individual strings.

• Suggest an improvement to this regular expression—or to the entire approach—that will make the parser better at recognizing a reservation’s information.

Page 87: Unit 5 Builder

87

• Solution 15.1• One way to make the parser more flexible is to let it

accept multiple blanks after a comma.• To do so, change the construction of the split() call as

follows:• s.split(“, *”);• Or, instead of accepting blanks after every comma, you

can allow any kind of whitespace by initializating the Regex object as follows:

• s.split(“,\\s”);

Page 88: Unit 5 Builder

88

• The \s characters indicate the whitespace “character class” in regular expressions.

• Note that all these solutions assume that there will be no commas embedded in the fields.

• Rather than making the regular expression more flexible, you might question the entire approach.

Page 89: Unit 5 Builder

89

• In particular, you might want to press the travel agencies [which submit the reservation requests] to begin sending reservations in XML format.

• You might establish a set of tags to use and read them in with an XML parser.

• [End of Challenge 15.1 Solution.]

Page 90: Unit 5 Builder

90

• The book mentions again the idea that a builder allows for gradual construction

• This is not exactly true• Recall the statement earlier about why it

might be undesirable to do piecewise construction of a reservation object:

• If the set of parameter values wasn’t valid, construction might not be possible

Page 91: Unit 5 Builder

91

• What’s happening with this pattern is that that problem is offloaded

• You do construct a builder object up front• The parser looks for parameters one-by-one• It then feeds these parameters to the builder

object• If the builder object is fed an invalid

parameter, life goes on

Page 92: Unit 5 Builder

92

• However, the overall result is this:• In this design pattern, the builder is constructed

up front and slowly given values• No matter what, construction of the reservation

isn’t gradual• Instead, it’s delayed until the builder is finished• If success isn’t achieved in setting up the builder,

then the reservation object will not be constructed

Page 93: Unit 5 Builder

93

• To repeat, in reality there is no such thing as gradual construction

• It’s a bit of a misnomer, although once you understand what the book means, maybe it’s a useful phrase

• At any rate, the book is now ready to consider the code for builders

• The UML diagram on the following overhead shows two concrete subclasses of the abstract class ReservationBuilder

Page 94: Unit 5 Builder

94

• The existence of the concrete subclasses clinches the justification for the pattern

• If the ReservationBuilder class were concrete it might have different constructors with different parameter lists

• However, as you know, inside the code for a constructor you don’t have control over anything other than the initialization of instance variables

Page 95: Unit 5 Builder

95

• As became apparent when examining the Singleton design pattern, your constructor either works and returns a reference to an object of that class

• Or it doesn’t work• There is no such thing as making construction

conditional based on the code inside the constructor

Page 96: Unit 5 Builder

96

A Forgiving Builder

• The idea now is to include more logic in the concrete builder class to accomplish the following:

• If a reservation request has no headcount, set that to the minimum

• If the dollars/head value is missing, set that high enough so that the total is at least the minimum

• The book notes that doing this correctly requires some thought

• For example, what would be a good solution if the parser finds a dollars/head value but no headcount?

Page 97: Unit 5 Builder

97

• Challenge 15.3• Write a specification for

ForgivingBuilder.build(), focusing on how the builder can handle missing values for headcount or dollars/head.

Page 98: Unit 5 Builder

98

• Solution 15.3• As before, the code must throw an exception

if the reservation fails to specify a city or a date, as there is no way to guess these values.

• Regarding missing values for headcount or dollars/head, note the following:

Page 99: Unit 5 Builder

99

• 1. If the reservation request specifies no headcount and no dollars/head, set the headcount to the minimum, and set dollars/head to the minimum total, divided by the headcount.

• 2. If there is no headcount but there is a dollars/head value, set the headcount to be at least the minimum attendance and at least enough to generate enough money for the event.

Page 100: Unit 5 Builder

100

• 3. If there is a headcount but no dollars/head value, set the dollars/head value to be high enough to generate the minimum take.

Page 101: Unit 5 Builder

101

• Challenge 15.4• After reviewing your approach, write the code

for the ForgivingBuilder class’s build() method.

Page 102: Unit 5 Builder

102

• Solution 15.4• One solution is as follows:• [See the following overheads.]

Page 103: Unit 5 Builder

103

• public Reservation build() throws BuilderException• {• boolean noHeadCount = (headcount == 0);• boolean noDollarsPerHead = (dollarsPerHead.isZero));• if(noHeadCount && noDollarsPerHead)• {• headcount = MINHEAD;• dollarsPerHead = sufficientDollars(headcount);• }• else if(noHeadCount)• {• headcount = (int) Math.ceil(MINTOTAL.dividedBy(dollarsPerHead));• headcount = Math.max(headcount, MINHEAD);• }• else if(noDollarsPerHead)• {• dollarsPerHead = sufficientDollars(headcount);• }• check();• return new Reservation(date, headcount, city, dollarsPerHead,

hasSite);• }

Page 104: Unit 5 Builder

104

• This code relies on a check() method that is similar to the build() method of the UnforgivingBuilder class.

• Comment mode on:• It also relies on a bunch of other things…• See the code on the next overhead

Page 105: Unit 5 Builder

105

• protected void check() throws BuilderException• {• if(date == null)• throw new BuilderException(“Valid date not found”);• if(city == null)• throw new BuilderException(“Valid city not found”);• if(headcount < MINHEAD)• throw new BuilderException(“Minimum headcount is ” +

MINHEAD);• if(dollarsPerHead.times(headcount).isLessThan(MINTOTAL))• throw new BuilderException(“Minimum total cost is ” +

MINTOTAL);• }