Skip to content

Commit 04ab0a7

Browse files
Mayur KadamMayur Kadam
authored andcommitted
Added Null Object Pattern & updated README.MD file.
1 parent 4fda8e9 commit 04ab0a7

File tree

9 files changed

+205
-0
lines changed

9 files changed

+205
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Vehicle } from "./Vehicle";
2+
3+
class Bicycle extends Vehicle {
4+
isEngine(): boolean {
5+
return false;
6+
}
7+
8+
isElectric(): boolean {
9+
return true;
10+
}
11+
12+
getEngineCount(): number {
13+
return 0;
14+
}
15+
}
16+
17+
export { Bicycle };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Vehicle } from "./Vehicle";
2+
3+
class Bike extends Vehicle {
4+
isEngine(): boolean {
5+
return true;
6+
}
7+
8+
isElectric(): boolean {
9+
return true;
10+
}
11+
12+
getEngineCount(): number {
13+
return 1;
14+
}
15+
}
16+
17+
export { Bike };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Vehicle } from "./Vehicle";
2+
3+
class Car extends Vehicle {
4+
isEngine(): boolean {
5+
return true;
6+
}
7+
8+
isElectric(): boolean {
9+
return false;
10+
}
11+
12+
getEngineCount(): number {
13+
return 1;
14+
}
15+
}
16+
17+
export { Car };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Vehicle } from "./Vehicle";
2+
3+
class NullVehicle extends Vehicle {
4+
private static instance: NullVehicle | null = null; // Explicitly declare static property
5+
6+
isEngine(): boolean {
7+
return false;
8+
}
9+
isElectric(): boolean {
10+
return false;
11+
}
12+
getEngineCount(): number {
13+
return 0;
14+
}
15+
16+
// OPTIONAL BELOW METHOD : Since NullVehicle does not store any state, we can make it a singleton to avoid creating multiple redundant instances.
17+
static getInstance(): NullVehicle {
18+
if (!NullVehicle.instance) {
19+
NullVehicle.instance = new NullVehicle();
20+
}
21+
return NullVehicle.instance;
22+
}
23+
}
24+
25+
export { NullVehicle };
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Null Object Pattern - Vehicle System
2+
3+
## Overview
4+
This project demonstrates the **Null Object Pattern** using a Vehicle system in TypeScript. The pattern is used to avoid `null` checks by providing a default behavior for cases where an object is expected but not available. Instead of returning `null`, a **NullVehicle** class is used to handle such cases gracefully.
5+
6+
## Design Pattern Explanation
7+
The **Null Object Pattern** is a behavioral design pattern that provides an object as a surrogate for the absence of a real object. It helps eliminate `null` checks and improves code maintainability.
8+
9+
## Project Structure
10+
```
11+
├── Bicycle.ts
12+
├── Bike.ts
13+
├── Car.ts
14+
├── NullVehicle.ts
15+
├── Vehicle.ts
16+
├── VehicleFactory.ts
17+
└── index.ts
18+
```
19+
20+
### **1. Vehicle Base Class**
21+
`Vehicle.ts` defines an abstract class that serves as the base for all vehicle types. It includes methods that each concrete vehicle type must implement.
22+
```typescript
23+
abstract class Vehicle {
24+
abstract isEngine(): boolean;
25+
abstract isElectric(): boolean;
26+
abstract getEngineCount(): number;
27+
}
28+
```
29+
30+
### **2. Concrete Vehicle Implementations**
31+
- `Car.ts`, `Bike.ts`, and `Bicycle.ts` implement the `Vehicle` class and provide real behavior.
32+
- Each concrete class returns values specific to its type.
33+
34+
### **3. Null Object Implementation**
35+
- `NullVehicle.ts` is a subclass of `Vehicle` that provides default values instead of returning `null`.
36+
```typescript
37+
class NullVehicle extends Vehicle {
38+
isEngine(): boolean { return false; }
39+
isElectric(): boolean { return false; }
40+
getEngineCount(): number { return 0; }
41+
}
42+
```
43+
44+
### **4. Factory for Creating Vehicles**
45+
- `VehicleFactory.ts` returns an instance of a requested vehicle type.
46+
- If an invalid vehicle type is requested, it returns an instance of `NullVehicle`.
47+
```typescript
48+
class VehicleFactory {
49+
static getVehicleObject(vehicleType: string): Vehicle {
50+
switch (vehicleType) {
51+
case "Car": return new Car();
52+
case "Bike": return new Bike();
53+
case "Bicycle": return new Bicycle();
54+
default: return new NullVehicle();
55+
}
56+
}
57+
}
58+
```
59+
60+
### **5. Usage Example**
61+
- `index.ts` demonstrates how the factory is used to create vehicles.
62+
```typescript
63+
const car = VehicleFactory.getVehicleObject("Car");
64+
console.log(car.isEngine()); // true
65+
66+
const unknownVehicle = VehicleFactory.getVehicleObject("Truck");
67+
console.log(unknownVehicle.isEngine()); // false (handled by NullVehicle)
68+
```
69+
70+
## Benefits of Using the Null Object Pattern
71+
**Removes null checks**: No need for repeated `if (vehicle !== null)` checks.
72+
**Improves code maintainability**: Cleaner and more readable code.
73+
**Prevents null pointer errors**: Avoids runtime crashes due to unexpected `null` values. Here in TypeScript it will be undefined value.
74+
**Encapsulates default behavior**: Defines expected behavior for missing objects in one place.
75+
76+
## When to Use
77+
- When handling missing or optional objects in a system.
78+
- When you want to avoid excessive null checks.
79+
- When returning `null` would require additional logic in multiple places.
80+
81+
## Conclusion
82+
This project demonstrates how the **Null Object Pattern** improves code readability and reliability by replacing `null` with a default, non-functional object (`NullVehicle`). This ensures the system remains stable even when an expected object is missing.
83+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
abstract class Vehicle {
2+
abstract isEngine(): boolean;
3+
abstract isElectric(): boolean;
4+
abstract getEngineCount(): number;
5+
}
6+
7+
export { Vehicle };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Bicycle } from "./Bicycle";
2+
import { Bike } from "./Bike";
3+
import { Car } from "./Car";
4+
import { Vehicle } from "./Vehicle";
5+
import { NullVehicle } from "./NullVehicle";
6+
7+
class VehicleFactory {
8+
static getVehicleObject(vehicleType: string): Vehicle {
9+
switch (vehicleType) {
10+
case "Car":
11+
return new Car();
12+
case "Bike":
13+
return new Bike();
14+
case "Bicycle":
15+
return new Bicycle();
16+
default:
17+
console.warn(`Warning: ${vehicleType} is not a valid vehicle type. Returning NullVehicle.`);
18+
return NullVehicle.getInstance();
19+
}
20+
}
21+
}
22+
23+
export { VehicleFactory };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { VehicleFactory } from "./VehicleFactory";
2+
3+
const myVehicle = VehicleFactory.getVehicleObject("Bicycle");
4+
5+
console.log(myVehicle.isEngine()); // false
6+
console.log(myVehicle.isElectric()); // true
7+
console.log(myVehicle.getEngineCount()); // 0
8+
console.log()
9+
10+
11+
const myNewVehicle = VehicleFactory.getVehicleObject("Lambo-Car");
12+
13+
console.log(myNewVehicle.isEngine()); // false - default value return
14+
console.log(myNewVehicle.isElectric()); // false - default value return
15+
console.log(myNewVehicle.getEngineCount()); // 0 - default value return

readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ Understand system design principles and implement design patterns.
217217
<summary>Behavioral Design Patterns</summary>
218218

219219
- [Chain of Responsibility (Logging System)](./05%20-%20Low%20Level%20Design/03%20-%20TypeScript%20Design%20Pattern/Behavioral%20Design%20Patterns/Chain%20of%20Responsibility/%20Logging%20System/README.md)
220+
- [Null Object Pattern (Vehicle System)](./05%20-%20Low%20Level%20Design/03%20-%20TypeScript%20Design%20Pattern/Behavioral%20Design%20Patterns/Null%20Object%20Pattern/Vehicle%20System/README.md)
220221
</details>
221222

222223
<details>

0 commit comments

Comments
 (0)