Edit Page

Lab 3: The prototype design pattern

The goal of this lab is to apply the prototype design pattern, which is a creational design pattern that solves a commonly occurring problem where we need to clone an object and pass it to another component instead of reconstructing it again.

The prototype design pattern is one of the twenty-three well-known Gang of Four (GoF) design patterns. Recall that, design patterns are proven solutions to solve recurring design problems to design flexible and reusable object-oriented software; that is, objects that are easier to implement, change, test and reuse.

The prototype design pattern is often used to create objects by cloning an existing object called a prototype.

We may use the Prototype Pattern when it is considered expensive or complicated to create an instance of a given class again. The prototype design pattern allow us to create an instance by copying or cloning the existing instance.

In this lab activity, you will work on a Java console application for a car auction service. The application will fetch car information such as recalls from an API by the US National Highway Traffic Safety Administration (NHTSA). The API returns the manufacturer recalls for a given vehicle by the make, model, and year. The application needs to pass the recalls to several classes of the application. Instead of reconstructing the Car object multiple times, the app will make use of the prototype design pattern to make a clone of the object and pass it to other classes.

Video

Objectives

In this lab you will

  1. understand a real-world scenario and choose when to apply the appropriate design pattern.
  2. design and implement the prototype design pattern.
  3. use the standard HttpClient API to send requests and retrieve their responses.
  4. parse and deserialize a JSON response (convert a JSON string into s Java object).
  5. write unit tests and apply Test-Driven Development (TDD).

Requirements and Tools

  • Java JDK 1.11 or above. This code won’t work on prior versions since we’re using HttpClient, which was introduced in Java 11.
  • An IDE (e.g., Apache NetBeans, Eclipse or IntelliJ IDEA).
  • If you do not like to use an IDE, you may use any text editor (e.g., VS Code, jEdit, etc.) and the Javac compiler.
  • Apache HttpCore. We are going to use Apache HttpCore URIBuilder to construct a valid URL with parameters.
  • Jackson is a 3rd party Java library that handles the serialization and deserialization of Java objects and their JSON representations. We are going to use it for JSON deserialization (converting a JSON string into an object.).
  • The unit testing framework, Junit
  • Apache Maven is a build automation tool to build projects and manage their dependencies.

Getting Started

If your instructor is using GitHub classroom, you will need to accept the assignment using the link below, clone the template repository, and import it as a project into your IDE.

If your instructor is not using GitHub classroom, clone and import the template project at https://github.com/cpit252/lab-03.

Problem Statement

A developer is working on a Car Auction service. He needs to display car information, and users will be able to rate the car and bid on the car. He thought that the system should display interesting facts about the car such as the recalls by the manufacturer.

To get started, he created a Car class that has a list of Recalls:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class Car {
    String make;
    String model;
    int year;
    List<Recall> recalls;

    public Car(String make, String model, int year) {
        this.make = make;
        this.model = model;
        this.year = year;
    }
}

He thought of using the NHTSA’s public API to fetch the recalls. He will make an HTTP GET request to get the recalls for a car by the combination of Make, Model and Year.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class Car {
    String make;
    String model;
    int year;
    List<Recall> recalls;

    public Car(String make, String model, int year) {
        this.make = make;
        this.model = model;
        this.year = year;
        this.recalls = fetchRecalls();
    }

    private List<Recall> fetchRecalls() {
        //TODO: Send and HTTP GET request to https://api.nhtsa.gov/recalls/recallsByVehicle
    }
}

Next, he created a Recall class to deserialize and store the response from the HTTP API. The class is annotated for deserialization by jackson.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Date;

public class Recall {
    @JsonProperty("Manufacturer")
    private String manufacturer;
    @JsonProperty("ParkIt")
    private boolean parkIt;
    @JsonProperty("ParkOutSide")
    private boolean parkOutSide;
    @JsonProperty("NHTSAActionNumber")
    private String nhtsaActionNumber;
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy")
    @JsonProperty("ReportReceivedDate")
    private Date reportReceivedDate;
    @JsonProperty("Component")
    private String component;
    @JsonProperty("Summary")
    private String summary;
    @JsonProperty("Consequence")
    private String consequence;
    @JsonProperty("Remedy")
    private String remedy;
    @JsonProperty("Notes")
    private String notes;

    public Date getReportReceivedDate(){
        return this.reportReceivedDate;
    }

    public String getComponent(){
        return this.component;
    }

    @Override
    public String toString() {
        return "Recall Details:\n\t" +
                "\n\tReport Received Date: " + this.reportReceivedDate.toString() +
                "\n\tComponent: " + this.component + "\n\tSummary: " + this.summary +
                "\n\tConsequence: " + this.consequence + "\n\tRemedy: " + this.remedy +
                "\n\tPark it: " + this.parkIt + ".\t\tPark Outside: " + this.parkOutSide +
                "\n\tNotes: " + this.notes;
    }
}

In the main/client class, he can create a car object that contains the associated list of recalls:

public class App {
    public static void main(String[] args) {
        Car c1 = new Car("Honda", "Accord", 2019);
        // print car and recall info
        System.out.println(c1);
    }
}

After running the program, he noticed that the output contains too many information that he might not need in his system.

Fetching recalls from NHTSA...
Honda	Accord	2019
3 recall(s).
Recall Details:
	
	Report Received Date: Thu May 28 03:00:00 AST 2020
	Component: FUEL SYSTEM, GASOLINE:DELIVERY:FUEL PUMP
	Summary: Honda (American Honda Motor Co.) is recalling certain 2018-2019 Acura NSX, 2019 Acura RDX, RLX and RLX Sport Hybrid, 2018-2019 Honda Accord, Civic Hatchback, Civic Type R and HR-V, 2019-2020 Insight and 2019 Fit vehicles.  The low-pressure fuel pump inside the fuel tank may fail.
	Consequence: If the fuel pump fails, the engine can stall while driving, increasing the risk of a crash.
	Remedy: Honda will notify owners and dealers will replace the fuel pump assembly, free of charge.  The recall began July 22, 2020.  Owners may contact Honda customer service at 1-888-234-2138.
	Park it: false.		Park Outside: false
	Notes: Owners may also contact the National Highway Traffic Safety Administration Vehicle Safety Hotline at 1-888-327-4236 (TTY 1-800-424-9153), or go to www.safercar.gov.Recall Details:
	
	Report Received Date: Thu Dec 10 03:00:00 AST 2020
	Component: ELECTRICAL SYSTEM:BODY CONTROL MODULE:SOFTWARE
	Summary: Honda (American Honda Motor Co.) is recalling certain 2018-2020 Accord Sedan, Accord Hybrid, and 2019-2020 Insight vehicles.  A software error may cause intermittent or continuous disruptions in communication between the Body Control Module (BCM) and other components.  This may result in malfunctions of various systems such as the windshield wipers and defroster, rearview camera, exterior lights, audible warning of a stopped vehicle, and power window operation.  As such, these vehicles fail to comply with the requirements of Federal Motor Vehicle Safety Standard (FMVSS) number 103, "Windshield Defrosting and Defogging Systems" and number 111, "Rear Visibility" as well as FMVSS numbers 104, 108, 114, 118, and 305.
	Consequence: Various system malfunctions such as inoperative windshield wipers, defroster, rearview camera, or exterior lighting can increase the risk of a crash.
	Remedy: Honda will notify owners, and dealers will update the BCM software, free of charge.  The recall is expected to begin February 22, 2021.  Owners may contact Honda customer service at 1-888-234-2138.  Honda's number for this recall is X95.
	Park it: false.		Park Outside: false
	Notes: Owners may also contact the National Highway Traffic Safety Administration Vehicle Safety Hotline at 1-888-327-4236 (TTY 1-800-424-9153), or go to www.safercar.gov.Recall Details:
	
	Report Received Date: Thu Mar 25 03:00:00 AST 2021
	Component: FUEL SYSTEM, GASOLINE:DELIVERY:FUEL PUMP
	Summary: Honda (American Honda Motor Co.) is recalling certain 2019-2020 Acura MDX, MDX Sport Hybrid, RDX, TLX, Honda Accord, Civic Hatchback, Insight, 2019 Acura ILX, Honda Accord Hybrid, Civic Coupe, Civic Coupe Si, Civic Sedan, Civic Sedan Si, Civic Type R, Fit, HR-V, Odyssey, Passport, Pilot and Ridgeline, and 2018-2019 CR-V vehicles.  The low-pressure fuel pump inside the fuel tank may fail.
	Consequence: Fuel pump failure can cause an engine stall while driving, increasing the risk of a crash.
	Remedy: Honda will notify owners, and dealers will replace the fuel pump assembly, free of charge.  Owner letters were mailed May 18, 2021.  Owners may contact Honda customer service at 1-888-234-2138.  Note: This recall is an expansion of recall 20V-314.
	Park it: false.		Park Outside: false
	Notes: Owners may also contact the National Highway Traffic Safety Administration Vehicle Safety Hotline at 1-888-327-4236 (TTY 1-800-424-9153), or go to www.safercar.gov.

Thus, he created another class called RecallViewer to print a summary table of the recall information:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package edu.kau.fcit.cpit252;

import java.text.SimpleDateFormat;

public class RecallViewer {
    private Car car;

    public RecallViewer(Car car){
        this.car  = car;
    }

    public void printTableView(){
        if (car.recalls.size() < 0){
            return;
        }
        String lineSeparator = "+--------------+---------+-------+-------------+--------------------------------------------------------------------";
        System.out.println(lineSeparator);
        System.out.println("| Manufacturer | Model   | Year  |    Date     |                          Component                                 ");
        System.out.println(lineSeparator);
        SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
        for(Recall recall: car.recalls){
            String reportedDate = formatter.format(recall.getReportReceivedDate());
            System.out.println(car.getMake()  + generateWhiteSpaces(9) + " | " +
                    car.getModel() + generateWhiteSpaces(1) +  " | " +
                    car.getYear() +  " | " + reportedDate + generateWhiteSpaces(1) + " |" +
                    recall.getComponent());
            System.out.println(lineSeparator);
        }
    }

    private String generateWhiteSpaces(int total){
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < total; i++) {
            builder.append(" ");
        }
        return builder.toString();
    }

}

So he changed the main method to use the new RecallViewer class. He created another object that looks exactly as the preveiously created one and pass it to the RecallViewer class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class App {
    public static void main(String[] args) {
        Car c1 = new Car("Honda", "Accord", 2019);
        // print car and recall info
        System.out.println(c1);
        // create a duplicate car object to print the recall info in a table
        Car c2 = new Car("Honda", "Accord", 2019);
        RecallViewer rv = new RecallViewer(c2);
        rv.printTableView();
    }
}

Now, the output is shown below:

Fetching recalls from NHTSA...
+--------------+---------+------+-------------+--------------------------------------------------------------------
| Manufacturer | Model   | Year |    Date     |                          Component                                 
+--------------+---------+------+-------------+--------------------------------------------------------------------
Honda          | Accord  | 2019 | 28-05-2020  |FUEL SYSTEM, GASOLINE:DELIVERY:FUEL PUMP
+--------------+---------+------+-------------+--------------------------------------------------------------------
Honda          | Accord  | 2019 | 10-12-2020  |ELECTRICAL SYSTEM:BODY CONTROL MODULE:SOFTWARE
+--------------+---------+------+-------------+--------------------------------------------------------------------
Honda          | Accord  | 2019 | 25-03-2021  |FUEL SYSTEM, GASOLINE:DELIVERY:FUEL PUMP
+--------------+---------+------+-------------+--------------------------------------------------------------------

Questions

The current implementation creates two identical objects and sending two HTTP requests to fetch recalls. This is indeed a problem especially if the API is paid.

Question 1: Fix the current implementation of the Car class using the prototype design pattern, so it supports cloning enabling the developer to clone the first object c1 and pass it to the RecallViewer object without the need to reconstruct it and send another HTTP request. To make sure that your implementation is correct, the program should send only one HTTP request for the same car (i.e., the program should print “********************************* Fetching recalls from NHTSA…” only once).

Question: 2 A unit test is a piece of code that tests a specific functionality in the program and ensures it behaves as intended. This will help you in the future when additional changes are made to the code. Run the included unit test at src/test/java/edu/kau/fcit/cpit252/Lab3Test.java to ensure that all tests passed and the code always returns a clone of the Car object.

Deliverables and Submission

Extra Task [Optional]

If you are done with this activity, you may enable a continuos integration tool such as CircleCI ↗ to automatically run your JUnit test upon code changes. You may also embed the status image/badge that shows the status of your build and test (passing/failing) into your README file (e.g., passing status image and failing status image). Please be sure to fork the repository under your own account, so you can enable the integration of CI tools in your account.

You may refer to the lecture notes from the prerequisite course CPIT-251 on Continuous Integration (CI) and adding a code coverage badge.