CSE-8B / PA6 / writeup / writeup.md
writeup.md
Raw

PA 6 - Polymorphism

Assignment Overview

Due date: Tuesday, February 18 @ 11:59PM

This assignment is intended to further your understanding of inheritance of implementation and interfaces through the use of abstract classes and interfaces, respectively. More generally, it should give you a sense of how the object-oriented programming paradigm can be implemented in Java. It will also serve as a gentle introduction to boolean expressions if you are not yet familiar with them.

Getting Started

There are a number of ways to get started on development. The following is the recommended way to ensure that your code will compile during grading.

  1. If you are using your own machine or are on a lab computer to complete the assignment, go to step 2 directly. Otherwise, ssh into your cs8bwi20 account.
    • ssh cs8bwi20__@ieng6.ucsd.edu
  2. Acquire the starter files.
    • From ieng6:
      • Log in to your cs8bwi20 account.
      • From the command line, use the command cp -r ~/../public/pa6 ~/ (this will copy the entire starter files directory to your home directory)
      • Type ls ~ to verify that you have copied the pa6 directory over.
    • From GitHub:
      • git clone https://github.com/CaoAssignments/cse8b-wi20-pa6-polymorphism-starter.git
      • Alternatively, you can download the repo as a zipped folder.
  3. If you downloaded the repo as a zipped folder, navigate to it through your terminal or text editor (Atom, Eclipse, etc.). If you git cloned the repo, you can switch into that directory immediately.
    • cd cse8b-wi20-pa6-polymorphism-starter
    • Optional: You may choose to rename this repo. You can do this by using this command (before the cd command above):
      • mv cse8b-wi20-pa6-polymorphism-starter pa6
  4. You can now start working on it through vim using the following command or open the directory in your preferred editor.
    • vim Expression.java or gvim Expression.java
  5. To compile your code, use the javac command.
    • Syntax: javac file1.java file2.java etc...
    • Example: javac BooleanExpressionTester.java or simply javac *.java
  6. To run your code, use the java command passing in the name of the class with the main method that you want to run.
    • Syntax: java nameOfClass
    • Example: java BooleanExpressionTester

Provided Files

Only one file will be provided, namely the tester: ExpressionTester.java

You are responsible for testing all cases in order to ensure that your code works as intended. If you have any questions about the behavior of certain cases that are not addressed in the writeup, please make a Piazza post.

Inheritance Hierarchy

In this assignment, you are implementing several boolean and arithmetic operations, such as conjunction (the && operator) and addition.

Each operation will be broken down into their individual classes so we can practice polymorphism. For example, to perform addition, you will need to create a Sum object that will take in two ArithmeticExpression objects. From this, you can call on the evaluate method from the Sum object that will return the sum of the two ArithmeticExpression objects. Boolean expressions will also follow a similar workflow.

Diagram

Looking at the diagram, we can see that all classes will inherit from the Expression class. All Expression objects will have an evaluate method that will return a Value upon evaluation.

For this assignment, there will be two subclasses (types) of Expression - ArithmeticExpression and BooleanExpression. Similarly, there will be two subclasses of Value - IntegerValue and BooleanValue.

Upon calling on the evaluate method which returns a Value, ArithmeticExpression will return an IntegerValue, while BooleanExpression will return a BooleanValue. (Think about why you can do this.)

Something you will notice is that Value implements both BoolEvaluable and IntEvaluable. These interfaces "decorate" our Value class and, in theory, allows us to evaluate any Value as a boolean or int. Both interfaces have a single method that you can call on to return either a boolean or int with respect to the value that the Value object represents (such as false or 10).

Because IntegerValue and BooleanValue both inherit from Value, they also inherit the "trait" of being evaluable as a boolean and an int. You might ask, how is this possible? To put it simply, we will treat the conversion in the following manner.

Conversion Guide
  • From boolean to int:
    • false => 0
    • true => 1
  • From int to boolean:
    • 0 => false
    • any other int => true

Your Task:

You will need to create 3 interfaces, 3 abstract classes, and 13 concrete classes (yes, you read that correctly) for a total of 19 files.

IMPORTANT NOTES
  • If any of the operands are null upon calling evaluate, return null. For example, true || null should return null. This applies to all operators.
  • For each operator that you will implement (the concrete classes), keep in mind that you will be passing in a BooleanExpression or ArithmeticExpression. This means that you will have to evaluate this expression before performing the operation.
    • For example, if we created a Negative object and passed in an Sum object with the operands being 5 and 6, then you need to evaluate the Sum object first before performing Negative.
    • This would look like -(5 + 6) where you would evaluate (5 + 6) first.
  • You should not have to create additional instance variables other than the ones mentioned. If you find yourself creating some, look in the parent classes to see if there are any that you can use.
  • You do not have to worry about Integer overflow/underflow for this assignment. We will let Java handle it for us.

Interfaces

BoolEvaluable.java: 1 abstract method

  • Declare a public method named boolEvaluate that takes in no arguments and returns a boolean

IntEvaluable.java: 1 abstract method

  • Declare a public method named intEvaluate that takes in no arguments and returns an int

Value.java

  • This interface contains nothing
  • We are defining it here so that the return value of the method evaluate can accept both IntegerValue and BooleanValue types
  • In general, you would be able to add more function declarations to this file if you believe any subclass of Value requires some functionality

Abstract Classes

Expression.java: 1 abstract method, 2 concrete methods

  • Declare a public abstract method named evaluate that takes in no arguments and returns a Value
  • Override the toString method by calling on the evaluate method and returning the String representation of the Value object that is returned from evaluate
    • If the evaluate returns null, then toString should return the String "undefined"
  • Override the equals method by comparing the toString value of the caller and the toString value of argument
    • Remember that this method should never return true if one of the compared objects is not actually an Expression object

ArithmeticExpression.java: 2 instance variables

  • This abstract class will extend Expression
  • Declare two public ArithmeticExpression instance variables
  • These will represent the (up to) 2 operands to an arithmetic operation (e.g., a and b in the expression a + b)
    • Subclasses will use these objects for their evaluations

BooleanExpression.java: 2 instance variables

  • This abstract class will extend Expression
  • Declare two public BooleanExpression instance variables
  • These will represent the (up to) 2 operands to an boolean operation (e.g., a and b in the expression a AND b)
    • Subclasses will use these objects for their evaluations

Concrete Classes

These concrete classes will extend some of the abstract classes that were defined above. They will also implement the methods that were declared in the interfaces and abstract classes.

Classes that extend BooleanExpression

These classes will allow you to perform the negation (!), conjunction (&&), disjunction (||), equivalence (==), and exclusive disjunction operations and implication (=>) expression. Make sure they extend BooleanExpression.

BooleanValue.java: 1 instance variable, 1 constructor, 4 other concrete methods

  • This class will represent a boolean with extra functionality
    • Implement Value,IntEvaluable and BoolEvaluable
    • Declare a public boolean instance variable. This will store the boolean value of this BooleanValue object
    • Create a public constructor that takes in a single boolean and initializes the instance variable according to the argument
  • You also need to implement the methods that were defined in the superclasses and their interfaces
    • Implement (and override) the evaluate method by returning a reference to this instance
    • Implement (and override) the intEvaluate method by returning the respective int mentioned in the conversion guide above
    • Implement (and override) the boolEvaluate method by returning the primitive boolean this BooleanValue represents
    • Implement (and override) the toString method by returning the String value "true" if this BooleanValue represents true and the String value "false" if it represents false

Negation.java: 1 constructor, 1 other concrete method

  • Create a public constructor that takes in a single BooleanExpression and stores it (note that this will only use one of your two instance variables, so make sure that you only use the one that you set when you evaluate)
  • Implement the evaluate method by returning the negation of the result of the BooleanExpression that was passed into the constructor
    • If the BooleanExpression is invalid (in other words, null or it evaluates to something that is not evaluable as a boolean) then evaluate should return null.

Truth table for negation (you may know this as NOT)

  • A is the result of the boolean expression
A Output (NOT A)
false true
true false

Conjunction.java: 1 constructor, 1 other concrete method

  • Create a public constructor that takes in two BooleanExpression and stores them
  • Implement the evaluate method by returning the conjunction of the result of the first BooleanExpression and the result of the second BooleanExpression
    • If either of the BooleanExpressions is invalid (null or evaluates to something that is not evaluable as a boolean) then evaluate should return null

Truth table for conjunction (you may know this as AND)

  • A is the result of the first boolean expression
  • B is the result of the second boolean expression
A B Output (A AND B)
false false false
false true false
true false false
true true true

Disjunction.java: 1 constructor, 1 other concrete method

  • Create a public constructor that takes in two BooleanExpression and stores them
  • Implement the evaluate method by returning the disjunction of the result of the first BooleanExpression and the result of the second BooleanExpression
    • If either of the BooleanExpressions is invalid (null or evaluates to something that is not evaluable as a boolean) then evaluate should return null.

Truth table for disjunction (you may know this as OR)

  • A is the result of the first boolean expression
  • B is the result of the second boolean expression
A B Output (A OR B)
false false false
false true true
true false true
true true true

Equivalence.java: 1 constructor, 1 other concrete method

  • Create a public constructor that takes in two BooleanExpression and stores them
  • Implement the evaluate method by returning the equivalence of the result of the first BooleanExpression and the result of the second BooleanExpression
    • If either of the BooleanExpressions is invalid (null or evaluates to something that is not evaluable as a boolean) then evaluate should return null
    • Note: We are not handling the case of both being null as returning a true value. It should still return null in this case.

Truth table for equivalence (you may know this as ==)

  • A is the result of the first boolean expression
  • B is the result of the second boolean expression
A B Output (A == B)
false false true
false true false
true false false
true true true

ExclusiveDisjunction.java: 1 constructor, 1 other concrete method

  • Create a public constructor that takes in two BooleanExpression and stores them
  • Implement the evaluate method by returning the exclusive disjunction of the result of the first BooleanExpression and the result of the second BooleanExpression
    • If either of the BooleanExpressions is invalid (null or evaluates to something that is not evaluable as a boolean) then evaluate should return null

Truth table for exclusive disjunction (you may know this as XOR)

  • A is the result of the first boolean expression
  • B is the result of the second boolean expression
A B Output (A XOR B)
false false false
false true true
true false true
true true false

Implication.java: 1 constructor, 1 other concrete method

  • Create a public constructor that takes in two BooleanExpression and stores them
  • Implement the evaluate method by returning the implication of the result of the first BooleanExpression and the result of the second BooleanExpression (the first argument to the constructor implies the second argument to the constructor).
    • If either of the BooleanExpressions is invalid (null or evaluates to something that is not evaluable as a boolean) then evaluate should return null

Truth table for implication (you may know this as =>)

  • A is the result of the first boolean expression
  • B is the result of the second boolean expression
A B Output (A => B)
false false true
false true true
true false false
true true true

Classes that extend ArithmeticExpression

These classes will allow you to perform the negative (-), sum (+), difference (-), product (*), and quotient (/) operations. Make sure they extend ArithmeticExpression.

IntegerValue.java: 1 instance variable, 1 constructor, 4 other concrete methods

  • Similar to BooleanValue, this class will represent an int with extra functionality
    • Implement Value,IntEvaluable and BoolEvaluable
    • Declare a public int instance variable. This will store the int value of this IntegerValue object
    • Create a public constructor that takes in a single int and initializes the instance variable according to the argument
  • Again, you need to implement the methods that were defined in the superclasses and their interfaces
    • Implement (and override) the evaluate method by returning a reference to this instance
    • Implement (and override) the intEvaluate method by returning the int that this IntegerValue represents
    • Implement (and override) the boolEvaluate method by returning the respective boolean mentioned in the conversion guide above
    • Implement (and override) the toString method by returning the String representation of the int that this IntegerValue represents
      • For example, if this IntegerValue represents 10, this method should return the String value "10".

Negative.java: 1 constructor, 1 other concrete method

  • Create a public constructor that takes in a single ArithmeticExpression and stores it (note that this will only use one of your two instance variables, so make sure that you only use the one that you set when you evaluate)
  • Implement the evaluate method by returning the result of the ArithmeticExpression that was passed into the constructor multiplied by -1
    • If the ArithmeticExpression is invalid (null or evaluates to something that is not evaluable as an int) then evaluate should return null

Sum.java: 1 constructor, 1 other concrete method

  • Create a public constructor that takes in two ArithmeticExpression and stores them
  • Implement the evaluate method by returning the sum of the result of the first ArithmeticExpression and the result of the second ArithmeticExpression
    • If either of the ArithmeticExpressions is invalid (null or evaluates to something that is not evaluable as an int) then evaluate should return null

Difference.java: 1 constructor, 1 other concrete method

  • Create a public constructor that takes in two ArithmeticExpression and stores them
  • Implement the evaluate method by returning the difference of the result of the first ArithmeticExpression and the result of the second ArithmeticExpression (the first argument to the constructor minus the second argument to the constructor)
    • If either of the ArithmeticExpressions is invalid (null or evaluates to something that is not evaluable as an int) then evaluate should return null

Product.java: 1 constructor, 1 other concrete method

  • Create a public constructor that takes in two ArithmeticExpression and stores them
  • Implement the evaluate method by returning the product of the result of the first ArithmeticExpression and the result of the second ArithmeticExpression
    • If either of the ArithmeticExpressions is invalid (null or evaluates to something that is not evaluable as an int) then evaluate should return null
    • Note: We are not handling the case where one ArithmeticExpression is null and the other is 0 by returning a 0 value. It should still return null in this case.

Quotient.java: 1 constructor, 1 other concrete method

  • Create a public constructor that takes in two ArithmeticExpression and stores them
  • Implement the evaluate method by returning the quotient of the result of the first ArithmeticExpression divided by the result of the second ArithmeticExpression (the first argument to the constructor divided by the second argument to the constructor).
    • If either of the ArithmeticExpressions is invalid (null or evaluates to something that is not evaluable as an int) then evaluate should return null.
    • Note: If the result of the ArithmeticExpression divisor is 0, then it is invalid (divide by zero exception will occur). Return null in this case.

Testing Your Code:

We have provided the tester file ExpressionTester.java for the purposes of testing your code. In it, you will find examples of how to test your evaluate methods.

In each sample test, we are creating a new ArithmeticExpression or BooleanExpression and calling System.out.println after performing an operation on the Values that we pass in. Recall that System.out.println() implicitly calls an object's toString method so in order for your output to be displayed, you must have implemented the toString methods in both IntegerValue and BooleanValue.

Although our other expression concrete classes do not implement toString, they have inherited the functionality that we desire from the Expression class. Recall that in the Expression class, we have implemented the toString method to return the String representation of the Value object that is returned after calling evaluate.

You should also try testing your code on more complex expressions, such as the nested expression present in the test case arithComplex.

IntegerValue one = new IntegerValue(1);
IntegerValue two = new IntegerValue(2);
IntegerValue three = new IntegerValue(3);

Expression arithComplex = new Sum(one, new Difference(three, two));
System.out.println("1 + (3 - 2) evaluated to: " + arithComplex);

The constructor of Sum expects two ArithmeticExpressions, and since both IntegerValue and Difference extend ArithmeticExpression, we can use them as arguments to Sum's constructor. Notice that the order in which we pass in our expressions to the constructors matter. Had we done Expression arithComplex = new Sum(one, new Difference(two, three)); we would have ended up with the expression 1 + (2 - 3) which would have evaluated to 0 instead of 2. Following these examples, create more tests on your own in order to thoroughly test the functionality of evaluate in both ArithmeticExpression and BooleanExpression. You may also want to print the actual (what your code evaluates) and the expected (what you manually calculate) side-by-side for easy comparison (or maybe print out a boolean saying if they are equal or not).

README

The following questions about provided files and general policies are graded for fair effort and completeness. Your file should be named README.md.

  1. Your classmate missed class (oh no!) and doesn't understand polymorphism. In your own words, explain the concept of polymorphism so your classmate can understand.
  2. As mentioned earlier, we can return IntegerValue or BooleanValue when we call on evaluate despite the return value of evaluate being Value. Why can we do this?
  3. In the BooleanExpression and ArithmeticExpression classes, we inherit from Expression. Yet, we don't implement the evaluate method. Why is this allowed?
  4. What is the difference between inheritance of interfaces and inheritance of implementation?
  5. Why does Java support multiple inheritance of interfaces but not multiple inheritance of implementation?

Student Satisfaction Survey

Please fill out our student satisfaction survey. We are changing how we approach giving assignments and would like to hear about your experiences.

Style

We will grade your code style thoroughly. Namely, there are a few things you must have in each file / class / method (this includes the README.md):

  1. File header
  2. Class header
  3. Method header(s)
  4. Inline comments
  5. Proper indentation
  6. Descriptive variable names
  7. No magic numbers
  8. Reasonably short methods (if you have implemented each method according to specification in this write-up, you’re fine). This is not enforced as strictly.
  9. Lines shorter than 80 characters
  10. Javadoc conventions (@param, @return tags, /** comments */, etc.)

A full style guide can be found here. If you need any clarifications, feel free to ask on Piazza.

Submission

Required Submission Files (20 files)

  • ArithmeticExpression.java
  • BoolEvaluable.java
  • BooleanExpression.java
  • BooleanValue.java
  • Conjunction.java
  • Difference.java
  • Disjunction.java
  • Equivalence.java
  • ExclusiveDisjunction.java
  • Expression.java
  • Implication.java
  • IntEvaluable.java
  • IntegerValue.java
  • Negation.java
  • Negative.java
  • Product.java
  • Quotient.java
  • Sum.java
  • Value.java
  • README.md

Start early and start often!