Sunday, 27 October 2013

The basics of object-oriented programming - "Supermarket"

Recently, I've been working on a small Supermarket program that demonstrates some key OO concepts. It has a very basic (primitive, even) GUI, but it's a good example of effective use of inheritance and superclass abstraction in object-oriented programming (if I do say so myself). I've also used lots of Java classes, including:

  • java.util.ArrayList (with generics!)
  • java.util.Scanner
  • java.text.NumberFormat
  • java.io.File

I've also used exception-handling code for NumberFormatExceptions and other RuntimeExceptions.

As this wasn't a very major project, I've simply copied and pasted all the code below rather than upload it to Pastebin.

Download the JAR file!

"Supermarket" consists of two files - an executable .jar file and a plain text file. Links to download both of these can be found below. Make sure you put both files in the same directory or the program will not function correctly.

Complete code with annotations:

package fruits;

// **ADDED** - import declaration for java.awt.Color
import java.awt.Color;

// **CHANGED** - made Fruit abstract
public abstract class Fruit
{
//attributes
float pricePerKG; //price per kg of this fruit
String sName; //name of this fruit
boolean bEdible; //is this fruit edible
String sTexture; //texture of this fruit
Color sColour; //colour of this fruit - should be an enum!

// **DELETED** - default constructor
//default constructor
public Fruit(String sName, float fPrice)
{
//initialise variable to blanks
this.pricePerKG = fPrice;
this.sName = sName;
this.bEdible = false;
this.sTexture = "";
this.sColour = null;
}
public float getPrice()
{
return(this.pricePerKG);
}
// **ADDED** - getter for sName
public String getName() {
return sName;
}
// **ADDED** - an abstract method that all concrete subclasses must "implement"
public abstract void eat();

}
-----------------------------------------------------------
// **ADDED** - class Apple

package fruits;

public class Apple extends Fruit {
int radiusToNearestCm;

public Apple(String sName, float fPrice, int radiusToNearestCm) {
super(sName, fPrice);
super.bEdible = true;
this.radiusToNearestCm = radiusToNearestCm;
}

@Override
public void eat() {
for (int i = 0; i < radiusToNearestCm; i++) {
System.out.println("Nom");
}
}

}
-----------------------------------------------------------
// **ADDED** - class Banana

package fruits;

public class Banana extends Fruit {

public Banana(String sName, float fPrice) {
super(sName, fPrice);
}

@Override
public void eat() {
peel();
System.out.println("Nom");
System.out.println("Nom");
System.out.println("Nom");
}

private void peel() {
System.out.println("Banana peeled!");
}

}
-----------------------------------------------------------
// **ADDED** - class Grape

package fruits;

public class Grape extends Fruit {
boolean peelSkin;

public Grape(String sName, float fPrice, boolean peelSkin) {
super(sName, fPrice);
this.peelSkin = peelSkin;
}

@Override
public void eat() {
if (peelSkin) peel();
System.out.println("Nom");
}
public void peel() {
System.out.println("Grape peeled!");
}

}
-----------------------------------------------------------


-----------------------------------------------------------

-----------------------------------------------------------
package supermarket;

import fruits.Fruit;


/*
 * This class is a holder for an item in a basket - it is not intended to be used independently of the Basket class
 */
public class BasketItem
{
// Declare Variables
private Fruit ourFruit;
private float fHowMuchFruit;

// **DELETED** - default constructor
// normal constructor for this class
public BasketItem(Fruit chosenFruit, float fThisMuchFruit)
{
this.ourFruit = chosenFruit;
this.fHowMuchFruit = fThisMuchFruit;
}
// **ADDED** - getter for ourFruit
public Fruit getFruit() {
return ourFruit;
}
// **ADDED** - getter for fHowMuchFruit
public float getAmountOfFruit() {
return fHowMuchFruit;
}
// **ADDED** - method that calculates and returns the price of the item
public float getPrice() {
return ourFruit.getPrice() * fHowMuchFruit;
}

}
-----------------------------------------------------------
package supermarket;

import java.util.ArrayList;

import fruits.Fruit;

/*
 * Holds an array of Basket Items and updates the total cost providing helper methods
 * Only accepts Fruit objects!
 */
public class Basket
{
//Declare variables
private float fBasketValue; //the gross cost of the all the basket items
// **ADDED** - generics
private ArrayList<BasketItem> basketItems; //an ArrayList of BasketItem objects to represent the fruit items
//default constructor
public Basket()
{
this.basketItems = new ArrayList<>();
this.fBasketValue = 0.0f;
}
//add a fruit to the Basket - using BasketItem as the intermediary object
public void addFruitToBasket(Fruit newChosenFruit, float thisMuchFruit)
{
//make a new BasketItem object
BasketItem newFruitItem = new BasketItem(newChosenFruit,thisMuchFruit);
this.basketItems.add(newFruitItem);
//now to update the total
this.fBasketValue += (newChosenFruit.getPrice() * thisMuchFruit);
}
public float getBasketValue()
{
return (this.fBasketValue);
}
// **ADDED** - a getter for basketItems
public ArrayList<BasketItem> getBasketItems() {
return basketItems;
}
// **ADDED** - remove a fruit from the Basket
public void removeFruitFromBasket(int index) throws IndexOutOfBoundsException {
BasketItem fruitItem = basketItems.get(index);
//now to update the total
fBasketValue -= fruitItem.getPrice();
basketItems.remove(index);
}

}
-----------------------------------------------------------
package supermarket;

import java.io.File;
import java.io.FileNotFoundException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Scanner;

import javax.swing.JOptionPane;

import fruits.Apple;
import fruits.Banana;
import fruits.Fruit;
import fruits.Grape;

public class SimpleShop {
private Basket ourBasket;
private ArrayList<String> discountCodes;

// **ADDED** - methods so that the code isn't all in main()
public void go() {
// Setup our Basket
ourBasket = new Basket();

// Welcome message
JOptionPane.showMessageDialog(null, "Welcome to SimpleShop!");

// **ADDED** - Options menu
boolean programExit = false;
while (!programExit) {

String optionString = JOptionPane
.showInputDialog("What would you like to do?\n1 - Add a fruit\n2 - Show the fruits in the basket\n3 - Calculate the price of the fruits in the basket\n4 - Remove an item\n5 - Pay");

if (optionString != null) {
switch (optionString) {
case "1":
addFruit();
break;
case "2":
showFruits();
break;
case "3":
calcPrice();
break;
case "4":
removeItem();
break;
case "5":
pay();
programExit = true;
break;
default:
break;
}
} else {
programExit = true;
}
}

JOptionPane.showMessageDialog(null, "Thanks for shopping. We hope to see you again soon!");

// End the program
System.exit(0);
}

private void addFruit() {
try {
Fruit f = null;
String fruitNum;
String name;
String priceString;
float price;
String weightString;
float weightInKg;

do {
fruitNum = JOptionPane
.showInputDialog("What fruit would you like to add?\n1 - Apple\n2 - Banana\n3 - Grape");
} while (fruitNum == null);

if (!fruitNum.equals("1") && !fruitNum.equals("2") && !fruitNum.equals("3")) {
throw new Exception();
}

do {
name = JOptionPane
.showInputDialog("And the name of the fruit?");
} while (name == null);

do {
priceString = JOptionPane.showInputDialog("Price per kg? (In pounds, please.)");
} while (priceString == null);
price = Float.parseFloat(priceString);

switch (fruitNum) {
case "1":
String radiusString;
int radius;

do {
radiusString = JOptionPane.showInputDialog("What's the radius of the apple? (To the nearest cm, please.)");
} while (radiusString == null);
radius = Integer.parseInt(radiusString);

f = new Apple(name, price, radius);

break;
case "2":
f = new Banana(name, price);
break;
case "3":
String peelSkinString;
boolean peelSkin;

do {
peelSkinString = JOptionPane.showInputDialog("Peel the skin of the grape(s)? y/n");
} while (peelSkinString == null);

if (peelSkinString.equals("y")) {
peelSkin = true;
} else if (peelSkinString.equals("n")) {
peelSkin = false;
} else {
throw new Exception();
}

f = new Grape(name, price, peelSkin);

break;
default:
break;
}

do {
weightString = JOptionPane.showInputDialog("How much, in kg, of the fruit do you want to add?");
} while (weightString == null);
weightInKg = Float.parseFloat(weightString);

ourBasket.addFruitToBasket(f, weightInKg);

JOptionPane.showMessageDialog(null, "Fruit successfully added!");

} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(null, "AAAARRRRGGGHHH! HORRIBLE EXPLOSION!!!!");
} catch (Exception e) {
JOptionPane.showMessageDialog(null, "Something bad happened. Sorry.");
}
}

private void showFruits() {
// **ADDED** - check what's in the basket!
// Create a StringBuilder object to add things to a string
// I could've just written 'String itemLister = "";', but that's boring :)
StringBuilder itemLister = new StringBuilder();
ArrayList<BasketItem> items = ourBasket.getBasketItems();

// Here, if I'd used a string ('String itemLister = "";') I would've written 'itemLister += "Name of fruit...";'
itemLister.append("Name of fruit--Weight (kg)--Price (£)\n");

for (BasketItem item : items) {
// Here, if I'd used a string ('String itemLister = "";') I would've written 'itemLister += item.getFruit()...;'
itemLister.append(item.getFruit().getName() + "--" + item.getAmountOfFruit() + "--" + String.format("%,.2f", item.getPrice()) + "\n");
}

// Here, if I'd used a string ('String itemLister = "";') I would've written '...Dialog(null, itemLister);'
JOptionPane.showMessageDialog(null, itemLister.toString());
}

private void calcPrice() {
// now to check what the cost of the basket is
NumberFormat format = NumberFormat.getCurrencyInstance();

JOptionPane.showMessageDialog(null, "Your basket is worth: " + format.format(ourBasket.getBasketValue()));
}

private void removeItem() {
// Display all the fruits in the basket and ask the user which one he/she would like to remove
StringBuilder messageBuilder = new StringBuilder();
ArrayList<BasketItem> items = ourBasket.getBasketItems();
int number = 0;
String indexString;
int indexToRemove;

messageBuilder.append("Here are the fruits currently in your basket:\n\n");

messageBuilder.append("No.--Name of fruit--Weight (kg)--Price (£)\n");

for (BasketItem item : items) {
messageBuilder.append(number + "--" + item.getFruit().getName() + "--" + item.getAmountOfFruit() + "--" + String.format("%,.2f", item.getPrice()) + "\n");
number++;
}

messageBuilder.append("\nWhich fruit would you like to remove? Type the corresponding item number.");

try {
do {
indexString = JOptionPane.showInputDialog(messageBuilder.toString());
} while (indexString == null);

indexToRemove = Integer.parseInt(indexString);

ourBasket.removeFruitFromBasket(indexToRemove);

JOptionPane.showMessageDialog(null, "Fruit successfully removed!");
} catch (RuntimeException e) {
JOptionPane.showMessageDialog(null, "AAAARRRRGGGHHH! HORRIBLE EXPLOSION!!!!");
}
}

private void pay() {
NumberFormat format = NumberFormat.getCurrencyInstance();
float amountToPay = ourBasket.getBasketValue();

int useDiscountCode = JOptionPane.showConfirmDialog(null, "Do you have a discount code?", "Discount code?", JOptionPane.YES_NO_OPTION);

if (useDiscountCode == 0) {
discountCodes = new ArrayList<>();

try (Scanner fileScanner = new Scanner(new File("discount_codes.txt"))) {
while (fileScanner.hasNextLine()) {
discountCodes.add(fileScanner.nextLine());
}

boolean match = false;
do {
String discountCode = JOptionPane.showInputDialog("Please enter your discount code");
if (discountCode != null) {
if (discountCodes.contains(discountCode)) {
match = true;
} else {
JOptionPane.showMessageDialog(null, "Please try again.");
}
} else {
pay();
return;
}
} while (!match);
JOptionPane.showMessageDialog(null, "10% discount!");
amountToPay = ourBasket.getBasketValue() * 0.9f;
} catch (FileNotFoundException e) {
JOptionPane.showMessageDialog(null, "Discount codes file not found. Sorry.");
}
}
JOptionPane.showMessageDialog(null, "Please pay " + format.format(amountToPay));
JOptionPane.showMessageDialog(null, "Thank you for paying!");
}

public static void main(String[] args)
{
SimpleShop shop = new SimpleShop();
shop.go();
}

}

Friday, 11 October 2013

Blog Bulletin - "Sorting Algorithms"

I've started creating pages for "special" programs that took me a substantial amount of time to make. Recently, I've finished making a program that sorts an inputted list of numbers in ascending order. Check out the page 'Program - "Sorting Algorithms"' for the complete code for the program, as well as a link to download an executable JAR file that runs the thing!

Thursday, 10 October 2013

Blog Bulletin - Pastebin

Just a quick post to let you know that I've created Pastebin account to store my code online. From now on, all the code posted on this blog will be embedded from Pastebin.