mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-05-14 08:58:26 +00:00
feat: Implemented the Table Inheritance pattern (#3105)
* sample classes and tests * sample classes and tests * read me addition * fix violations * fix quality (coverage) * fix quality (coverage) #2 * resolved comments
This commit is contained in:
@@ -218,6 +218,7 @@
|
||||
<module>function-composition</module>
|
||||
<module>microservices-distributed-tracing</module>
|
||||
<module>microservices-idempotent-consumer</module>
|
||||
<module>table-inheritance</module>
|
||||
</modules>
|
||||
<repositories>
|
||||
<repository>
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
---
|
||||
title: "Table Inheritance Pattern in Java: Modeling Hierarchical Data in Relational Databases"
|
||||
shortTitle: Table Inheritance
|
||||
description: "Explore the Table Inheritance pattern in Java with real-world examples, database schema, and tutorials. Learn how to model class hierarchies elegantly in relational databases."
|
||||
category: Data Access Pattern, Structural Pattern
|
||||
language: en
|
||||
tag:
|
||||
- Decoupling
|
||||
- Inheritance
|
||||
- Polymorphism
|
||||
- Object Mapping
|
||||
- Persistence
|
||||
- Data Transformation
|
||||
---
|
||||
|
||||
## Also Known As
|
||||
- Class Table Inheritance
|
||||
---
|
||||
|
||||
## Intent of Table Inheritance Pattern
|
||||
The Table Inheritance pattern models a class hierarchy in a relational database by creating
|
||||
separate tables for each class in the hierarchy. These tables share a common primary key, which in
|
||||
subclass tables also serves as a foreign key referencing the primary key of the base class table.
|
||||
This linkage maintains relationships and effectively represents the inheritance structure. This pattern
|
||||
enables the organization of complex data models, particularly when subclasses have unique properties
|
||||
that must be stored in distinct tables.
|
||||
|
||||
---
|
||||
|
||||
## Detailed Explanation of Table Inheritance Pattern with Real-World Examples
|
||||
|
||||
### Real-World Example
|
||||
Consider a **Vehicle Management System** with a `Vehicle` superclass and subclasses like `Car` and `Truck`.
|
||||
|
||||
- The **Vehicle Table** stores attributes common to all vehicles, such as `make`, `model`, and `year`. Its primary key (`id`) uniquely identifies each vehicle.
|
||||
- The **Car Table** and **Truck Table** store attributes specific to their respective types, such as `numberOfDoors` for cars and `payloadCapacity` for trucks.
|
||||
- The `id` column in the **Car Table** and **Truck Table** serves as both the primary key for those tables and a foreign key referencing the `id` in the **Vehicle Table**.
|
||||
|
||||
This setup ensures each subclass entry corresponds to a base class entry, maintaining the inheritance relationship while keeping subclass-specific data in their own tables.
|
||||
|
||||
### In Plain Words
|
||||
In table inheritance, each class in the hierarchy is represented by a separate table, which
|
||||
allows for a clear distinction between shared attributes (stored in the base class table) and
|
||||
specific attributes (stored in subclass tables).
|
||||
|
||||
### Martin Fowler Says
|
||||
|
||||
Relational databases don't support inheritance, which creates a mismatch when mapping objects.
|
||||
To fix this, Table Inheritance uses a separate table for each class in the hierarchy while maintaining
|
||||
relationships through foreign keys, making it easier to link the classes together in the database.
|
||||
|
||||
For more detailed information, refer to Martin Fowler's article on [Class Table Inheritance](https://martinfowler.com/eaaCatalog/classTableInheritance.html).
|
||||
|
||||
|
||||
## Programmatic Example of Table Inheritance Pattern in Java
|
||||
|
||||
|
||||
The `Vehicle` class will be the superclass, and we will have `Car` and `Truck` as subclasses that extend
|
||||
`Vehicle`. The `Vehicle` class will store common attributes, while `Car` and `Truck` will store
|
||||
attributes specific to those subclasses.
|
||||
|
||||
### Key Aspects of the Pattern:
|
||||
|
||||
1. **Superclass (`Vehicle`)**:
|
||||
The `Vehicle` class stores attributes shared by all vehicle types, such as:
|
||||
- `make`: The manufacturer of the vehicle.
|
||||
- `model`: The model of the vehicle.
|
||||
- `year`: The year the vehicle was manufactured.
|
||||
- `id`: A unique identifier for the vehicle.
|
||||
|
||||
These attributes are stored in the **`Vehicle` table** in the database.
|
||||
|
||||
2. **Subclass (`Car` and `Truck`)**:
|
||||
Each subclass (`Car` and `Truck`) stores attributes specific to that vehicle type:
|
||||
- `Car`: Has an additional attribute `numberOfDoors` representing the number of doors the car has.
|
||||
- `Truck`: Has an additional attribute `payloadCapacity` representing the payload capacity of the truck.
|
||||
|
||||
These subclass-specific attributes are stored in the **`Car` and `Truck` tables**.
|
||||
|
||||
3. **Foreign Key Relationship**:
|
||||
Each subclass (`Car` and `Truck`) contains the `id` field which acts as a **foreign key** that
|
||||
references the primary key (`id`) of the superclass (`Vehicle`). This foreign key ensures the
|
||||
relationship between the common attributes in the `Vehicle` table and the specific attributes in the
|
||||
subclass tables (`Car` and `Truck`).
|
||||
|
||||
|
||||
```java
|
||||
/**
|
||||
* Superclass
|
||||
* Represents a generic vehicle with basic attributes like make, model, year, and ID.
|
||||
*/
|
||||
public class Vehicle {
|
||||
private String make;
|
||||
private String model;
|
||||
private int year;
|
||||
private int id;
|
||||
|
||||
// Constructor, getters, and setters...
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a car, which is a subclass of Vehicle.
|
||||
*/
|
||||
public class Car extends Vehicle {
|
||||
private int numberOfDoors;
|
||||
|
||||
// Constructor, getters, and setters...
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a truck, which is a subclass of Vehicle.
|
||||
*/
|
||||
public class Truck extends Vehicle {
|
||||
private int payloadCapacity;
|
||||
|
||||
// Constructor, getters, and setters...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Table Inheritance Pattern Class Diagram
|
||||
|
||||
|
||||
<img src="etc/class-diagram.png" width="400" height="500" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Table Inheritance Pattern Database Schema
|
||||
|
||||
### Vehicle Table
|
||||
| Column | Description |
|
||||
|--------|-------------------------------------|
|
||||
| id | Primary key |
|
||||
| make | The make of the vehicle |
|
||||
| model | The model of the vehicle |
|
||||
| year | The manufacturing year of the vehicle |
|
||||
|
||||
### Car Table
|
||||
| Column | Description |
|
||||
|------------------|-------------------------------------|
|
||||
| id | Foreign key referencing `Vehicle(id)` |
|
||||
| numberOfDoors | Number of doors in the car |
|
||||
|
||||
### Truck Table
|
||||
| Column | Description |
|
||||
|-------------------|-------------------------------------|
|
||||
| id | Foreign key referencing `Vehicle(id)` |
|
||||
| payloadCapacity | Payload capacity of the truck |
|
||||
|
||||
---
|
||||
|
||||
## When to Use the Table Inheritance Pattern in Java
|
||||
|
||||
- When your application requires a clear mapping of an object-oriented class hierarchy to relational tables.
|
||||
- When subclasses have unique attributes that do not fit into a single base table.
|
||||
- When scalability and normalization of data are important considerations.
|
||||
- When you need to separate concerns and organize data in a way that each subclass has its own
|
||||
table but maintains relationships with the superclass.
|
||||
|
||||
## Table Inheritance Pattern Java Tutorials
|
||||
|
||||
- [Software Patterns Lexicon: Class Table Inheritance](https://softwarepatternslexicon.com/patterns-sql/4/4/2/)
|
||||
- [Martin Fowler: Class Table Inheritance](http://thierryroussel.free.fr/java/books/martinfowler/www.martinfowler.com/isa/classTableInheritance.html)
|
||||
|
||||
---
|
||||
|
||||
## Real-World Applications of Table Inheritance Pattern in Java
|
||||
|
||||
- **Vehicle Management System**: Used to store different types of vehicles like Car and Truck in separate tables but maintain a relationship through a common superclass `Vehicle`.
|
||||
- **E-Commerce Platforms**: Where different product types, such as Clothing, Electronics, and Furniture, are stored in separate tables with shared attributes in a superclass `Product`.
|
||||
|
||||
## Benefits and Trade-offs of Table Inheritance Pattern
|
||||
|
||||
### Benefits
|
||||
|
||||
- **Clear Structure**: Each class has its own table, making the data model easier to maintain and understand.
|
||||
- **Scalability**: Each subclass can be extended independently without affecting the other tables, making the system more scalable.
|
||||
- **Data Normalization**: Helps avoid data redundancy and keeps the schema normalized.
|
||||
|
||||
### Trade-offs
|
||||
|
||||
- **Multiple Joins**: Retrieving data that spans multiple subclasses may require joining multiple tables, which could lead to performance issues.
|
||||
- **Increased Complexity**: Managing relationships between tables and maintaining integrity can become more complex.
|
||||
- **Potential for Sparse Tables**: Subclasses with fewer attributes may end up with tables that have many null fields.
|
||||
|
||||
## Related Java Design Patterns
|
||||
|
||||
- **Single Table Inheritance** – A strategy where a single table is used to store all classes in an
|
||||
inheritance hierarchy. It stores all attributes of the class and its subclasses in one table.
|
||||
- **Singleton Pattern** – Used when a class needs to have only one instance.
|
||||
|
||||
|
||||
## References and Credits
|
||||
|
||||
- **Martin Fowler** - [*Patterns of Enterprise Application Architecture*](https://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420)
|
||||
- **Java Persistence with Hibernate** - [Link to book](https://www.amazon.com/Java-Persistence-Hibernate-Christian-Bauer/dp/193239469X)
|
||||
- **Object-Relational Mapping on Wikipedia** - [Link to article](https://en.wikipedia.org/wiki/Object-relational_mapping)
|
||||
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.26.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>table-inheritance</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.7.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.24</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.iluwatar.table.inheritance;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* The main entry point of the application demonstrating the use of vehicles.
|
||||
*
|
||||
* <p>The Table Inheritance pattern models a class hierarchy in a relational database by creating
|
||||
* separate tables for each class in the hierarchy. These tables share a common primary key, which in
|
||||
* subclass tables also serves as a foreign key referencing the primary key of the base class table.
|
||||
* This linkage maintains relationships and effectively represents the inheritance structure. This
|
||||
* pattern enables the organization of complex data models, particularly when subclasses have unique
|
||||
* properties that must be stored in distinct tables.
|
||||
*/
|
||||
|
||||
public class App {
|
||||
/**
|
||||
* Manages the storage and retrieval of Vehicle objects, including Cars and Trucks.
|
||||
*
|
||||
* <p>This example demonstrates the **Table Inheritance** pattern, where each vehicle type
|
||||
* (Car and Truck) is stored in its own separate table. The `VehicleDatabase` simulates
|
||||
* a simple database that manages these entities, with each subclass (Car and Truck)
|
||||
* being stored in its respective table.
|
||||
*
|
||||
* <p>The `VehicleDatabase` contains the following tables:
|
||||
* - `vehicleTable`: Stores all vehicle objects, including both `Car` and `Truck` objects.
|
||||
* - `carTable`: Stores only `Car` objects, with fields specific to cars.
|
||||
* - `truckTable`: Stores only `Truck` objects, with fields specific to trucks.
|
||||
*
|
||||
* <p>The example demonstrates:
|
||||
* 1. Saving instances of `Car` and `Truck` to their respective tables in the database.
|
||||
* 2. Retrieving vehicles (both cars and trucks) from the appropriate table based on their ID.
|
||||
* 3. Printing all vehicles stored in the database.
|
||||
* 4. Showing how to retrieve specific types of vehicles (`Car` or `Truck`) by their IDs.
|
||||
*
|
||||
* <p>In the **Table Inheritance** pattern, each subclass has its own table, making it easier
|
||||
* to manage specific attributes of each subclass.
|
||||
*
|
||||
* @param args command-line arguments
|
||||
*/
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
final Logger logger = Logger.getLogger(App.class.getName());
|
||||
|
||||
VehicleDatabase database = new VehicleDatabase();
|
||||
|
||||
Car car = new Car(2020, "Toyota", "Corolla", 4, 1);
|
||||
Truck truck = new Truck(2018, "Ford", "F-150", 60, 2);
|
||||
|
||||
database.saveVehicle(car);
|
||||
database.saveVehicle(truck);
|
||||
|
||||
database.printAllVehicles();
|
||||
|
||||
Vehicle vehicle = database.getVehicle(car.getId());
|
||||
Car retrievedCar = database.getCar(car.getId());
|
||||
Truck retrievedTruck = database.getTruck(truck.getId());
|
||||
|
||||
logger.info(String.format("Retrieved Vehicle: %s", vehicle));
|
||||
logger.info(String.format("Retrieved Car: %s", retrievedCar));
|
||||
logger.info(String.format("Retrieved Truck: %s", retrievedTruck));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.iluwatar.table.inheritance;
|
||||
import lombok.Getter;
|
||||
/**
|
||||
* Represents a car with a specific number of doors.
|
||||
*/
|
||||
|
||||
@Getter
|
||||
public class Car extends Vehicle {
|
||||
private int numDoors;
|
||||
|
||||
/**
|
||||
* Constructs a Car object.
|
||||
*
|
||||
* @param year the manufacturing year
|
||||
* @param make the make of the car
|
||||
* @param model the model of the car
|
||||
* @param numDoors the number of doors
|
||||
* @param id the unique identifier for the car
|
||||
*/
|
||||
public Car(int year, String make, String model, int numDoors, int id) {
|
||||
super(year, make, model, id);
|
||||
if (numDoors <= 0) {
|
||||
throw new IllegalArgumentException("Number of doors must be positive.");
|
||||
}
|
||||
this.numDoors = numDoors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of doors for the car.
|
||||
*
|
||||
* @param doors the number of doors
|
||||
*/
|
||||
public void setNumDoors(int doors) {
|
||||
if (doors <= 0) {
|
||||
throw new IllegalArgumentException("Number of doors must be positive.");
|
||||
}
|
||||
this.numDoors = doors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Car{"
|
||||
+ "id=" + getId()
|
||||
+ ", make='" + getMake() + '\''
|
||||
+ ", model='" + getModel() + '\''
|
||||
+ ", year=" + getYear()
|
||||
+ ", numberOfDoors=" + getNumDoors()
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.iluwatar.table.inheritance;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Represents a truck, a type of vehicle with a specific load capacity.
|
||||
*/
|
||||
@Getter
|
||||
public class Truck extends Vehicle {
|
||||
private double loadCapacity;
|
||||
|
||||
/**
|
||||
* Constructs a Truck object with the given parameters.
|
||||
*
|
||||
* @param year the year of manufacture
|
||||
* @param make the make of the truck
|
||||
* @param model the model of the truck
|
||||
* @param loadCapacity the load capacity of the truck
|
||||
* @param id the unique ID of the truck
|
||||
*/
|
||||
public Truck(int year, String make, String model, double loadCapacity, int id) {
|
||||
super(year, make, model, id);
|
||||
if (loadCapacity <= 0) {
|
||||
throw new IllegalArgumentException("Load capacity must be positive.");
|
||||
}
|
||||
this.loadCapacity = loadCapacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the load capacity of the truck.
|
||||
*
|
||||
* @param capacity the new load capacity
|
||||
*/
|
||||
public void setLoadCapacity(double capacity) {
|
||||
if (capacity <= 0) {
|
||||
throw new IllegalArgumentException("Load capacity must be positive.");
|
||||
}
|
||||
this.loadCapacity = capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the truck.
|
||||
*
|
||||
* @return a string with the truck's details
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Truck{"
|
||||
+ "id=" + getId()
|
||||
+ ", make='" + getMake() + '\''
|
||||
+ ", model='" + getModel() + '\''
|
||||
+ ", year=" + getYear()
|
||||
+ ", payloadCapacity=" + getLoadCapacity()
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.iluwatar.table.inheritance;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* Represents a generic vehicle with basic attributes like make, model, year, and ID.
|
||||
*/
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public class Vehicle {
|
||||
|
||||
private String make;
|
||||
private String model;
|
||||
private int year;
|
||||
private int id;
|
||||
|
||||
/**
|
||||
* Constructs a Vehicle object with the given parameters.
|
||||
*
|
||||
* @param year the year of manufacture
|
||||
* @param make the make of the vehicle
|
||||
* @param model the model of the vehicle
|
||||
* @param id the unique ID of the vehicle
|
||||
*/
|
||||
public Vehicle(int year, String make, String model, int id) {
|
||||
this.make = make;
|
||||
this.model = model;
|
||||
this.year = year;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the vehicle.
|
||||
*
|
||||
* @return a string with the vehicle's details
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Vehicle{"
|
||||
+ "id=" + id
|
||||
+ ", make='" + make + '\''
|
||||
+ ", model='" + model + '\''
|
||||
+ ", year=" + year
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.iluwatar.table.inheritance;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* Manages the storage and retrieval of Vehicle objects, including Cars and Trucks.
|
||||
*/
|
||||
public class VehicleDatabase {
|
||||
|
||||
final Logger logger = Logger.getLogger(VehicleDatabase.class.getName());
|
||||
|
||||
private Map<Integer, Vehicle> vehicleTable = new HashMap<>();
|
||||
private Map<Integer, Car> carTable = new HashMap<>();
|
||||
private Map<Integer, Truck> truckTable = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Saves a vehicle to the database. If the vehicle is a Car or Truck, it is added to the respective table.
|
||||
*
|
||||
* @param vehicle the vehicle to save
|
||||
*/
|
||||
public void saveVehicle(Vehicle vehicle) {
|
||||
vehicleTable.put(vehicle.getId(), vehicle);
|
||||
if (vehicle instanceof Car) {
|
||||
carTable.put(vehicle.getId(), (Car) vehicle);
|
||||
} else if (vehicle instanceof Truck) {
|
||||
truckTable.put(vehicle.getId(), (Truck) vehicle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a vehicle by its ID.
|
||||
*
|
||||
* @param id the ID of the vehicle
|
||||
* @return the vehicle with the given ID, or null if not found
|
||||
*/
|
||||
public Vehicle getVehicle(int id) {
|
||||
return vehicleTable.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a car by its ID.
|
||||
*
|
||||
* @param id the ID of the car
|
||||
* @return the car with the given ID, or null if not found
|
||||
*/
|
||||
public Car getCar(int id) {
|
||||
return carTable.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a truck by its ID.
|
||||
*
|
||||
* @param id the ID of the truck
|
||||
* @return the truck with the given ID, or null if not found
|
||||
*/
|
||||
public Truck getTruck(int id) {
|
||||
return truckTable.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints all vehicles in the database.
|
||||
*/
|
||||
public void printAllVehicles() {
|
||||
for (Vehicle vehicle : vehicleTable.values()) {
|
||||
logger.info(vehicle.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import com.iluwatar.table.inheritance.App;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.logging.ConsoleHandler;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Logger;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests if the main method runs without throwing exceptions and prints expected output.
|
||||
*/
|
||||
|
||||
class AppTest {
|
||||
|
||||
@Test
|
||||
void testAppMainMethod() {
|
||||
|
||||
ByteArrayOutputStream outContent = new ByteArrayOutputStream();
|
||||
PrintStream printStream = new PrintStream(outContent);
|
||||
|
||||
System.setOut(printStream);
|
||||
|
||||
Logger logger = Logger.getLogger(App.class.getName());
|
||||
|
||||
Handler handler = new ConsoleHandler() {
|
||||
@Override
|
||||
public void publish(java.util.logging.LogRecord recordObj) {
|
||||
printStream.println(getFormatter().format(recordObj));
|
||||
}
|
||||
};
|
||||
handler.setLevel(java.util.logging.Level.ALL);
|
||||
logger.addHandler(handler);
|
||||
|
||||
App.main(new String[]{});
|
||||
|
||||
String output = outContent.toString();
|
||||
|
||||
assertTrue(output.contains("Retrieved Vehicle:"));
|
||||
assertTrue(output.contains("Toyota")); // Car make
|
||||
assertTrue(output.contains("Ford")); // Truck make
|
||||
assertTrue(output.contains("Retrieved Car:"));
|
||||
assertTrue(output.contains("Retrieved Truck:"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.iluwatar.table.inheritance.Car;
|
||||
import com.iluwatar.table.inheritance.Truck;
|
||||
import com.iluwatar.table.inheritance.Vehicle;
|
||||
import com.iluwatar.table.inheritance.VehicleDatabase;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for the {@link VehicleDatabase} class.
|
||||
* Tests saving, retrieving, and printing vehicles of different types.
|
||||
*/
|
||||
class VehicleDatabaseTest {
|
||||
|
||||
private VehicleDatabase vehicleDatabase;
|
||||
|
||||
/**
|
||||
* Sets up a new instance of {@link VehicleDatabase} before each test.
|
||||
*/
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
vehicleDatabase = new VehicleDatabase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests saving a {@link Car} to the database and retrieving it.
|
||||
*/
|
||||
@Test
|
||||
void testSaveAndRetrieveCar() {
|
||||
Car car = new Car(2020, "Toyota", "Corolla", 4, 1);
|
||||
vehicleDatabase.saveVehicle(car);
|
||||
|
||||
Vehicle retrievedVehicle = vehicleDatabase.getVehicle(car.getId());
|
||||
assertNotNull(retrievedVehicle);
|
||||
assertEquals(car.getId(), retrievedVehicle.getId());
|
||||
assertEquals(car.getMake(), retrievedVehicle.getMake());
|
||||
assertEquals(car.getModel(), retrievedVehicle.getModel());
|
||||
assertEquals(car.getYear(), retrievedVehicle.getYear());
|
||||
|
||||
Car retrievedCar = vehicleDatabase.getCar(car.getId());
|
||||
assertNotNull(retrievedCar);
|
||||
assertEquals(car.getNumDoors(), retrievedCar.getNumDoors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests saving a {@link Truck} to the database and retrieving it.
|
||||
*/
|
||||
@Test
|
||||
void testSaveAndRetrieveTruck() {
|
||||
Truck truck = new Truck(2018, "Ford", "F-150", 60, 2);
|
||||
vehicleDatabase.saveVehicle(truck);
|
||||
|
||||
Vehicle retrievedVehicle = vehicleDatabase.getVehicle(truck.getId());
|
||||
assertNotNull(retrievedVehicle);
|
||||
assertEquals(truck.getId(), retrievedVehicle.getId());
|
||||
assertEquals(truck.getMake(), retrievedVehicle.getMake());
|
||||
assertEquals(truck.getModel(), retrievedVehicle.getModel());
|
||||
assertEquals(truck.getYear(), retrievedVehicle.getYear());
|
||||
|
||||
Truck retrievedTruck = vehicleDatabase.getTruck(truck.getId());
|
||||
assertNotNull(retrievedTruck);
|
||||
assertEquals(truck.getLoadCapacity(), retrievedTruck.getLoadCapacity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests saving multiple vehicles to the database and printing them.
|
||||
*/
|
||||
@Test
|
||||
void testPrintAllVehicles() {
|
||||
Car car = new Car(2020, "Toyota", "Corolla", 4, 1);
|
||||
Truck truck = new Truck(2018, "Ford", "F-150", 60, 2);
|
||||
vehicleDatabase.saveVehicle(car);
|
||||
vehicleDatabase.saveVehicle(truck);
|
||||
|
||||
vehicleDatabase.printAllVehicles();
|
||||
|
||||
Vehicle retrievedCar = vehicleDatabase.getVehicle(car.getId());
|
||||
Vehicle retrievedTruck = vehicleDatabase.getVehicle(truck.getId());
|
||||
|
||||
assertNotNull(retrievedCar);
|
||||
assertNotNull(retrievedTruck);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the constructor of {@link Car} with valid values.
|
||||
*/
|
||||
@Test
|
||||
void testCarConstructor() {
|
||||
Car car = new Car(2020, "Toyota", "Corolla", 4, 1);
|
||||
assertEquals(2020, car.getYear());
|
||||
assertEquals("Toyota", car.getMake());
|
||||
assertEquals("Corolla", car.getModel());
|
||||
assertEquals(4, car.getNumDoors());
|
||||
assertEquals(1, car.getId()); // Assuming the ID is auto-generated in the constructor
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the constructor of {@link Car} with invalid number of doors (negative value).
|
||||
*/
|
||||
@Test
|
||||
void testCarConstructorWithInvalidNumDoors() {
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
new Car(2020, "Toyota", "Corolla", -4, 1);
|
||||
});
|
||||
assertEquals("Number of doors must be positive.", exception.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the constructor of {@link Car} with zero doors.
|
||||
*/
|
||||
@Test
|
||||
void testCarConstructorWithZeroDoors() {
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
new Car(2020, "Toyota", "Corolla", 0, 1);
|
||||
});
|
||||
assertEquals("Number of doors must be positive.", exception.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the constructor of {@link Truck} with invalid load capacity (negative value).
|
||||
*/
|
||||
@Test
|
||||
void testTruckConstructorWithInvalidLoadCapacity() {
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
new Truck(2018, "Ford", "F-150", -60, 2);
|
||||
});
|
||||
assertEquals("Load capacity must be positive.", exception.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the constructor of {@link Truck} with zero load capacity.
|
||||
*/
|
||||
@Test
|
||||
void testTruckConstructorWithZeroLoadCapacity() {
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
new Truck(2018, "Ford", "F-150", 0, 2);
|
||||
});
|
||||
assertEquals("Load capacity must be positive.", exception.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests setting invalid number of doors in {@link Car} using setter (negative value).
|
||||
*/
|
||||
@Test
|
||||
void testSetInvalidNumDoors() {
|
||||
Car car = new Car(2020, "Toyota", "Corolla", 4, 1);
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
car.setNumDoors(-2);
|
||||
});
|
||||
assertEquals("Number of doors must be positive.", exception.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests setting invalid load capacity in {@link Truck} using setter (negative value).
|
||||
*/
|
||||
@Test
|
||||
void testSetInvalidLoadCapacity() {
|
||||
Truck truck = new Truck(2018, "Ford", "F-150", 60, 2);
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
truck.setLoadCapacity(-10);
|
||||
});
|
||||
assertEquals("Load capacity must be positive.", exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user