Programming讲解、辅导Java、Java程序语言调试、hierarchy讲解 讲解R语言编程|调试Matlab程序

- 首页 >> 其他
Programming Project 3

Due Wednesday, November 6 at 11:59pm

IMPORTANT: Read the Do's and Dont's in the Course Honor Policy found on blackboard.

I. Overview

The purpose of this project is to give you practice designing a class/type hierarchy. It is important that you spend time designing your class hierarchy before you start coding. If you properly organize your classes and/or interfaces, you can achieve the desired program behavior below with significantly less code than with a poorly organized hierarchy. This project will also how there are limitations to what we can do with Java's classes and interfaces.

II. Code Readability (20% of your project grade)

New for this assignment: The comments above the class or interface and above each method must be written in JavaDoc format. You will be introduced to JavaDoc style commenting in the labs. You can also find a description in the Java in a Nutshell text. Be sure to run JavaDoc and view the webpage in order to verify that you have implemented the comments correctly.

To receive the full readability marks, your code must follow the following guideline:

All variables (fields, parameters, local variables) must be given appropriate and descriptive names.
All variable and method names must start with a lowercase letter. All class and interface names must start with an uppercase letter.
The class body should be organized so that all the fields are at the top of the file, the constructors are next, the non-static methods next, and the static methods at the bottom.
There should not be two statements on the same line.
All code must be properly indented (see page 644 of the Lewis book for an example of good style). The amount of indentation is up to you, but it should be at least 2 spaces, and it must be used consistently throughout the code.
You must be consistent in your use of {, }. The closing } must be on its own line and indented the same amount as the line containing the opening {.
There must be an empty line between each method.
There must be a space separating each operator from its operands as well as a space after each comma.
There must be a comment at the top of the file that is in proper JavaDoc format and includes both your name and a description of what the class represents. The comment should include tags for the author.
There must be a comment directly above each method (including constructors) that is in proper JavaDoc format and states what task the method is doing, not how it is doing it. The comment should include tags for any parameters, return values and exceptions, and the tags should include appropriate comments that indicate the purpose of the inputs, the value returned, and the meaning of the exceptions.
There must be a comment directly above each field that, in one line, states what the field is storing.
There must be a comment either above or to the right of each non-field variable indicating what the variable is storing. Any comments placed to the right should be aligned so they start on the same column.
There must be a comment above each loop that indicates the purpose of the loop. Ideally, the comment would consist of any preconditions (if they exist) and the subgoal for the loop iteration.
Any code that is complicated should have a short comment either above it or aligned to the right that explains the logic of the code.
III. Program Testing (20% of your project grade)

New for this assignment: The testing routines should be included in a JUnit test class.

You are to write a test report that indicates the types of tests needed to thoroughly test your project. The tests should demonstrate that all of your methods behave correctly. An conditional statements will need tests that go through each branch of the execution. Any loops will need tests that cover the "test 0, test 1, test many" and "test first, test middle, test last" guidelines. Your testing report should not list the actual tests and results.

You are to have a JUnit test class or classes that implement each of the needed tests. Comments and method names in your JUnit class should connect to your testing report. For example, if your testing report states "method xxx must be tested with string inputs of different lengths", then the reader should be able to go to the JUnit class and easily identify the tests that test that method on inputs of length 0, 1, and more than 1.

The testing report must be separate from the JUnit class. In most companies, the testing document will be written in a style that allows both programmers and non-programmers to read it and recognize whether all the needed test cases were included.

IV. Java Programming (60% of your grade)

Design Rules: Your project must contain the following seven types and each type must contain the listed methods. The project may use any combination of classes, abstract classes, or interfaces that you feel is appropriate. The project may add additional types to the ones listed. The classes/types may contain additional methods to the ones listed if you feel they are needed. You may use any combination of inheritance, method overriding, and method overloading to achieve the needed behavior. Part of the coding grade will be the quality of the hierarchy you create.

Hint (repeated from above): Spend a lot of time designing your hierarchy before you code. A well designed hierarchy will reduce the amount of code you have to write.

A note on types: you may find generic types and wrapper classes to be useful in this assignment though they are not required. We will be covering both generic types and wrapper classes before this project is due but, again, they are not required for this project. If you know about generic types and wrapper classes and want to use them, you are welcome to do so because they can help reduce your coding. However, you should use them properly. That means that if the compiler gives you a warning message that you are using one incorrectly, be sure to correct the error and do not submit code that gives warnings.

Programming (60% of the project grade)

Here are two Java shortcuts you will use: enum and variable length parameters.

An overview of enum

The project will be using enum types. An enum is a shortcut for a class with a private constructor. The code

enum WeekDay {
Monday, Tuesday, Wednesday, Thursday, Friday;
you may add additional methods here
}
is identical to
public static class WeekDay {
public static final WeekDay Monday = new WeekDay();
public static final WeekDay Tuesday = new WeekDay();
public static final WeekDay Wednesday = new WeekDay();
public static final WeekDay Thursday = new WeekDay();
public static final WeekDay Friday = new WeekDay();

private WeekDay() {
}

some special helper methods provided for enums (see your text)

you may add additional methods here
}
So, WeekDay will be a static "inner" or "nested" class of whatever class you place the enum inside. The Monday, Tuesday, etc. are fields set to instances of the WeekDay class. Because the constructor is private, no other instances can be created than one instance for each of the listed fields. (Note that we do not need to override the equals method in an enum. Since no other instances can be created than those stored in the fields, you can use == to compare enum values.) As entered, you have a default private constructor for the enum, but you can create your own constructor if you wish. For example, we could create a constructor that takes a String that is the name of the day. If we do that, we would need to do the following:
enum WeekDay {
Monday("Monday"), Tuesday("Tuesday"), Wednesday("Wednesday"), Thursday("Thursday"), Friday("Friday");

private String name;

private WeekDay(String name) {
this.name = name;
}

you may add additional methods here
}
An overview of variable length parameters

A variable length parameter is a Java shortcut that can be used by a method that takes an array as input. With the shortcut, you do not need to create the array explicitly. For example:

public int maximum(int[] a) {
int max = 0;
for (int i = 1; i < a.length; i++)
if (a[i] > a[max])
max = i;
return max;
}
is the normal way we pass an array to a method. To call maximum, we must create an array first: maximum(new int[]{1, 2, 3}).
If we change maximum to take a variable length parameter, it looks like this:

public int maximum(int... a) {
int max = 0;
for (int i = 1; i < a.length; i++)
if (a[i] > a[max])
max = i;
return max;
}
Notice that the body of maximum did not change. The input a is still an array of int. However, we now have two ways to call a. We can still use the traditional way of passing in an array: maximum(new int[]{1, 2, 3}) or we can just pass in the elements and Java will automatically place them in an array of the correct size: maximum(1, 2, 3). Note that there can be only one variable length parameter per method, and the variable length parameter must be the last parameter of the method. (Can you see why?)
Your programming task

Some programming languages, like Scratch, are designed for beginning programmers, and especially children, to learn the basics of coding. In these languages, coding structures are often displayed graphically as colored blocks and shaped in a way to indicate what types of structures can be contained in other structures. The purpose of this code is to design the underlying model used for a simple Scratch-like programming language. You will design classes that represent different programming language strucrures, and then a user can program by combining the different structures together.

Your project should contain the following types. Each type can be a class or an interface. You may need to create additional types to accomplish all the tasks. The goal is to write a good hierachy that limits the amount of code you need to write:

State: The state represents the variables and their contents. Because our language is geared to true novices, the only type we will allow is integer types. Since everything is an integer, we will not have errors if a variable is used before it is assigned a value.

The State should contain a Hashtable from the Java API. You should set the key of the hashtable to be String (this will represent the names of the variables) and the values of the hashtable to be type Integer (this will represent the values stored in the variables. That is, you will represent the state as a Hashtable. The State class should have the following methods:

update takes a variable name and a variable value. The name and value pair should be added to the hash table.
lookup takes a variable name and returns the int/Integer value of the variable, as stored in the hashtable. If there is no such variable in the hashtable, the value returned should be 0. (No error should be given because we want to avoid all errors in our language.)
Variable: The Variable type represents a variable in our program. A Variable type instance should be created with one argument: a string name, and the type should have the following methods:

value: takes a state as input and returns the int/Integer value associated with this variable name that is stored in the input state.
getName: should return the name of the variable.
toString: should return the name of the variable.
Number: The Number type represents a whole number. A Number type instance should be created with one argument: an int/Integer value, and the type should have the following methods:

value: takes a state as input and returns the int/Integer value of the number.
toString: returns a string representation of the numeric value of the number.
ArithmeticOperation: The ArithmeticOperation represents an arithmetic operation between two values. The ArithmeticOperation type should contain the following enum type to represent the possible operations for the language (you are free to add additional operators if you wish):

public enum Operator { Add, Sub, Mult, Div, Rem; }
The operators represent the +, -, *, /, and % operations.
An ArithmeticOperation type instance should be created with three arguments: an operation that is from the above Operator enum type, and two expressions that represent the left operand expression and the right operand expression. The type should allow any language structure that has an int/Integer value to be a possible expression. The ArithmeticOperation type should have the following methods:

value: takes a state as input and returns the int/Integer value that is the result of applying the operation to the values of each of the operand expressions. The state should be used to get the values of the expressions.
toString: should return a string that first contains a string representation of the left operand followed by a space, a string representation of the operator, a space, and the string representation of the right operand.
Comparison: The Comparison type represents a comparison between two expressions. The Comparison type should contain the following enum type to represent the possible comparisons for the langauge (you are free to add additional operators if you wish):

public enum Operator { LT, LTE, GT, GTE, EQ, NEQ; }
The operators represent the < <=, >, >=, ==, and != operations.
The Comparison type instance should be created with three arguments: an operation that is from the above Operator enum type, and two expressions that represent the left operand expression and the right operand expression. The type should allow any language structure that has an int/Integer value to be a possible expression. The Comparison type should have the following methods:

value: takes a state as input and returns the boolean/Boolean value that is the result of applying the operation to the values of each of the operand expressions. The state should be used to get the values of the expressions.
toString: should return a string that first contains a string representation of the left operand followed by a space, a string representation of the operator, a space, and the string representation of the right operand.
BooleanOperation: The BooleanOperation type represents a boolean operation between two boolean conditions. The BooleanOperation type should contain the following enum type to represent the possible operations for the language (you are free to add additional operators if you wish):

public enum Operator { And, Or; }
The operators represent the && and || operations.
An BooleanOperation type instance should be created with three arguments: an operation that is from the above Operator enum type, and two boolean conditions that represent the left operand and the right operand. The type should allow any language structure that has a boolean/Boolean value to be a possible condition. The BooleanOperation type should have the following methods:

value: takes a state as input and returns the boolean/Boolean value that is the result of applying the operation to the values of each of the operand conditions. The state should be used to get the values of the conditions.
toString: should return a string that first contains a string representation of the left operand followed by a space, a string representation of the operator, a space, and the string representation of the right operand.
Assignment: The Assignment type represents an assignment operation/statement in the language. An Assignment type instance should be created with two arguments, a variable and an expression. Any language structure that has an int/Integer value should be allowed as the expression. The Assignment type should have the following methods:

execute: takes a state and returns nothing. The method updates the state to set the value of the variable to be the value of the expression. The state should be used to get the value of the expression.
value: takes a state and returns a int/Integer. The method updates the state to set the value of the variable to be the value of the expression. The state should be used to get the value of the expression. The value of the expression is also returned from the method.
toString: should return a string that contains the variable's string value, followed by a space, followed by ":=" character, followed by a space, followed by a string representation of the expression followed by the newline character.
toStringTabbed: takes an int as input and returns a string. The returned string should be indentical to what is returned by toString except that the string is prefixed by a number of tab characters equal to the value of the input int. You may assume the input value is not negative.
Return: The Return type represents a return statement in the language. A Return type instance should be created with one argument, an expression. Any language structure that has an int/Integer value should be allowed as the expression. The Return type should have the following methods:

execute: takes a state and returns nothing. The method updates the state to set the value of a special variable called "return" to have the value of the expression. The state should be used to get the value of the expression.
toString: should return a string that contains the text "return", followed by a space, followed by a string representation of the expression followed by the newline character.
toStringTabbed: takes an int as input and returns a string. The returned string should be indentical to what is returned by toString except that the string is prefixed by a number of tab characters equal to the value of the input int. You may assume the input value is not negative.
ConditionalStatement: The ConditionalStatement type represents a conditional statement (if) in the language. A ConditionalStatement type instance should be created with three arguments, a boolean condition and two statements representing the then statement and the else statement. Any language structure that has an boolean/Boolean value should be allowed as the condition, and any language structure that represents a statement should be allowed as either statement. The ConditionalStatement type should have the following methods:

execute: takes a state and returns nothing. The state is used to get the value of the condition. If the condition's value is true the state is used to executed the then statement. Otherwise the state is used to execute the the else statement.
toString: should return a string that contains the text "if", followed by a space, followed by a "(", followed by the string representation of the conditional, followed by a ")", followed by a newline character, followed by the result of calling toStringTabbed with input 1 on the then statement, followed by a "else", followed by a newline character, followed by the result of calling toStringTabbed with input 1 on the else statement.
toStringTabbed: takes an int as input and returns a string. The returned string should be indentical to what is returned by toString except that both the "if" and the "else" are prefixed by a number of tab characters equal to the value of the input int, and the toStringTabbed methods for the then and else statements are called with an input that is one more than the input to this method. You may assume the input value is not negative.
Loop: The Loop type represents a loop statement (while) in the language. A Loop type instance should be created with two arguments, a boolean condition and one statement representing the loop body. Any language structure that has an boolean/Boolean value should be allowed as the condition, and any language structure that represents a statement should be allowed as the body. The Loop type should have the following methods:

execute: takes a state and returns nothing. The method has a loop. At each iteration of the loop, the state is used to get the value of the condition. If the condition's value is true the state is used to executed the body statement. If the condition evaluates to false, the loop terminates and the method exits.
toString: should return a string that contains the text "while", followed by a space, followed by a "(", followed by the string representation of the conditional, followed by a ")", followed by a newline character, followed by the result of calling toStringTabbed with input 1 on the body statement.
toStringTabbed: takes an int as input and returns a string. The returned string should be indentical to what is returned by toString except that the "while" is prefixed by a number of tab characters equal to the value of the input int, and the toStringTabbed method for the body statement is called with an input that is one more than the input to this method. You may assume the input value is not negative.
CompoundStatement: The CompoundStatement type represents a compound statement/block of code in the language. A CompoundStatement type instance should be created with one variable length parameter of statements. Any language structure that represents a statement should be allowed as the body. The CompoundStatement type should have the following methods:

execute: takes a state and returns nothing. The method has a loop. At each iteration of the loop, the state is used to execute each of the statements in the array of statements, in order.
toString: should return a string that contains a text "{", followed by a newline character, followed by the result of calling the toStringTabbed method with input 1 on each of the statements in the list of statements, in order, followed by a "}", followed by the newline character.
toStringTabbed: takes an int as input and returns a string. The returned string should be indentical to what is returned by toString except that the "}" and "{" are prefixed by a number of tab characters equal to one less than the value of the input int, and the toStringTabbed method for each statement in the list is called with an input that is equal to the input to this method. You may assume the input value is not negative.
Note that, unlike in Java, any variable declared in the compound statement will still exist after the compound statement ends. (You do not have to try implement this feature, it will occur automatically.)
An example

If you get the above classes working, you can do the following. You will have to imagine somebody using a graphical interface to create different programming features and then plugging them into each other to create a program. This may look complex, but with a good hierarchy, you will not need a lot of code to be able to do everything below!

First, we can create a statement, and execute it:

> Variable x = new Variable("x")
> Assignment increment = new Assignment(x, new ArithmeticOperation(ArithmeticOperation.Operator.Add, x, new Number(1)))
> increment
x := x + 1
> State s = new State()
> s.lookup(x.getName())
0
> increment.execute(s)
> s.lookup(x.getName())
1
> increment.execute(s)
> s.lookup(x.getName())
2
Now, let's create a loop using the increment statement already created

> Variable result = new Variable("result")
> Assignment sumUpdate = new Assignment(result, new ArithmeticOperation(ArithmeticOperation.Operator.Add, result, x))
> sumUpdate
result := result + x
> Loop loop = new Loop(new Comparison(Comparison.Operator.LTE, x, new Number(10)), new CompoundStatement(sumUpdate, increment))
> loop
while (x < 10)
{
result := result + x
x := x + 1
}
Now, let's execute the loop on an empty state (all variables are default 0), and see what is stored in result at the end.

> State s = new State()
> loop.execute(s)
> s.lookup(result.getName())
55
This should be a "plug-and-play" type of coding, so we can change the loop condition and body

> loop.setCondition(new Comparison(Comparison.Operator.LTE, increment, new Number(10)))
> loop
while (x := x + 1 <= 10)
{
result := result + x
x := x + 1
}
> s = new State()
> s.lookup(result.getName())
25
> s.lookup(x.getName())
11
> loop.setBody(sumUpdate);
> loop
while (x := x + 1 <= 10)
result := result + x
> s = new State()
> loop.execute(s)
> s.lookup(result.getName())
55
Extra Credit

Create two additional types:

Function: The Function type represents a function definition in the language. A Function type instance should be created with three inputs: a string name, a statement that is the function body, and a variable length parameter of variables that will be this function's parameters. Any language structure that represents a statement should be allowed as the body. The Function type should have the following method:

toString: should return a string that contains a text "function", followed by the function's name, followed by "(", followed by each of the variables of the function, separated by a space and a comma (except the last one), followed by a ")", followed by the newline character, followed by the result of calling the toStringTabbed method of the body of the function with input 1.
Note, that unlike Java functions will not be able to access any variables declared outside of the function. You should not implement that here. It will be taken care of in the FunctionCall type described below.
FunctionCall: The FunctionCall type represents a function call in the language. A FunctionCall type instance should be created with two inputs: a Function and a variable length parameter of expressions that will be the arguments of this function call. Any language structure that produces an int/Integer value should be allowed as an expression. The FunctionCall type should have the following methods:

value: should take a state and return an int/Integer. The method should create a new empty state. For each variable in the function's list of parameters, the new state should be updated with this variable and the value from the corresponding argument. The original state should be used to get the value of the argument. If there is no corresponding argument, the value of 0 should be used for the update. Then, the function's body should be executed using the new state. Finally, the value of the keyword "return" should be looked up in the new state and returned from this function.
toString: should return a string that contains the function's name, followed by "(", followed by the string representation of each of the arguments of the function call, separated by a space and a comma (except the last one), followed by a ")".
toStringTabbed: takes an int as input and returns a string. The returned string should be indentical to what is returned by toString except that the "}" and "{" are prefixed by a number of tab characters equal to one less than the value of the input int, and the toStringTabbed method for each statement in the list is called with an input that is equal to the input to this method. You may assume the input value is not negative.
If you get the above classes working, you can do the following:

> Variable x = new Variable("x")
> Variable n = new Variable("n")
> Function f = new Function("sum", new CompoundStatement(new Assignment(x, new Number(0)), new Loop(new Comparison(Comparison.Operator.GT, n, new Number(0)), new CompoundStatement(new Assignment(x, new ArithmeticOperation(ArithmeticOperation.Operator.Add, x, n)), new Assignment(n, new ArithmeticOperation(ArithmeticOperation.Operator.Sub, n, new Number(1))))), new Return(x)), n)
> f.toString()
"function sum(n)
{
x := 0;
while (n > 0)
{
x := x + n;
n := n - 1;
}
return x;
}"

> new FunctionCall(f, new Number(1)).value(new State())
1
> new FunctionCall(f, new Number(10)).value(new State())
55
> Function m = new Function("main", new CompoundStatement(new Assignment(new Variable("y"), new Number(6)), new Return(new FunctionCall(f, new Variable("y")))))
> m.toString()
"function main()
{
y := 6;
return sum(y);
}"

> new FunctionCall(m).value(new State())
21