Question: State Refactoring The VendingMachine.java source file contains a class that simulates a simple vending machine that dispenses products such as soda or candy. All of

State Refactoring

The VendingMachine.java source file contains a class that simulates a simple vending machine that dispenses products such as soda or candy. All of the products in this vending machine cost exactly $1.00. The behavior of the vending machine's various methods to insert coins, dispense products, or refund coins that have been inserted depends on the amount of money that has been inserted.

You will refactor the VendingMachine class to use the State pattern. You will first need to analyze the code to determine what the possible states are, and what events trigger transitions from one state to another. You should capture this behavior as a finite state machine diagram (or UML statechart, if you prefer) before you attempt to refactor the code into the new design.

Testing Your Refactored Design

You have been provided with a JUnit 4 unit test that tests the various vending machine methods in different states and verifies the expected output from the machine. Feel free to examine this test class if you'd like, but don't worry if you don't understand what it does or how it does it. You will need to run this test after you have completed your refactored design to verify that the vending machine's observable behavior has not changed.

If you are using IntelliJ, support for JUnit 4 is integrated into the development environment. The JUnit test file will not compile by default in a new project. In IntelliJ, you will need to open the test file in the editor and click to place your cursor on one of the @Test attributes. Then use alt-enter to add JUnit 4 as a dependency to your project. If you are using an IDE other than IntelliJ, you are on your own to figure out how to get JUnit 4 to work with your IDE.

 import java.util.ArrayList; import java.util.List; public class VendingMachine { public enum Coin { NICKEL (0.05), DIME (0.10), QUARTER (0.25), FIFTY_CENT_PIECE (0.5), DOLLAR (1.00); private double amount; Coin(double amount) { this.amount = amount; } public double amount() { return amount; } } private List coins; private boolean refunding; private boolean dispensable; private final long refundDelay; public VendingMachine() { this(500); } VendingMachine(long refundDelay) { coins = new ArrayList<>(); refunding = false; dispensable = false; this.refundDelay = refundDelay; } public synchronized void insertCoin(Coin coin) { double amountAlreadyInserted = calculateMoneyInserted(); if(dispensable) { System.out.println("Can't insert more coins."); } else if(amountAlreadyInserted + coin.amount() > 1.00) { System.out.println("Exact change only."); } else if(refunding) { System.out.println("Can't insert coins while refunding in progress."); } else { coins.add(coin); if(calculateMoneyInserted() == 1.00) { dispensable = true; } } } public synchronized void refund() { if(refunding) { System.out.println("Stop mashing the button!"); } else { refunding = true; dispensable = false; for(Coin coin : coins) { System.out.println("A " + coin.toString() + " pops out. *clink*"); // realistic simulation!  try { wait(refundDelay); } catch (InterruptedException e) { // ignore  } } coins.clear(); refunding = false; } } public synchronized void dispenseProduct() { if(refunding) { System.out.println("Can't purchase while refunding!"); } else if(dispensable) { System.out.println("Dispensing delicious goodies!"); coins.clear(); dispensable = false; } else { System.out.println("Insufficient funds!"); } } private double calculateMoneyInserted() { double amount = 0.0; for(Coin coin : coins) { amount += coin.amount(); } return amount; } } 

_______________Test______________

import org.junit.Before; import org.junit.Test; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; public class VendingMachineTest { private static final long REFUND_DELAY = 100; private static final String NL = System.getProperty("line.separator"); private VendingMachine underTest; private ByteArrayOutputStream bout; private PrintStream out; @Before public void runBeforeTests() { underTest = new VendingMachine(REFUND_DELAY); bout = new ByteArrayOutputStream(); out = new PrintStream(bout); System.setOut(out); } @Test public void insertCoinAfterDispensable() { underTest.insertCoin(VendingMachine.Coin.DOLLAR); underTest.insertCoin(VendingMachine.Coin.NICKEL); assertExpectedOutput("Can't insert more coins."); } @Test public void insertCoinOverOneDollar() { underTest.insertCoin(VendingMachine.Coin.FIFTY_CENT_PIECE); underTest.insertCoin(VendingMachine.Coin.QUARTER); underTest.insertCoin(VendingMachine.Coin.FIFTY_CENT_PIECE); assertExpectedOutput("Exact change only."); } @Test public void refund() { underTest.insertCoin(VendingMachine.Coin.FIFTY_CENT_PIECE); underTest.insertCoin(VendingMachine.Coin.QUARTER); underTest.insertCoin(VendingMachine.Coin.DIME); underTest.insertCoin(VendingMachine.Coin.NICKEL); underTest.refund(); String expected = "A FIFTY_CENT_PIECE pops out. *clink*" + NL + "A QUARTER pops out. *clink*" + NL + "A DIME pops out. *clink*" + NL + "A NICKEL pops out. *clink*"; assertExpectedOutput(expected); } @Test public void refundMashButton() throws InterruptedException { underTest.insertCoin(VendingMachine.Coin.FIFTY_CENT_PIECE); underTest.insertCoin(VendingMachine.Coin.QUARTER); Thread refund = new Thread( () -> { underTest.refund(); }); refund.start(); underTest.refund(); refund.join(); String expected = "A FIFTY_CENT_PIECE pops out. *clink*" + NL + "Stop mashing the button!" + NL + "A QUARTER pops out. *clink*"; assertExpectedOutput(expected); } @Test public void insertCoinRefunding() throws InterruptedException { underTest.insertCoin(VendingMachine.Coin.QUARTER); underTest.insertCoin(VendingMachine.Coin.DIME); underTest.insertCoin(VendingMachine.Coin.NICKEL); Thread insert = new Thread( () -> { underTest.refund(); }); insert.start(); Thread.sleep(REFUND_DELAY/2); underTest.insertCoin(VendingMachine.Coin.FIFTY_CENT_PIECE); insert.join(); String expected = "A QUARTER pops out. *clink*" + NL + "Can't insert coins while refunding in progress." + NL + "A DIME pops out. *clink*" + NL + "A NICKEL pops out. *clink*"; assertExpectedOutput(expected); } @Test public void dispenseProductExactChange() { underTest.insertCoin(VendingMachine.Coin.FIFTY_CENT_PIECE); underTest.insertCoin(VendingMachine.Coin.QUARTER); underTest.insertCoin(VendingMachine.Coin.DIME); underTest.insertCoin(VendingMachine.Coin.DIME); underTest.insertCoin(VendingMachine.Coin.NICKEL); underTest.dispenseProduct(); assertExpectedOutput("Dispensing delicious goodies!"); } @Test public void dispenseProductRefunding() throws InterruptedException { underTest.insertCoin(VendingMachine.Coin.FIFTY_CENT_PIECE); underTest.insertCoin(VendingMachine.Coin.FIFTY_CENT_PIECE); Thread insert = new Thread( () -> { underTest.refund(); }); insert.start(); Thread.sleep(REFUND_DELAY/2); underTest.dispenseProduct(); insert.join(); String expected = "A FIFTY_CENT_PIECE pops out. *clink*" + NL + "Can't purchase while refunding!" + NL + "A FIFTY_CENT_PIECE pops out. *clink*"; assertExpectedOutput(expected); } @Test public void dispenseProductInsufficientFunds() { underTest.insertCoin(VendingMachine.Coin.QUARTER); underTest.insertCoin(VendingMachine.Coin.QUARTER); underTest.insertCoin(VendingMachine.Coin.QUARTER); underTest.insertCoin(VendingMachine.Coin.DIME); underTest.insertCoin(VendingMachine.Coin.DIME); underTest.dispenseProduct(); assertExpectedOutput("Insufficient funds!"); } private void assertExpectedOutput(String expectedNoNewline) { String expected = expectedNoNewline + NL; out.flush(); byte[] data = bout.toByteArray(); String actual = new String(data); assertEquals("Output does not match expected!", expected, actual); } } 

____________________________

Finite State Machine Diagram

You will create a finite state machine diagram or, if you prefer, a UML statechart. This diagram will be the state machine for the code that has been provided to you.

GoF Pattern Card

Once you have refactored the design to use the required design pattern, complete the GoF pattern card in the refactoring design document. Remember to use context specific names for your classes (i.e. avoid using names like "Conext," "Client," "Subject," etc.). You should also write at least 2-3 sentences describing each class's role in the pattern within the context of this system; do notuse generic descriptions of the class roles in the pattern (i.e. "This class is the observer," or "This class is the client.").

UML Diagram

Create a UML diagram of your refactored subsystem. Your diagram should completely capture all of the relationships between classes in the subsystem using proper UML notation. Be sure to include the pattern stereotype for any class that is playing a role in the design pattern by including the name of the role in guillemets beneath the name of the class, e.g. <>

Step by Step Solution

There are 3 Steps involved in it

1 Expert Approved Answer
Step: 1 Unlock blur-text-image
Question Has Been Solved by an Expert!

Get step-by-step solutions from verified subject matter experts

Step: 2 Unlock
Step: 3 Unlock

Students Have Also Explored These Related Databases Questions!