Description
COMP 2522 Object oriented programming Assignment 01
Introduction
Welcome to your first COMP 2522 assignment. Today you will apply what you have started learning as we
migrate from Python to Java. You will implement the first class for a larger project.
Developing software is often a highly iterative process. Iterative software development involves repeated
cycles of designing, coding, testing, and integrating small sections or increments of a program. We build
a little, show it to our clients, make adjustments, and continue.
By deconstructing a large project into smaller, more manageable tasks, we can abstract away portions of
the application and focus on separate, solvable problems. The knowledge we gain from developing and
testing these smaller modules can be applied to the development of other parts of the project. Ultimately
we generate correct, working, and maintainable software efficiently and quickly.
You have been hired to generate some helpful software for a group of ichthyologists at the Vancouver
Aquarium. A new and invasive species of Poecilia, the live-bearing aquarium fish also known as the guppy,
has been discovered in some of the hot springs, pools, and streams near Skookumchuk. The owners of
the land have asked the scientists to study the prolific little fish and determine, among other things, if its
reproductive rate will be a threat to native aquatic fauna.
Figure 1: Guppies by Per Harald Olsen (Own work) via Wikimedia Commons [GFDL
(https://www.gnu.org/copyleft/fdl.html) or CC BY 3.0 (https://creativecommons.org/licenses/by/3.0)]
The scientists are still studying the problem domain, and they havenβt finalized the scope of the simulations they want to conduct. But thatβs okay. Object-oriented analysis, design, and programming are well
suited to developing software systems whose requirements are incomplete and changing.
During the first few analysis meetings with the scientists, you identified some fundamental classes in
the problem domain. For the first iteration of your software project, you will abstract away much of the
potentially complicated logic and design in the application. You will begin by writing one of the classes
that represent the basic building blocks of the simulations the scientists will want to run.
1
1 Submission requirements
This assignment must be submitted via GitHub by Sunday January 19th at or before 23:59:59 PM. Tardy
submissions will not be accepted.
Your code must be in the ca.bcit.comp2522.assignments.a1 package of the main COMP 2522 IntelliJ
project you created in Lab 01.
We will mark the code as it appears during the final commit before the due date and time.
2 Grading scheme
Your assignment will be marked out of 10:
1. 5 points for code style (refer to the code style handout)
2. 5 point for code correctness (we will even provide you with some helpful JUnit tests later this week).
3 Implementation requirements
You must implement a class called Guppy.java which contains the following elements:
1. The following nine public symbolic constants. Use these variable names and data types. In Java,
symbolic constants are static and final. Do not use different names or data types, and do not add
any other symbolic constants. Use these constants instead of magic numbers inside your code:
(a) YOUNG_FISH_AGE_IN_WEEKS an integer equal to 10
(b) MATURE_FISH_AGE_IN_WEEKS an integer equal to 30
(c) MAXIMUM_AGE_IN_WEEKS an integer equal to 50
(d) MINIMUM_WATER_VOLUME_ML a double equal to 250.0
(e) DEFAULT_GENUS a String equal to βPoecilia”
(f) DEFAULT_SPECIES a String equal to βreticulata”
(g) DEFAULT_HEALTH_COEFFICIENT a double equal to 0.5
(h) MINIMUM_HEALTH_COEFFICIENT a double equal to 0.0
(i) MAXIMUM_HEALTH_COEFFICIENT a double equal to 1.0
2. The following eight private instance variables. Use these variable names and data types. Do not
use different names or data types, and do not add any other instance variables:
(a) genus (first word of the scientific binomial two-part name) a String
(b) species (second word of the scientific binomial name) a String
(c) ageInWeeks an integer
(d) isFemale a boolean
(e) generationNumber an integer
(f) isAlive a boolean
(g) healthCoefficient a double
(h) identificationNumber an integer
3. numberOfGuppiesBorn, a private static integer that has an initial value of 0. This variable is incremented by 1 each time a new guppy is constructed, and the newly incremented value is assigned
to the new guppyβs identificationNumber instance variable. Further details are provided in the
section about constructors (remember that static variables are shared by all objects of a class).
4. Two (2) constructors:
2
(a) A zero-parameter constructor which sets ageInWeeks and generationNumber to zero, and sets:
i. genus to DEFAULT_GENUS
ii. species to DEFAULT_SPECIES
iii. isFemale to true
iv. isAlive to true
v. healthCoefficient to DEFAULT_HEALTH_COEFFICIENT
vi. identificationNumber to the newly incremented value of numberOfGuppiesBorn
(b) A second constructor which accepts the following parameters in the following order:
i. newGenus a String. Format the String passed in newGenus correctly (capital first letter, the
rest lower case) and assign the new String to the genus instance variable.
ii. newSpecies a String. Format the String passed in newSpecies correctly (lower case) and
assign the new String to the species instance variable.
iii. newAgeInWeeks a positive integer. Assign newAgeInWeeks to the ageInWeeks instance variable. If the parameter passed to the method is negative, assign a 0.
iv. newIsFemale a boolean to assign to the isFemale instance variable.
v. newGenerationNumber an integer. If the argument passed as the parameter is less than
zero, set the value to one. Otherwise assign newGenerationNumber to the generationNumber
instance variable.
vi. newHealthCoefficient a double. If the argument passed as the parameter is less than
MINIMUM_HEALTH_COEFFICIENT or greater than MAXIMUM_HEALTH_COEFFICIENT, set the value
to the closest bound. Otherwise assign newHealthCoefficient to the instance variable
called healthCoefficient.
vii. This constructor should not accept a parameter for the isAlive instance variable. Set the
isAlive variable to true. Every new Guppy is alive by default.
(c) Remember that each constructor must also increment the static numberOfGuppiesBorn
variable by 1 and and assign the new value to identificationNumber.
5. A method with the header public void incrementAge() which increases the value in the ageInWeeks
instance variable field by 1. If the new value in ageInWeeks is greater than MAXIMUM_AGE_IN_WEEKS,
set the isAlive instance variable to false.
6. An accessor (getter) for all eight instance variables, and a mutator (setter) for ageInWeeks, isAlive,
and healthCoefficient:
(a) Do not create mutators for genus, species, isFemale, or identificationNumber.
(b) Every accessor method name must follow the pattern getVariableName.
(c) Every mutator method name must follow the pattern setVariableName.
(d) Also create a static accessor for the numberOfGuppiesBorn static variable. It must be called
public static int getNumberOfGuppiesBorn().
(e) The mutator for ageInWeeks must ignore values below 0 and above MAXIMUM_AGE_IN_WEEKS.
(f) The mutator for healthCoefficient must ignore values that would cause the healthCoefficient
variable to exceed its bounds.
7. A method with the header public double getVolumeNeeded() which returns the volume of water
in millilitres that the guppy needs according to the following formula:
(a) If the fish is less than 10 weeks old, return MINIMUM_WATER_VOLUME_ML.
(b) If the fish is 10 to 30 weeks old, return MINIMUM_WATER_VOLUME_ML * ageInWeeks / YOUNG_FISH_WEEKS.
(c) If the fish is 31 to 50 weeks old, return MINIMUM_WATER_VOLUME_ML * 1.5.
(d) If the fish is older than 50 weeks, return 0.0.
3
8. a method with the header public void changeHealthCoefficient(double delta) which adds
the value passed in the delta parameter to the healthCoefficient instance variable. The parameter delta may be positive or negative, but if the new value of healthCoefficient would be less than
or equal to MINIMUM_HEALTH_COEFFICIENT, in addition to setting the value of healthCoefficient
to 0.0, set the isAlive instance variable to false. If the new value of healthCoefficient would be
greater than MAXIMUM_HEALTH_COEFFICIENT, set healthCoefficient to MAXIMUM_HEALTH_COEFFICIENT.
9. a method with the header public String toString() which returns a String representation of the
guppy. The version generated by IntelliJ is sufficient.
10. an equals() method. Write this one yourself from scratch.
Thatβs it! Good luck, and have fun!
COMP 2522 Object oriented programming 1 Assignment 2
Introduction Hooray! It took a few late nights, but you met your first milestone. You successfully demonstrated the first iteration of the simulation software to the ichthyologists and they are eager for you to finish. The scientists have returned from their final fact-hunting expedition. They have given you the data you need to finish the program. And itβs a rush job β they need a finished simulation in about two weeks! Itβs time to complete your contract and give the whole project a little bit of polish. You are ready to begin the next iteration. Did you have any idea that youβd be writing full-fledged Java programs after only a few weeks? For assignment 2, you will implement a class that represents a hot spring Pool and βmanagesβ a collection of resident Guppy objects. You will make minor additions to the Guppy class, and create one Ecosystem class to rule them all. Letβs begin! Figure 1: Guppy Simulation class diagram (Proposed design) 1 Submission requirements This assignment must be completed by Wednesday February 12th 2020 at or before 23:59:59. Your code must all be in the ca.bcit.comp2522.assignments.a2 package of your COMP 2522 IntelliJ project. We will mark the code as it appears during the final commit before the due date and time. 1 2 Grading scheme Your assignment will be marked out of 10: 1. 2 points for code style 2. 2 point the modifications to the Guppy class 3. 3 points for the Pool class 4. 3 points for the Ecosystem class 3 Implementation requirements Implement the following: 1. Copy the Guppy.java and GuppyTest.java source files to the a2 package. 2. Itβs time to implement reproduction for the Guppies! You must implement a method called public ArrayList spawn() in the Guppy.java class. Hereβs how it must work: (a) Only female Guppies can have babies (b) Female Guppies must be 8 weeks of age or older to spawn (c) Each week, each female Guppy who is 8 weeks old or older has a 50 percent chance of spawning (hint: use Random) (d) A spawning female has between 0 and 100 fry (Note: Guppies donβt lay eggs, they have live babies called fry! Really!) (e) Each fry is the same genus and species as its mother (f) Each fry has a 50 percent chance of being female (g) A new fry has a health coefficient equal to (1.0 + motherβs health quotient) / 2.0 (h) The generation number of a new fry is 1 + the motherβs generation number. Hereβs a good way to approach this. The method should begin by checking if the Guppy is female. If not, or if the Guppy is 0 – 7 weeks old, return null immediately. Otherwise create a local ArrayList called babyGuppies. Use a Random object to generate a double between 0 and 1.0. If the random number is equal to or less than 0.50, the female will spawn some babies. If the female is going to have babies, use the Random object to generate a random int between 0 and 100 inclusive for the female. Use a loop statement to create this number of Guppies (0 – 100) with the correct attributes, and add them to the babyGuppies ArrayList. After the loop is done, return the newly created ArrayList. Thatβs all. 3. You must create a class called Pool.java which contains the following elements: (a) The following public symbolic constants. Use these variable names and data types. Remember symbolic constants are static and final. Do not use different names or data types. Use these constants instead of magic numbers inside your code: i. DEFAULT_POOL_NAME a String equal to βUnnamed” ii. DEFAULT_POOL_TEMP_CELSIUS a double equal to 40.0 iii. MINIMUM_POOL_TEMP_CELSIUS a double equal to 0.0 iv. MAXIMUM_POOL_TEMP_CELSIUS a double equal to 100.0 v. NEUTRAL_PH a double equal to 7.0 vi. DEFAULT_NUTRIENT_COEFFICIENT a double equal to 0.50 vii. MINIMUM_NUTRIENT_COEFFICIENT a double equal to 0.0 viii. MAXIMUM_NUTRIENT_COEFFICIENT a double equal to 1.0. (b) The following 8 private instance variables. Use these variable names and data types. Do not use different names or data types, and do not add any other instance variables: 2 i. name a String ii. volumeLitres a double iii. temperatureCelsius a double iv. pH a double that will always be between 0 and 14.0 inclusive v. nutrientCoefficient, a double that will always be between 0 and 1.0 inclusive vi. identificationNumber an int vii. guppiesInPool an ArrayList of Guppy viii. randomNumberGenerator a Random object. (c) numberOf Pools a private static variable with an initial value of 0 that is incremented by 1 each time a new Pool is constructed. Further details are provided in the section about constructors (remember that static variables are shared by all objects of a class). (d) Two (2) constructors: i. Zero-parameter constructor which sets volumeLitres to 0.0, and also sets: A. name is set to DEFAULT_POOL_NAME B. temperatureCelsius is set to DEFAULT_POOL_TEMP_CELSIUS C. pH is set to NEUTRAL_PH D. nutrientCoefficient is set to DEFAULT_NUTRIENT_COEFFICIENT E. guppiesInPool is initialized to be an empty ArrayList of Guppy, and randomNumberGenerator is initialized to be a new Random (use the zero-parameter constructor in the Random class). ii. Multi-Parameter constructor which accepts a parameter for each instance variable (except the identification number, the ArrayList of guppies, and the random number generator) and validates it: A. The parameter passed for the name field must not be null, and must not be an empty String or a String composed only of whitespace. If the argument passed as the parameter is either null or an empty/whitespace String, throw an IllegalArgumentException. Otherwise, format the value correctly (remove the whitespace, capitalize the first letter, set the rest to lower case) before storing it in the instance variable. B. The parameter passed for the volumeLitres field must be positive, otherwise set the instance variable to 0.0. C. The parameter passed for the temperatureCelsius instance variable must be greater than or equal to MINIMUM_POOL_TEMP_CELSIUS and less than or equal to MAXIMUM_POOL_TEMP_CELSIUS, else the instance variable must be set to the default temperature. D. the parameter passed for the pH field must be within the range [0, 14.0], otherwise set the instance variable to NEUTRAL_PH. E. the parameter passed for the nutrientCoefficient must be within the range [0, 1.0] (between 0 and 1.0 inclusive), otherwise set the instance variable to DEFAULT_NUTRIENT_COEFFICIENT. F. guppiesInPool is initialized to be a new ArrayList of Guppy. G. randomNumberGenerator is initialized to be a new Random (use the zero-parameter constructor in the Random class). iii. Both constructors must create and assign a unique numerical identification number to the identificationNumber instance variable. A good way to do this is to increment the static counter field from inside the constructor, and then assign that newly incremented value to the new Pool objectβs identification field. (e) Implement accessors (getters) and mutators (setters). Create accessors for name, volumeLitres, temperatureCelsius, pH, nutrientCoefficient, and identificationNumber. We do not want to create an accessor for the ArrayList of Guppy because we want to keep that collection encapsulated and protected from tampering. Create mutators for volumeLitres. temperatureCelsius, pH, nutrientCoefficient. All mutators should ignore values that are unacceptable. For example. the pH 3 mutator must ignore input that does not fall within the range [0, 14], and the nutrientCoefficient mutator should ignore values that do not fall within the range [0, 1.0]. Instance variables which do not have mutators should be final. (f) a method with the header public void changeNutrientCoefficient(double delta) which adds the value passed in the delta parameter to the Poolβs nutrient coefficient. The parameter may be positive or negative, but if the new value of the nutrient coefficient would be less than the minimum, set the nutrient coefficient to the minimum. If the new value would be greater than the maximum, set the new value to the maximum. (g) a method with the header public void changeTemperature(double delta) which changes the temperature by the amount passed in the delta parameter. If the new Pool temperature would be less than the minimum, set the pool temperature to the minimum. If the new Pool temperature would be greater than the maximum, set it to the maximum. (h) a method with the header public static int getNumberCreated() which returns the total number of Pools created. (i) a method with the header public boolean addGuppy(Guppy guppy) which adds a Guppy to the Pool. If the parameter equals null, the method should return false. If the parameter is not equal to null, add the Guppy to the ArrayList of Guppy. If the Guppy is successfully added, return true, else return false. (j) a method with the header public int getPopulation() which returns the number of living Guppies in the pool. (k) a method with the header public int applyNutrientCoefficient() that calculates which Guppies in the Pool have died of malnutrition this week, and returns the number of deaths. Iterate over the ArrayList of Guppy in the Pool using the iterator returned by the ArrayListβs iterator( ) method. For each Guppy, generate a different random number between 0.0 and 1.0 inclusive using the Random method nextDouble(). If this randomly generated number is greater than the Poolβs nutrient coefficient, kill that Guppy by setting the appropriate boolean field in the Guppy. Note that this method does not remove any dead Guppies from the Pool, it just kills them. Do not do anything else. (l) a method with the header public int removeDeadGuppies() which uses an ArrayList iterator and a loop to visit each Guppy in the collection and remove the Guppies that are not alive. This method must keep track of how many Guppies are removed. Return the number of Guppies that have been removed. (m) a method with the header public double getGuppyVolumeRequirementInLitres() which returns the total number of litres required by all of the living Guppies in the Pool. Note that the Guppy class has a helpful method with returns the total number of milliLitres required per Guppy. You will need to convert this to litres (note that 1,000 mL = 1 L). (n) a method with the header public double getAverageAgeInWeeks() which returns the average age of the living Guppies in the Pool. (o) a method with the header public double getAverageHealthCoefficient() which returns the average health coefficient of the living Guppies in the Pool. (p) a method with the header public double getFemalePercentage() which returns a double representing the percentage of living Guppies in the Pool that are female. (q) a method with the header public double getMedianAge() which returns median age of the living Guppies in the Pool. (r) a method with the header public String toString() which returns a String representation of the Pool. The version generated by IntelliJ is sufficient. (s) Implement a method called public int spawn(). This method must Iterate over the Guppies in the Pool and invoke the spawn method on each one. Each Guppy will return null (if it is male, or too young to reproduce) or an ArrayList of Guppies containing the new fry. Add the new baby Guppies to the Poolβs collection of Guppies. You can add each motherβs new fry to the Pool like this: guppiesInPool.addAll(newBabies);. Count the total number of new Guppies that have been born and added to the Pool, and return the total. 4 (t) Implement a method called public int incrementAges(). This method increments the ages of every Guppy in the Pool and returns the number that have died of old age. Do not remove the dead Guppies from the collection, just kill them. The dead Guppies still need to be removed from the system. (u) Implement a method called public int adjustForCrowding( ). This Pool method extinguishes the Guppies that have suffocated due to overcrowding. If the total volume needed by a collection of Guppies exceeds the volume of their Pool, then some of the Guppies must be extinguished. If the total volume required by the Guppies in the Pool exceeds the total volume of the Pool, then we must loop through the Guppies in the Pool and terminate the weakest, e.g., the Guppy with the lowest health coefficient. We must do this again and again, and keep terminating the weakest Guppy in the Pool until the total volume requirement of the group of Guppies that remain alive is equal to or less than than the volume of the Pool. Note that the crowded out Guppies are only terminated. They are not removed from the Pool. Return the total number of Guppies that have died due to crowding. 4. Implement a new class called Ecosystem. Ecosystem contains and drives the simulation and requires the following elements: (a) One private instance variable, private ArrayList pools. Do not add any other instance variables. (b) Zero-parameter constructor that must initialize the ArrayList of Pools. Do not create a oneparameter constructor. (c) Implement a method called public void addPool(Pool newPool) which adds the Pool passed as a parameter to the Collection of Pools. Ignore null parameters, e.g., do not add any nulls to the Pool collection. (d) Implement a method called public void reset( ) that resets the Ecosystem by emptying the ArrayList of Pool (hint: check out the clear() method available to the ArrayList). (e) Implement a method called public int getGuppyPopulation( ) which returns the total number of living Guppies in the Ecosystem. Hint: use a for each loop to invoke the getPopulation() method on each pool and add the result to a local accumulator variable. (f) Implement a method called public int adjustForCrowding( ) (same name as the Pool method!) which iterates over all the Pools, invokes each Poolβs adjustForCrowding( ) method, and adds the number of Guppies that have been crowded out to a running total. Once all the Pools have been βde-crowded,β adjustForCrowding should return the total number of Guppies that have been squeezed out of the entire ecosystem. (g) Implement a method called public void setupSimulation( ). This method should create the following Ecosystem: i. The first pool is called Skookumchuk. It has a total volume of 3,000 litres. Its temperature is 42 degrees Celsius and its pH is 7.9. The nutrient coefficient is 0.9. ii. The second pool is called Squamish. It has a total volume of 15,000 litres. Its temperature is 39 degrees Celsius and its pH is 7.7. The nutrient coefficient is 0.85. iii. The third and final pool is called Semiahmoo. It has a total volume of 4,500 litres. Its temperature is 37 degrees Celsius and its pH is 7.5. The nutrient coefficient is 1.0. iv. There are 300 Guppies in Skookumchuk Pool. They are all Poecilia reticulata. Use your Random object and the multi- parameter Guppy constructor to create each new Guppy with an age between 10 and 25 weeks inclusive and a health coefficient between 0.5 and 0.8 inclusive. Each original Guppy has a 75 percent chance of being female. v. There are 100 Guppies in Squamish Pool. They are all Poecilia reticulata. Use your Random object and the multi-parameter Guppy constructor to create each new Guppy with an age be- tween 10 and 15 weeks inclusive and a health coefficient between 0.8 and 1.0 inclusive. Each original Guppy has a 75 percent chance of being female. vi. There are 200 Guppies in Semiahmoo Pool. They are all Poecilia reticulata. Use your Random object and the non-default Guppy constructor to create each new Guppy with an age between 15 and 49 weeks inclusive and a health coefficient between 0.0 and 1.0 inclusive. Each original Guppy has a 35 percent chance of being female. 5 (h) Implement a method called public void simulate(int numberOf Weeks). It uses a loop to invoke the next method, simulateOneWeek, the specified number of times. (i) Implement a method called public void simulateOneWeek( ) that runs the simulation for one week. This method must: i. Invoke incrementAges( ) on each Pool and add the return value to a local int called diedOfOldAge. ii. Invoke removeDeadGuppies( ) on each Pool and add the return value to a local int called numberRemoved. iii. Invoke applyNutrientCoefficient( ) on each Pool and add the return value to a local int called starvedToDeath. iv. Invoke removeDeadGuppies( ) again (second time) on each Pool and add the return value to the local int called numberRemoved. v. Invoke spawn( ) on each Pool and add the return value to a local int called newFry. vi. Invoke adjustForCrowding( ) and assign the return value to a local int called crowdedOut. vii. Invoke removeDeadGuppies( ) again (third time!) and add the return value to numberRemoved. viii. Check that diedOfOldAge + starvedToDeath + crowdedOut == numberRemoved. If they are not equal, you have a logic error. ix. Print in a lovely and easy-to-read format: A. Week number B. Number of deaths this week due to old age C. Number of deaths this week due to starvation D. Number of deaths this week due to overcrowding E. Number of births (new fry) this week F. List of pools and their current populations at the end of the week G. Total population of the Ecosystem at the end of the week 5. Create a Driver class. The Driver class should contain a main method, nothing else. Inside the main method, instantiate an Ecosystem object. Ask the user how many weeks they would like to run the simulation, and then invoke the Ecosystem objectβs public void simulate(int numberOf Weeks) method passing in the userβs choice. 6. A bonus of up to 10% is available for submissions that do one more thing. Modify the setupSimulation method in Ecosystem. Modify the Pool temperatures, pHs, and nutritional coefficients so that the populations in the Pool eventually reach a thriving balance point. I define a thriving balance point as a combination of environmental factors that result in a steady population where the number of deaths equals the number of births and the median health coefficient of the Guppies in the Ecosystem is greater that 0.6. Thatβs it! Good luck, and have fun!
COMP 2522 Object oriented programming 1 Assignment 3
Introduction For assignment 3, we will examine abstraction and inheritance through the lens of mathematics. In 1924, a Polish mathematician named Jan Εukasiewicz invented something called Polish notation. It was refined in the early 1960s by Edsger Dijkstra who developed Reverse Polish Notation to take advantage of the Stack data structure. Weβve already talked about binary infix operators in COMP 1510 and COMP 2522. Binary infix operators are called binary because they require two operands. They are called infix because the operator is placed between the operands: 2 + 2 = 4 4 – 2 = 2 5 / 3 = 1 (assuming we are using ints) Reverse Polish notation (RPN) is a mathematical notation where operators follow their operands. Instead of using a binary infix operator, RPN uses a binary postfix operator: 2 2 + = 4 4 2 – = 2 5 3 / = 1 Consider our usual notation. When we mix our operations using binary infix operators, we must implement rules of precedence. The use of parentheses can result in dramatically different results: 2 – 3 * 4 = -10 (2 – 3) * 4 = -4 RPN (postfix) notation removes the need for parentheses! RPNβs greatest advantage is clear when we consider expressions that contain more than one operand. If we want an operation to take precedence, we just put the operator immediately to the right of the two operands: 2 3 4 * – = -10 2 3 – 4 * = -4 RPN doesnβt just eliminate parentheses and keystrokes. Itβs flexible. If we use a Stack by pushing operands onto the Stack until we reached an operator, we can pop operands off the Stack, transform them with the operator, and push the result back onto the Stack as we go, letting us calculate complex partial results without having to save them in multiple locations. Thatβs exactly what weβre going to do. Weβll use a bit of inheritance and abstraction to create a hierarchy of operations. Then weβll build an RPNCalculator class that contains just a few methods. 1 Ordinarily for a project like this, we would use as many existing classes as possible. Why re-invent the wheel, right? Ordinarily we would use the java.util.Stack class to implement our Stack. Ordinarily. But not for a3. For a3, in order to gain some more experience with data structures and exceptions, you will do something extraordinary. You will implement your own Stack. Youβve already created an implementation during an exam, a seriously stressful situation, so this will surely be a cinch. Fun will be had by all. Read on, and get started now! 1 Submission requirements This assignment must be completed by Sunday March the 8th at or before 23:59:59. Your code must all be in the ca.bcit.comp2522.assignments.a3 package of your COMP 2522 IntelliJ project. We will mark the code as it appears during the final commit before the due date and time. 2 Grading scheme Your assignment will be marked out of 10. A rubric will be made available on the Learning Hub to help guide your priorities. 3 Implementation requirements Implement the following: 1. We are going to build a Reverse Polish Notation calculator. The RPNCalculator will accepts two command line arguments. The first command line argument must be an int which represents the initial size of the Stack we will use to store operands. The second command line argument must be a String in double quotes that contains a valid Reverse Polish Notation expression. We will place the main method inside a class called RPNCalculator, and we will invoke the program like this: java RPNCalculator 10 “1 2 3 4 5 6 7 8 9 10 + + + + + + + + +” The program must respond by printing the expression inside a set of square brackets followed by the result: [1 2 3 4 5 6 7 8 9 10 + + + + + + + + +] = 55 2. Start by defining an interface called Operation. An Operation is a function that has a symbol. It requires two operands. The Operation interface contains two public methods, char getSymbol(), which returns the operation symbol to the user, and int perform(int operandA, int operandB), which is a blueprint for performing a math operation. Thatβs all we need to put inside the Operation. 3. Note that the Operation doesnβt contain any instance variables because itβs an interface. An interface just tells us how to interact with something. It makes no demands about implementation. Since itβs an interface, it doesnβt have a constructor either. We need to create a level of abstraction, another layer of the onion if you will, that adds the instance variable that will store the actual symbol being used. 4. Define an abstract class called AbstractOperation that implements Operation. Ensure AbstractOperation is an abstract class. It must contain a single protected instance variable called operationType, a char. Add a constructor which accepts a char and assigns it to operationType. Add an 2 accessor that returns the char. The accessor must be called getSymbol() and it must be final. We donβt want any subclasses overriding it. 5. We are going to confine our exploration to the basic operations: +, -, *, and /. Create four classes that extend AbstractOperation. They must be called AdditionOperation, SubtractionOperation, MultiplicationOperation, and DivisionOperation. Each of these classes must contain a static constant char called ADDITION_CODE or SUBTRACTION_CODE or MULTIPLICATION_CODE or DIVISION_CODE, each of which is assigned the value β+β, β-β, β*β, or β/β (I will let you decide which goes with which!). The constructor must pass the constant to the superclass constructor. Ensure each class provides a concrete implementation of perform too. Thatβs all you need inside each class. 6. The hierarchy we just created is really quite elegant. We defined the concept of an Operation, and we can interact with an Operation by getting its symbol and passing it two operands to operate on. We further added an abstract implementation of Operation which added an instance variable, a constructor to assign it, and an accessor to acquire it. Then we created four concrete implementations of Operation. Each one is many things at once. For example, an AdditionOperation is-an AbstractOperation and it is-an Operation, too. 7. So we have this wonderful little inheritance hierarchy. Itβs easy to add more operations. In our project, any operation that can be represented as a single char can be added. Do I smell a bonus? Perhaps… 8. Iβd like you to implement the Stack next. We will implement a fixed size, non-resizable Stack using an olde fashioned array of int: (a) The Stack must have two instance variables, an array of int called stackValues, and an int called count. (b) The Stack constructor must accept an integer representing the size of the array to create. If the size is less than one, throw an IllegalArgumentException. (c) Add a capacity( ) method that returns the size of the Stack. (d) Add a size( ) method that returns the number of elements in the Stack, i.e., the count. (e) Add a method called unused( ) which returns the amount of space left in the Stack. (f) Add a method called push(int value) which accepts an integer called value. This method pushes the value onto the Stack. If the Stack is already full, this method must throw a checked exception called StackOverflowException. You must create this exception. Pass the message “This stack is full!” to the StackOverflowException constructor. If, however, there is room, push the value onto the Stack. The next call to pop will remove this item( ) and return it. The next call to peek( ) will return a reference to this item without removing it. (g) Did someone say pop? Add a method called pop( ) which accepts no parameters and returns an int. If someone tries to pop a value from an empty Stack, ensure this method throws a checked exception called StackUnderflowException. You will have to add this exception to your project too. Pass the message “Cannot call Stack.pop() on an empty stack!” to the StackUnderflowException exception. (h) Finally, add a method called peek( ) which does NOT remove anything from the Stack, but does return the value on top of it. If the Stack is empty, throw a new StackUnderflowException and pass the message “Cannot call Stack.peek() on an empty stack!”. 9. The pieces are in place. We just need to create the main class. Implement a class called RPNCalculator. Itβs time for the fun stuff! (a) RPNCalculator contains a single integer constant called MIN_STACK_SIZE which stores two. The smallest RPN calculation is two operands followed by a single operation. (b) RPNCalculator contains a single instance variable of type Stack called (wait for it) stack. (c) The constructor must accept an integer called stackSize. If this integer is less than MIN_STACK_SIZE the constructor must throw an IllegalArgumentException. Otherwise instantiate a Stack of that size and assign the address of this new object to the instance variable. 3 (d) Implement a method called public int processFormula(final String formula): i. This method must throw an IllegalArgumentException if formula is equal to null or if it is a string of length zero. ii. Otherwise, instantiate a Scanner object, passing the formula to its constructor. We will parse the formula using an instance of the Scanner class. iii. Hereβs the algorithm Iβd like you to use. While the Scanner objectβs hasNext( ) method returns true, check if the hasNextInt( ) method returns true, too. If it does, we know the next token in the formula String is an operand that we can push onto the Stack. If this is the case, go ahead and push that int onto the stack with a helper method called void push(final int operand). iv. The method void push(final int operand) must check that unused( ) does not return zero. If it does, we must throw a StackOverflowException with an appropriate message. Otherwise, push the operand onto the Stack. v. Otherwise, it must be an operation. If itβs an operation, we must scan the operation and use it to instantiate the correct Operation descendant. Do this in a helper method called private Operation getOperation(final char symbol). vi. Inside private Operation getOperation(final char symbol), use a switch statement to evaluate symbol. If itβs β+β, return a new AdditionOperation object, if itβs β-β, return a new SubtractionOperation, etc. vii. The return value must be assigned to a variable inside the processFormula method whose data type is Operation. This makes our code flexible. Now we can use polymorphism! We can create and use any kind of Operation we like. We can define new and novel operations. All we have to do is add a line inside the switch statement in the getOperation method. viii. In the switch statement, the default case must throw a checked exception called InvalidOperationTypeException (you will need to create this). Pass the errant operation to the InvalidOperationTypeException constructor. ix. Otherwise, processFormula must pass the instance of Operation created in the helper method to a method called perform( ), and then invoke a public method called getResult( ). (e) public int getResult() must use the peek( ) method in the Stack to retrieve the current value in the Stack, i.e., the result. If the size of the Stack is zero throw a new StackUnderflowException and pass the message, “There are no operands!”. (f) Finally. The moment youβve been waiting for. The one method that rules them all. private void perform(final Operation operation) will accept the Operation object instantiated by processFormula and its helpers. Check that operation is not null. If it is, throw an IllegalArgumentException using the message, “Operation cannot be null!”. Otherwise, pop the top two operands and pass them to the Operationβs perform method. Use the push( ) method to push the result onto the Stack. 10. In order to give you a little start, Iβve included the main method which you must insert into your RPNCalculator class. You may not change this. Not a single thing: /** * Drives the program by evaluating the RPN calculation provided as * a command line argument. * * Example usage: RPNCalculator 10 “1 2 +” * * Note that the formula MUST be placed inside of double quotes. * * @param argv – the command line arguments are the size of the Stack * to be created followed by the expression to evaluate. */ public static void main(final String[] argv) { // Checks for correct number of command line arguments. 4 if (argv.length != 2) { System.err.println(“Usage: Main “); System.exit(1); } // Initializes stack and RPNCalculator. final int stackSize = Integer.parseInt(argv[0]); final RPNCalculator calculator = new RPNCalculator(stackSize); try { System.out.println(“[” + argv[1] + “] = ” + calculator.processFormula(argv[1])); } catch (final InvalidOperationTypeException ex) { System.err.println(“formula can only contain integers, +, -, *, and /”); } catch (final StackOverflowException ex) { System.err.println(“too many operands in the formula, increase the stack size”); } catch (final StackUnderflowException ex) { System.err.println(“too few operands in the formula”); } } Thatβs it! Iβll furnish some unit tests later that include some formulas your RPNCalculator must be able to evaluate, and which ensure your methods are throwing the right checked Exceptions, etc. Good luck, and have fun!
COMP 2522 Object oriented programming 1 Assignment 4
Introduction For your fourth assignment, you will apply what you have learned about sets in COMP 1510, and arrays, ArrayLists, generics, and inner classes in COMP 2522. You will implement a novel and simple data structure that we will call the ArraySet. There is no ArraySet in the Java Collections Framework, but you have all the tools and knowledge you need to write one. The ArraySet is a parameterized data structure just like the ArrayList β it uses an array βunder the hoodβ to implement a data structure (in this case, a set instead of a list). Recall that a set is a collection of items without duplicates and in no particular order (i.e., unordered). A user may add items to a set, remove items from a set, and check whether an item is in a particular set. Occasionally, the elements in the set must be accessed one at a time; for instance, this is necessary to list the set elements to the standard output. A SetIterator object supports accessing each element of a set, one at a time. In Java, a set cannot contain nulls, either. 1 Submission requirements This assignment must be completed byββββββββββ Friday March the 27th Sunday March the 29th at or before 23:59:59. Your code must all be in the ca.bcit.comp2522.assignments.a4 package of your COMP 2522 IntelliJ project. We will mark the code as it appears during the final commit before the due date and time. 2 Grading scheme This assignment will be marked out of 10: 1. 3 points for code style: identifiers, indentation, minimization of mutability, etc. 2. 7 points for passing our JUnit tests. 3 Implementation requirements 1. Download the βstarter packβ and copy its contents to the a4 package in your COMP 2522 assignments project. 2. The ArraySet class already contains some helpful instance variables and a constant. Read the method comments to better understand what each method is supposed to do. You must implement the following methods inside the ArraySet class: 1 (a) the ArraySet constructor (b) public boolean add(E newItem) (c) public boolean contains(E item) (d) public boolean remove(E item) (e) public Object[] toArray() (f) public void clear() (g) public int size() (h) public SetIterator iterator() (i) private void resize() 3. The ArraySet must contain an inner class called SetIterator. The SetIterator class is used internally by the ArraySet class to iterate over its elements. There are two methods to implement: (a) public boolean hasNext() (b) public E next() 4. Some helpful hints: (a) An implementation of a set can be realized in more than one way. For your assignment, you must implement the set by using an array whose elements are of type E. (b) Your implementation of the ArraySet class must support initially holding 10 items, i.e., it must have a capacity of 10. If there is an attempt to add one more item than the current size allows, i.e., the current size has increased to be equal to the capacity, the size of the set (its capacity) must double. The recommended way to do this is to create a new array that is twice the size of the current array and add all of the data from the current array to the new array. (c) When an item is added to the ArraySet, you should add the item to the end of the array; do not try to keep track of null entries in the underlying array. When an item is removed from the ArraySet, another item in the ArraySet must be moved into the recently vacated slot (you should determine which item to move). Remember this is okay because the ArraySet doesnβt maintain sequence. In the case of removal, your implementation must not move more than one element to remain efficient. (d) You must strictly adhere to the specification in this document and in the ArraySet comments. (e) To allow the the items of an ArraySet to be accessed, a SetIterator is used. A SetIterator for an instance of an ArraySet called s is an object that can access the items of s. During iteration, the SetIterator βrefers toβ an item in s (though how it refers to the element is your implementation decision). The SetIterator allows the user to access the ArraySet item that the SetIterator points to, advance the SetIterator to the next item in the ArraySet, and check whether there is another item in the ArraySet that have not yet been accessed by the SetIterator. (f) The only object that knows how to set up a SetIterator for an ArraySet is the ArraySet itself. For this reason, the ArraySet contains a single method called iterator() that returns a SetIterator for that set. Therefore, the ArraySet you implement has to include its own SetIterator that can be returned when its method iterator() is called. The following code snippet illustrates how a client can use a SetIterator to print out the people that are stored in a fictitious ArraySet of Person: ArraySet people = new ArraySet<>(); SetIterator it = people.iterator(); while( it.hasNext() ) { Person nextPerson = it.next(); System.out.println( nextPerson.toString() ); } (g) An easy way to define a SetIterator for an ArraySet is to define a class inside the ArraySet class named SetIterator. Such a class is called an inner class, of course (remember a nested class is static. Donβt use a nested class). That is what our ArraySet will do. There are several advantages to implementing the SetIterator as an inner class of the ArraySet class: 2 i. The client of an ArraySet wonβt know that it exists. This is a good thing. All the client needs to know is that when they call the iterator() method they get back something (they donβt care exactly what) that iterates over the ArraySetβs elements. ii. The SetIterator has direct access to the private members of the enclosing class. In other words, your SetIterator can access the private members of your ArraySet class. This is really useful as the SetIterator will need to extract data from the ArraySet. iii. Your SetIterator will have a single data member that stores the index of the next item to be returned by the iterator. Thatβs it! Good luck, and have fun!