unit 27 interpreter summary prepared by kirk scott 1

147
Unit 27 Interpreter Summary prepared by Kirk Scott 1

Upload: arline-stone

Post on 05-Jan-2016

215 views

Category:

Documents


0 download

TRANSCRIPT

Slide 1

Unit 27InterpreterSummary prepared by Kirk Scott1Design Patterns in JavaChapter 25InterpreterSummary prepared by Kirk Scott2The Introduction Before the IntroductionThere are several problems with this chapterAmong them are the following:1. The book doesnt explain the following two points very well:A. What is an interpreter?B. Why would you want to use the Interpreter design pattern?The book touches on these questions in the last subsection of the chapterEven then, the book seems to concede that not all of the machinery is present in order to make this be very practical or seem very practical32. Another shortcoming is that in implementing the pattern, some of the books code is not easy to understandThis breaks down into two sub-problemsA. The underlying problem seems to me to be that the code violates encapsulationNot only is this potentially a poor choice, but this is part of what makes it hard to understand 4B. The additional observation on this is that the books explanations are not incredibly transparentThe code is hard to understand, and even after reading the explanations, youre left wondering whether the books approach was a good idea or not51.A What is an Interpreter?Returning to point 1 in more detail:In general, in what problem domain do you expect to hear about interpreters?The answer is, this belongs in the category of language processingIn other words, you think of interpreters and compilers at the same timeThese are pieces of code which allow you to translate from a source language to a target language, from high level code to machine code6If you have had a course in programming languages or compilers you might also have heard the terms lexical analyzer and parserThese terms refer to different steps in reading a source file containing statements in some language, and translating them, ultimately, into a sequence of instructions that can be executed on a computer chip7For the sake of background, here is a definition of a lexical analyzer (from Aho, Sethi, and UllmanThe lexical analyzer is the first phase of a compiler. Its main task is to read the input characters and produce as output a sequence of tokens that the parser uses for syntax analysis.In other words, given a stream of characters, break it into a sequence of constants, variables, operators, keywords, etc.8Here is a definition of a parser (from Aho, Sethi, and Ullman)In our compiler model, the parser obtains a string of tokens from the lexical analyzer, , and verifies that the string can be generated by the grammar for a source language. We expect the parser to report any syntax errors in an intelligible fashion.In other words, the parser checks to see whether the input, after having been broken into tokens, forms a set of statements in the supposed source language.9The last stage in a compiler is translation from the verified statements of the source file to an equivalent sequence of instructions in the target languageSo whats an interpreter, compared to a compiler?In very simple terms, an interpreter is a system that translates each statement as its parsed, and executes it without waiting to translate the complete program10Interpretation can be a preferred way of dealing with programs for several reasonsIt is common with command languages, for exampleUsers may enter ad hoc sequences of instructions at a command promptThese instructions may be calls to external applications like system utilities11If on the command side, its difficult to foresee and impossible to control exactly what results the system utility produces, then a compiler may not be able to form a complete programIt is better to have an interpreted system that is flexible and responsive to the results that are produced command by commandIn other words, it makes sense to translate one step at a time12You may recall that Java is referred to as an interpreted languageOn the other hand, you are also familiar with the idea that you have to compile Java source codeWhats going on?A Java source file is written in Java, a high level, human understandable language13Compilation results in a class file, containing what is known as bytecodeBytecode is not machine language for any actual physical machineIt is the language of the Java Virtual MachineWhat is the Java Virtual Machine?In one respect, it is a simulated machine which runs bytecode14In another respect, it is an interpreterIt interprets bytecode, line by line, into code that can run on the physical host machineWhat advantage is there to this overall design?Java is universalIt can be run on any hardware for which a Java Virtual Machine exists15The code is only compiled once, into bytecodeIt is not compiled a second time, into machine languageWhen a Java class file is to be run, there is no delay while it is translated in its entirety into machine language codeIt starts running immediatelyrunning on the machine simulationrunning on the interpreter which the Java Virtual Machine is16B. Why Would You Want to Use the Interpreter Design Pattern?The book tries to illustrate the Interpreter design pattern in a practical, applied wayInstead of worrying about the problems of language interpretation, the authors pose the problem of creating programs that interact with and control machines in a factoryThey develop a mechanism that makes it possible to build programs that would have this effect17Then at the end of the chapter they observe that you could write programs directly which would control the machinesIts not clear that youve gained any benefit from the machinery that makes it possible to compose sequences of commands which could be used as machine control programsIn response to this, they observe the following:18It would be possible to have another language, a language for controlling machines, in which a programmer/engineer/person responsible for a machine could write a program to control itSuch a language would not have to have the features of JavaIt could be restricted to concepts of machine control only19Writing machine control programs in such a language would be simplerThe problem would then become, how to translate them into Java code for use in factory environment where implementations were done in that language20The book finally concludes that you would need a parser that could translate from the simple, machine control language, into the machinery for building Java programs which they present as the Interpreter design patternThen the interpreter design pattern as presented here would become useful21On the one hand, we can be thankful that the book doesnt try to complete the job by developing a parserOn the other hand, it turns out that the motivation for the Interpreter design pattern is incompleteWe are shown how its done, but its hard to imagine that its very useful when the book concedes that you could just write straight Java code to accomplish all that the examples accomplish22In conclusion in this introductory subsection, I can only say this:Its an eternal problem that when you try to explain something complicated, you end up simplifyingThe simplification often makes the example seem pointlessThis doesnt mean that the design pattern is uselessRegardless of the examples, the goal is to understand the pattern232.A Encapsulation is ViolatedIt appears that the authors violate encapsulation in part of their implementation of the Interpreter design patternThis will be pointed out specifically in the code when it is presentedThe statement is always made that violating encapsulation is badAt the very least, you will see that it makes the code hard to understand24The books explanations dont really make it clearThis may lead you to the following, valid conclusion:Maybe part of the reason that violating encapsulation is bad is that it leads to code that is hard to understandMaybe part of the reason for developing the concept of encapsulation was to try to avoid the difficulties in understanding that become evident when encapsulation is violated25Introductory Book MaterialThe Interpreter design pattern can be used to make objects where the objects, in essence, are like individual commandsDifferent kinds of commands are implemented as different classesEach of these classes has a method of the same name, something like execute(), and the difference in the command objects consists of the fact that the code for the execute() method differs among the different command classes26In general, the Interpreter design pattern can be thought of as supporting multiple commandsStructurally, the Interpreter pattern is similar to the State and Strategy patterns because each class has the same methodRecall that in the State example, every door state had a touch methodIn the strategy example, every kind of advisor had a recommend() methodWith the interpreter, each kind of command will have an execute() method27The Interpreter design pattern is also closely related to the Composite design patternThe Interpreter pattern will be used to create sequences of commandsMacros or subroutine calls in a program are subsequences of commandsA complex sequence of commands may consist of single commands as well as composite commandsA reasonably complete implementation of the Interpreter pattern will track the way things can be put together as individual leaf items, and composite nodes, where each of these is a type of component28Book definition:The intent of the Interpreter pattern is to let you compose executable objects according to a set of composition rules that you define.29An Interpreter ExampleThe book illustrates the Interpreter design pattern using the robots that transport material between machines and that can load and unload the machinesThe UML diagram on the following overhead shows a Robot class and a small hierarchy of commandsThe command hierarchy is the same in structure as a composite hierarchy3031

In looking at the UML diagram, the following things should be noticed:There is an abstract method, execute(), at the top of the command hierarchyIt is concretely implemented in the two subclassesThe CommandSequence class also has an add() method for building up compositesOtherwise, there are no other methods in the Command hierarchy32Also notice that the CarryCommand class has a constructor, which takes two machines as input parametersThe way a command is set up to work on robots/machines is by constructing it with the necessary referencesThere are corresponding instance variables for these references in the CarryCommand classYou can foresee that the implementation of the execute() method will work by calling the carry() method of the Robot class on these references33The next thing to ask is, So, what part of this design is the interpreter?The answer is, the command hierarchy is in essence the interpreterA program, named ShowInterpreter, will be given shortly, which illustrates the use of the hierarchy34A bunch of things are assumed in the code, but theyre not difficultYou obtain a machine composite object for the factory in Dublin, IrelandYou then obtain references to specific machinesYou then write code to load bins into the machines35You then construct two instances of CarryCommand using these machines as input parametersYou then construct an instance of CommandSequence and add the individual commandsFinally, you call execute on the sequence objectIt is this one, final line of code which controls the robot/machines in the factory36In essence, the whole machine control program has been packaged up in the one object and the one call to the execute() methodThis is what the mean by composing sequences of instructionsThe code is given on the next overhead37public class ShowInterpreter { public static void main(String[] args) { MachineComposite dublin = OozinozFactory.dublin(); ShellAssembler assembler = (ShellAssembler) dublin.find("ShellAssembler:3302"); StarPress press = (StarPress) dublin.find("StarPress:3404"); Fuser fuser = (Fuser) dublin.find("Fuser:3102");

assembler.load(new Bin(11011)); press.load(new Bin(11015));

CarryCommand carry1 = new CarryCommand(assembler, fuser); CarryCommand carry2 = new CarryCommand(press, fuser);

CommandSequence seq = new CommandSequence(); seq.add(carry1); seq.add(carry2);

seq.execute(); }}38Rather than showing the code for the command classes in advance, the book shows it after the example where it is usedThe CommandSequence class has an ArrayList of commandsThe add() method allows commands to be added39The execute() method looks like the methods, like isTree(), in the composite design patternIt iterates over all of the elements of the ArrayList calling execute() on themIf the element is a leaf, then the version of execute() in the CarryCommand class is usedIf the element is a composite, then the call is recursiveThe code for the CommandSequence class is shown on the next overhead

40public class CommandSequence extends Command { protected List commands = new ArrayList();

public void add(Command c) { commands.add(c); } public void execute() { for (int i = 0; i < commands.size(); i++) { Command c = (Command) commands.get(i); c.execute(); } }}41The CarryCommand class has references to two machinesIt has a constructor that takes two machine references as input parametersAnd it is has an execute() methodThe execute() is implemented by making a call to carry() on the single instance of Robot maintained in the Robot class, passing the machine references as parameters to this methodThe code is shown on the following overhead42public class CarryCommand extends Command { protected Machine fromMachine; protected Machine toMachine;

public CarryCommand(Machine fromMachine, Machine toMachine) { this.fromMachine = fromMachine; this.toMachine = toMachine; }

public void execute() { Robot.singleton.carry(fromMachine, toMachine); }}43The next step in developing the Interpreter design pattern is to add more subclasses to the Command hierarchyThe idea is that each different command type, in its execute() method, will package up a different kind of action that could be taken in controlling machines in a factoryThis expands and generalizes the kind of total program for machine control that the interpreter pattern can generate and package altogether44In addition to a CarryCommand, there might be a StartUpCommand and a ShutDownCommandIn addition to simple commands, there might be commands that reflect the structure of a programming languageThis is where the complexity starts to set in45The tree-like structure of the composite makes it possible to simply model macros, namely subsequences of codeHowever, a complete programming language allows iterationThe book introduces the idea of a ForCommand into the Command hierarchyThe ForCommand class implements the logic of a for each loopThe UML diagram for the expanded interpreter is shown on the next overheadExplanations follow4647

Most of the UML diagram is just a generalization of the previous onewith more Command classes addedThe new aspect of the diagram is the arrow from the ForCommand class to the Command classAn instance of ForCommand will have a reference to another commandThis other command constitutes the body of the for each loop48In general, you might expect a design pattern that starts to mimic the elements of a programming language to need a construct that is like a variableThe book points out that in particular, something that works like a for each loop depends on a variable

49The syntax for a simple for each loop was given in Unit 9 of CS 202 with this example:for(Cup7 someCup: mycollection){myterminal.println(someCup);}mycollection was an ArrayList, for example, and someCup was a variable local to the loop which referred to the successive elements of mycollection

50This is an analogous loop for commands as given in the book:for(Command c: commands){ c.execute();}51The books idea is that if you want to set up a for each command, the constructor for the ForCommand class will need to take in 3 parameters:1. A collection of machines, the actual objects to iterate over2. A temporary variable, to hold a reference to each object as its iterated over in the loop3. A command, which would be the body of the loop that would be iterated

52Take the CarryCommand for example againIt was an operation that worked on two objectsThose objects were machinesWhen an instance of CarryCommand was constructed, those two machines were input parameters

53Analogously, the ForCommand constructor will take in a collection of machines as an input parameterThe ForCommand object that is constructed will iterate over the machines applying whatever command was also entered as an input parameter when the ForCommand object was constructedIn order to make the ForCommand logic work, its necessary to be able to define a variable that will hold the reference to machine objects

54For better or worse, adding variables to the interpreter is not trivialIt will take several overheads to discuss how the book accomplishes that before returning to the question of how the ForCommand is implementedThe classes needed to include variables are shown in the UML diagram on the following overhead5556

In the UML diagram there is a hierarchy with the class Term at the topThinking back to the very beginning of the presentation, you can think of a term as being a token, more or lessIn other words, a term is a unit, a word that a programming language deals with57In a full scale interpreter there could be many different kinds of termsAs shown here, there are a minimumTerms are either constants or variablesEach of the concrete classes, Variable and Constant, has a constructor58The system works with machinesA constant is constructed with a machine as the input parameterA variable is constructed with a String as an input parameterThis String is the name of the variableA variable also has a Term instance variableThis holds whatever value is assigned to the variable59The Constant class has an equals() method, which tests to see whether two constants are in fact the same machineIt also has an eval() method which is essentially like a get() methodIt returns whatever machine the constant happens to contain60The Variable class has an assign() methodThis is of critical importanceVariables dont make sense without the assignment operationThe assign() method implements the assignment operationThis method takes in a term, either a constant or a variable, and assigns that value to the variable61The Variable class has an equals() method, which tests to see whether two variables refer to the same machineIt also has an eval() methodThe eval() method in this class is not the same as a get() methodIt returns a reference to the machine object that the variable refers toThe code for the Constant and Variable classes is given on the following overheads

62public class Constant extends Term { protected Machine machine; public Constant(Machine machine) { this.machine = machine; } public boolean equals(Object obj) { if (obj == this) return true;

if (!(obj.getClass() == this.getClass())) return false;

Constant that = (Constant) obj; return this.machine.equals(that.machine); }63 public int hashCode() { return machine.hashCode(); } public Machine eval() { return machine; } public String toString() { return machine.toString(); }}64public class Variable extends Term { protected String name; protected Term value; public Variable(String name) { this.name = name; } public String getName() { return name; } public void assign(Term term) { this.value = term; } public boolean equals(Object o) { if (o == this) return true;

if (!(o instanceof Variable)) return false;

Variable v = (Variable) o;

return name.equals(v.name); }65 public int hashCode() { return name.hashCode(); } public Machine eval() { return value.eval(); } public String toString() { return name + ": " + value; }}

66The book is not yet ready to try and show how to use variables in the implementation of the ForCommand classFirst is shows how some of the simpler commands could be generalized by using references to terms In other words, the commands could then be used with either constants or variables which refer to machines rather than machine references themselves67The main thing that happens is that whenever a term is used, it is resolved to a machine reference by calling eval() on itThe CarryCommand class rewritten using terms is shown on the next overhead

68public class CarryCommand extends Command { protected Term from; protected Term to;

public CarryCommand(Term fromTerm, Term toTerm) { from = fromTerm; to = toTerm; }

public void execute() { Robot.singleton.carry(from.eval(), to.eval()); }}69On the next overhead, code for a StartUpCommand class is given which uses termsThe same approach is used as for the CarryCommandWhenever a term is used, it is resolved to a machine reference by calling eval()

70public class StartUpCommand extends Command { protected Term term;

public StartUpCommand(Term term) { this.term = term; }

public void execute() { Machine m = term.eval(); m.startup(); }}71Its not very clear what youve gained by this since everything has to resolve to a machineThe explanation, again, stems all the way back to the initial discussion72Ultimately, you might have a machine control language, which you parse and then interpretThe machine control language might use variablesIncluding terms, constants, and variables on the interpreter end makes it possible to support variables on the control language end

73Next, the book tackles the topic of how to write the ForCommand classAs mentioned already, this is where things get messyThe book presents partial codeThe most twisted part of the implementation is reserved for a challenge74Rather than doing this as a challenge, the complete code is given on the following overheads, broken up with explanationsThe first overhead shows the instance variables and the constructor of the ForCommand class

75public class ForCommand extends Command { protected MachineComponent root; protected Variable variable; protected Command body;

public ForCommand(MachineComponent mc, Variable v, Command body) { this.root = mc; this.variable = v; this.body = body; }76The constructor, as advertised, takes in a MachineComponent, a Variable, and a CommandThe only particularly notable thing is the naming of the instance variable that has the MachineComponent parameter assigned to itIts called rootBecause MachineComponent can be a composite, it can be a tree of instructions, and the for loop that ForCommand implements will iterate over that tree, using the input parameter as the root node77The next overhead shows the implementations of the execute() methods for the ForCommand class78 public void execute() { execute(root); }

private void execute(MachineComponent mc) { if (mc instanceof Machine) { Machine m = (Machine) mc; variable.assign(new Constant(m)); body.execute(); return; }

MachineComposite comp = (MachineComposite) mc; List children = comp.getComponents(); for (int i = 0; i < children.size(); i++) { MachineComponent child = (MachineComponent) children.get(i); execute(child); } }}79The first method on the previous overhead is an execute() command for ForCommand without any input parametersKeep in mind that the model thats being followed is that all necessary values are input at construction timeThen to execute an instance of ForCommand or any other of the commands, its simply necessary to call execute() with no parameters80In ForCommand, the execute() command without parameters wraps a call to execute() which takes a single MachineComponent parameter, the root of the tree of machines that is to be iterated overThis parameter is available because it is passed in at the time the ForCommand object is constructed81The second method is the private method, execute(), which takes the reference to the MachineComponent object as its input parameterThis is the method which the execute() without parameters callsThis is the method that does the work82Once again, the structure of the methods is reminiscent of things that were seen with the composite patternThere is a method without parameters that is ultimately implemented by calling a method with a parameterThe execute() methods are reminiscent of the isTree() methodsexecute() with a parameter is recursive, making it possible to traverse a tree of machines83This execute() method breaks into two sub-parts, the non-recursive, base case, and the recursive caseConsider the recursive part firstIt is easier to understandThat part of the code is shown again on the next overhead so I dont have to skip back84public class ForCommand extends Command { protected MachineComponent root; protected Variable variable; protected Command body;

public ForCommand(MachineComponent mc, Variable v, Command body) { this.root = mc; this.variable = v; this.body = body; } private void execute(MachineComponent mc) { if (mc instanceof Machine) { } // Else MachineComposite comp = (MachineComposite) mc; List children = comp.getComponents(); for (int i = 0; i < children.size(); i++) { MachineComponent child = (MachineComponent) children.get(i); execute(child); } }85If the input parameter is not an individual machine, then its a compositeCast it to the composite type and acquire a reference to the List (ArrayList) of components that it containsIterate over this list of components86What you do with each element is recursively call this same execute() method, passing in each element, in turn, as the explicit parameterDo not be confusedIn this recursive part of the code you are not calling execute on individual machines/commandsYou are traversing those elements of the tree that are composite, recursively calling this execute() method on them

87The first part of the method is where the recursion stopsThis is the part where you encounter tree elements which are leaves, or individual machinesThat part of the code is shown again on the next overhead so I dont have to skip backIt will then be discussed, line by line88public class ForCommand extends Command { protected MachineComponent root; protected Variable variable; protected Command body;

public ForCommand(MachineComponent mc, Variable v, Command body) { this.root = mc; this.variable = v; this.body = body; } private void execute(MachineComponent mc) { if (mc instanceof Machine) { Machine m = (Machine) mc; variable.assign(new Constant(m)); body.execute(); return; } }89This is precisely the point where things get uglyIt is the point where things depend on breaking encapsulationIts hard to understand what is happening because you are working with referencesIt is doubly hard because these are references that came in at construction time, not parameters that came in as direct input to this method90Here are the lines of code, explained step-by-step1.if (mc instanceof Machine) You are iterating through a tree of machine components and you have hit a leaf node, namely a single machine, not a composite

912.Machine m = (Machine) mc;You cast the reference from MachineComponent to MachineThis isnt too troublesome

923.variable.assign(new Constant(m));Inside the call you construct an instance of Constant using the machine objectThis is necessary because you want to use the assign() method, which takes an instance of Constant as its explicit parameterYou assign this constant to the reference named variable93At this point you need to recall that this Variable reference was passed in when the ForCommand object was being constructedIt may not be clear what the purpose of this is, but there is nothing apparently wrong (yet)There is an instance variable in the ForCommand class that holds the reference to the Variable objectThe key point is this:Any assignment to the Variable reference here affects the variable as it was in the client code that constructed the ForCommand object, because the Variable instance variable is a reference that was passed in, and not a simple value944.body.execute();This is the line of code that is the mystery requiring explanationYou are supposed to be iterating over a collection of machinesForCommand has a bodyThis is the command that is supposed to be applied to each machine in the collection95The question is, in this line of code, where is it apparent that body varies according to whatever machine you were on?The answer only becomes apparent if you look at an example of a call to construct an instance of ForCommandHere is such an example:Command c = new ForCommand(dublin, v, new ShutDownCommand(v));96The first parameter is the collection of machinesThe second parameter is a variable, a placeholderThe third parameter is an instance of ShutDownCommand, the command that should be applied to each machine as you iterate through the collection97The third parameter, an instance of ShutDownCommand, is constructed with a reference to the Variable v as its input parameterIn other words, the ShutDownCommand that is passed in depends on whatever the value of v is at any given time

98In the execute() method of ForCommand, every time this line of code is executedvariable.assign(new Constant(m));The variable belonging to the ShutDownCommand is affected

99This is where encapsulation is being violatedIts not a violation to pass Variable v in, assign it to the instance variable named variable, and have variable changed in the execute methodThe violation is that changing variable in the execute() method changes the value inside the ShutDownCommand objectThis happens without calling a set method on the ShutDownCommand object100This is what makes the code so devilishly hard to followYou make a change to variable in the execute() method and there is nothing in there that tells you that the ShutDownCommand is going to be affectedYou only know that by looking at the calling client code where you pass v and a ShutDownCommand that was constructed using v101Look once more at the code on the following overheadTo the good: Yes, the book accomplishes its goalYou can iterate over a collection of machinesWhen you do so, you can call body.execute() which will execute the desired command on each machine encountered102public class ForCommand extends Command { protected MachineComponent root; protected Variable variable; protected Command body;

public ForCommand(MachineComponent mc, Variable v, Command body) { this.root = mc; this.variable = v; this.body = body; } private void execute(MachineComponent mc) { if (mc instanceof Machine) { Machine m = (Machine) mc; variable.assign(new Constant(m)); body.execute(); return; } }103Note that this execute call is not a recursive call of the execute inside the ForCommand classIt is a call to execute on whatever command was passed into the ForCommand class when it was constructedIf you want to toy with insanity, think of how you would do nested loops using this machineryYou would pass a ForCommand object as the body to another ForCommand object104This code is not easy to understand, especially without seeing how its calledThe lack of understandability is unfortunateIt is also a symptom of the fact that as implemented by the authors, this design pattern relies on violating encapsulation105The book next gives a complete little program that shows how you could use ForCommand to shut down every machine in a factoryThe idea is that you would use ForCommand to iterate over all of the machines in the factory, where the body of the loop consisted of a call to a ShutDownCommand command with the current machine as its parameterThe code is given on the next overhead106public class ShowDown { public static void main(String[] args) { MachineComposite dublin = OozinozFactory.dublin(); Variable v = new Variable("machine"); Command c = new ForCommand(dublin, v, new ShutDownCommand(v)); c.execute(); }}107The previous example is mainly just a reminder of the power of iterationHowever, it also serves the purpose of beginning to illustrate the power of the Interpreter design patternInstead of having to hand code the shutdown of every machine, it is possible to do this by constructing and executing a single instance of the ForCommand class108Continuing with the development of the Interpreter design pattern:An interpreter would become even more complete by adding commands like IfCommand and WhileCommand which are additional constructs of high level languagesBoth of these constructs require the ability to test a condition that will give a boolean result109The book hints that the way to do this might be to develop a new hierarchy of boolean classesHowever, the authors opt to put the new classes into the Term hierarchy they already introducedIts hard to say whether this is a great idea110It has the obvious advantage of not requiring that a new hierarchy be developedIt also has the apparent disadvantage that the classes in the hierarchy dont necessarily seem relatedHowever, it does work because the new classes will be based on an eval() method, and thats the method that Variable and Constant classes were based on111boolean results will be modeled using this technique:The eval() method will be typed to return a reference to a machineIf eval() returns null, this will signify falseIf eval() returns a reference to an actual machine, that will signify trueThe UML diagram on the next overhead shows the new Term hierarchy with an Equals and a HasMaterial class added to it

112113

The Equals class has a constructor that takes in two terms as parametersThe eval() method doesnt take in any parametersIt uses the eval() method of the Term class to convert its instance variable terms to machine referencesIt then uses the equals() method of the Machine class to test for equalityIt returns the reference to the first machine if the two machines are equal, and returns null if theyre not equalThe code is shown on the following overhead114public class Equals extends Term { protected Term term1; protected Term term2;

public Equals(Term term1, Term term2) { this.term1 = term1; this.term2 = term2; }

public Machine eval() { Machine m1 = term1.eval(); Machine m2 = term2.eval(); return m1.equals(m2) ? m1 : null; }}115The HasMaterial class has a constructor that takes in one term as a parameterThe eval() method doesnt take in any parametersIt uses the eval() method of the Term class to convert its instance variable term to a machine referenceIt then uses the hasMaterial() method of the Machine class to see whether the machine has any materialIt returns the reference to the machine if it has material, and returns null if it doesntThe code is shown on the following overhead

116public class HasMaterial extends Term { protected Term term;

public HasMaterial(Term term) { this.term = term; }

public Machine eval() { Machine m = term.eval(); return m.hasMaterial() ? m : null; }}117If youre going to have if statements, it is helpful to also have a null commandThis represents the situation where you have either an empty if or an empty elseIn practice, this is just a command where the execute() method itself is emptyCode for a NullCommand class is shown on the next overhead118public class NullCommand extends Command{ public void execute() { }}119With boolean conditions modeled, its possible to add an IfCommand and a WhileCommand to the Command hierarchyThe UML diagram on the next overhead shows thisNote that just like with ForCommand, IfCommand and WhileCommand have a reference to another commandThis other command will be the body of the if statement or the while statement, respectively120121

The book does the completion of the code for the IfCommand, and the entirety of the code for the WhileCommand as challengesAs usual, its easiest not to bother with incomplete code and to go directly to the challenges and their solutions122Challenge 25.2Complete the code in the execute() method of the IfCommand class.Solution 25.2One solution is:[See the complete code on the following overhead.]123public class IfCommand extends Command { protected Term term; protected Command body; protected Command elseBody;

public IfCommand(Term term, Command body, Command elseBody) { this.term = term; this.body = body; this.elseBody = elseBody; }

public void execute() { if (term.eval() != null) body.execute(); else elseBody.execute(); }}124Challenge 25.3Write the code for the WhileCommand class.Solution 25.3One way to write WhileCommand.java is:[See the code on the following overhead.]125public class WhileCommand extends Command { protected Term term; protected Command body;

public WhileCommand(Term term, Command body) { this.term = term; this.body = body; }

public void execute() { while (term.eval() != null) body.execute(); }}126Once you start getting used to the Interpreter design pattern, the code for these command classes doesnt seem too difficultThe execute() method wraps the logic for if and while, using terms and bodies as stand-ins for whatever actual items and actions come from the problem domainIn particular, these two kinds of commands are easier than the ForCommand, because no variable is involved127The book next illustrates using the WhileCommandThe code for the ShowWhile program is given on the following overheadWhile the start press has material, that material will be carried to the fuser128public class ShowWhile { public static void main(String[] args) { MachineComposite dublin = OozinozFactory.dublin(); Term starPress = new Constant((Machine) dublin.find("StarPress:1401")); Term fuser = new Constant((Machine) dublin.find("Fuser:1101")); starPress.eval().load(new Bin(77)); starPress.eval().load(new Bin(88)); WhileCommand command = new WhileCommand( new HasMaterial(starPress), new CarryCommand(starPress, fuser)); command.execute(); }}129As with the other programs that illustrated the use of the Interpreter design pattern, this one ended with the construction of a single command object and the calling of execute() on that objectThat one object had packaged up in it potentially many other individual commands130The book explains whats happening with this one sentence, which summarizes the design pattern:The command object is an interpreter that interprets execute() to mean unload all the bins from star press 1401.In other words, the process of interpreting is simply polymorphism in action131You have a hierarchy of command classesEach class implements an execute() methodYou can compose complex commands based both on the composite design pattern and inherently complex commands like for, if, and whileThen at run time, interpretation simply means letting polymorphism figure out what execute() means for each of the individual commands that are encountered132Once again, this pattern starts to look like lots of other patterns that functioned as a result of polymorphismAt this point, the books devotion to the concept of intent starts to seem more reasonableEven though this pattern is a simple application of polymorphism, what is being accomplished is not obviously so simple133This was the books definition of the pattern:The intent of the Interpreter pattern is to let you compose executable objects according to a set of composition rules that you define.Once youve seen the pattern, maybe the definition makes sense

134A clumsier, more functionally oriented description might go something like this:The Interpreter design pattern is based on classes that wrap the concepts of a programming languageThe classes have a common execute() method, so the pattern depends on polymorphism in order to work135Challenge 25.4Close this book and write down a short explanation of the difference between Command and Interpreter.Comment mode on:Sure, now they get all strict and make us close the book

136Solution 25.4One answer is: The intent of the Interpreter pattern is to let you compose executable objects from a hierarchy of classes that provide various interpretations of a common operation. The intent of Command is merely to encapsulate a request in an object137Solution 25.4, contd.Can an interpreter object function as a command? Sure! The question of which pattern applies depends on your intent. Are you creating a toolkit for composing executable objects, or are you encapsulating a request in an object?138Comment mode on:I would summarize my own answer in this way:If you are trying to build up a hierarchy of command classes that wrap a set of programming language constructs, then youre using the Interpeter design patternIf you are trying to wrap a single command for one-shot use, then youre using the Command pattern139At this point the book turns to the topic which I mentioned at the beginningWriting programs to create interpreter command objects seems kind of sillyWhy not just write the code for machine and robot control programs?The books scenario is as follows140You could write a simple high-level language that was designed for machine and robot controlThe book gives this as a pseudo-code example of a program in that language:for(m in factory) if(not(m is unloadBuffer)) ub = findUnload for m while(m hasMaterial) carry(m, ub)

141Then, in theory, you could write a parser program that took machine control programs as input and created interpreters as outputIn other words, you wouldnt have to handcode a program like ShowInterpreter; the parser would do it for you142The benefit you would gain is that users would not have to code in JavaThey could code in the machine control language and the parser/interpreter combination would produce Java programs that implemented the machine control programs written by users

143SummaryThe Interpreter design pattern is based on a hierarchy of command classesThe names of the command classes suggest what it is they doEach class implements an execute() methodThe implementation should correspond to the name of the class144The class constructors should take the needed input parametersThe execute method should then work on instance variables and not need input parametersSome of the commands will be simple actionsOthers will be commands that make it possible to compose multiple simple commands145The interpreter design pattern may be accompanied by a hierarchy that defines terms for variables, booleans, etc.In order to be practical, interpreters may also be used along with parsers, although how parsing works wasnt really covered in the chapter146The End147