package com.industriallogic.videostore;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
public class CustomerTest {
Customer customer = new Customer("Bob");
@Test
public void rentalRecord() {
customer.addRental(new Rental(new Movie("Jaws", new RegularPrice()), 2));
customer.addRental(new Rental(new Movie("GoldenEye", new RegularPrice()), 3));
customer.addRental(new Rental(new Movie("ShortNew", new NewReleasePrice()), 1));
customer.addRental(new Rental(new Movie("LongNew", new NewReleasePrice()), 2));
customer.addRental(new Rental(new Movie("Bambi", new ChildrensPrice()), 3));
customer.addRental(new Rental(new Movie("Toy Story", new ChildrensPrice()), 4));
assertEquals("Rental Record for Bob\n" + "\tJaws\t2.0\n"
+ "\tGoldenEye\t3.5\n"
+ "\tShortNew\t3.0\n"
+ "\tLongNew\t6.0\n"
+ "\tBambi\t1.5\n"
+ "\tToy Story\t3.0\n"
+ "You owed 19.0\n"
+ "You earned 7 frequent renter points",
customer.statement(new TextStatement()));
}
@Test
public void htmlStatement() {
customer.addRental(new Rental(new Movie("Jaws 2", new RegularPrice()), 1));
assertEquals("<h2>Bob</h2>\n"
+ "<b>Jaws 2</b>: 2.0\n"
+ "<br/>You owe 2.0\n",
customer.statement(new HtmlStatement()));
}
}
import java.util.ArrayList;
import java.util.List;
public class Customer {
private String _name;
private List<Rental> myRentals = new ArrayList<Rental>();
public Customer(String name) {
this._name = name;
}
public void addRental(Rental rental) {
myRentals.add(rental);
}
public String getName() {
return _name;
}
public String statement(Statement statement) {
return statement.statement(getName(), this.myRentals, totalAmount(this.myRentals), frequentRenterPoints(this.myRentals));
}
private double totalAmount(Iterable<Rental> rentals) {
double totalAmount = 0;
for (Rental each : rentals) {
double thisAmount = each.amount();
totalAmount += thisAmount;
}
return totalAmount;
}
private int frequentRenterPoints(Iterable<Rental> rentals) {
int totalRenterPoints = 0;
for (Rental each : rentals) {
totalRenterPoints += each.renterPoints(each.getDaysRented());
}
return totalRenterPoints;
}
}
class Rental {
private Movie _movie;
private int _daysRented;
public Rental(Movie movie, int daysRented) {
_movie = movie;
_daysRented = daysRented;
}
public int getDaysRented() {
return _daysRented;
}
public Movie getMovie() {
return _movie;
}
public double amount() {
return getMovie().amount(getDaysRented());
}
int renterPoints(int daysRented) {
return getMovie().renterPoints(daysRented);
}
}
public class Movie {
private String _title;
private Price _price;
public Movie(String title, Price price) {
_title = title;
_price = price;
}
public String getTitle() {
return _title;
}
public double amount(int daysRented) {
return _price.amount(daysRented);
}
public int renterPoints(int daysRented) {
return _price.renterPoints(daysRented);
}
}
public abstract class Price {
abstract double amount(int daysRented);
public int renterPoints(int daysRented) {
return 1;
}
}
public class RegularPrice extends Price {
public double amount(int daysRented) {
double thisAmount = 2;
if (daysRented > 2) {
thisAmount += (daysRented - 2) * 1.5;
}
return thisAmount;
}
}
public class NewReleasePrice extends Price {
public double amount(int daysRented) {
return (double) (daysRented * 3);
}
public int renterPoints(int daysRented) {
return daysRented > 1 ? 2 : 1;
}
}
public class ChildrensPrice extends Price {
public double amount(int daysRented) {
double thisAmount = 1.5;
if (daysRented > 3) {
thisAmount += (daysRented - 3) * 1.5;
}
return thisAmount;
}
}
import java.util.List;
public abstract class Statement {
protected abstract String header(String name);
protected abstract String lineItem(Movie movie, double amount);
protected abstract String footer(double totalAmount, int frequentRenterPoints);
public String statement(String name, List<Rental> rentals, double totalAmount, int totalRenterPoints) {
String result = header(name);
for (Rental each : rentals) {
result += lineItem(each.getMovie(), each.amount());
}
result += footer(totalAmount, totalRenterPoints);
return result;
}
}
public class TextStatement extends Statement {
protected String header(String name) {
return "Rental Record for " + name + "\n";
}
protected String lineItem(Movie movie, double amount) {
return "\t" + movie.getTitle() + "\t" + String.valueOf(amount) + "\n";
}
protected String footer(double totalAmount, int frequentRenterPoints) {
String footer1 = "You owed " + String.valueOf(totalAmount) + "\n";
String footer2 = "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points";
return footer1 + footer2;
}
}
public class HtmlStatement extends Statement {
@Override
protected String header(String name) {
return "<h2>" + name + "</h2>\n";
}
@Override
protected String lineItem(Movie movie, double amount) {
return "<b>" + movie.getTitle() + "</b>: " + amount + "\n";
}
@Override
protected String footer(double totalAmount, int frequentRenterPoints) {
return "<br/>You owe " + totalAmount + "\n";
}
}
If I wanted to improve this solution further, I'd:
Introduce an object to represent the list (or set) of rentals, and move totalPoints()
and frequentRenterPoints()
over there.
Shift the design so Statement
knows Customer
rather than Customer
knowing Statement
.
Martin Fowler has written up a
JavaScript version .
Part 1 (~60 min.) shows breaking up the statement processing, and introducing
Price
types.
VIDEO
Part 2 (~30 min.) shows getting rid of the now-unused price code,
and pulling out a separate
Statement
object.
VIDEO
One solution
Java Compatibility Note : These exercises are tested
with
JDK 17 , a long-term support version released in 2021.
While many exercises work with earlier Java versions, some
may require JDK 17 due to library dependencies. If you
encounter issues, please use JDK 17. Should problems persist,
contact us for support.