Design a parking lot using object-oriented principles

Muhammad Salem - Jul 14 - - Dev Community

System requirements:

  1. Multi-Floor Structure:

    • The parking lot should have multiple floors.
    • Each floor should have a unique identifier (e.g., Floor 1, Floor 2, etc.).
    • The system should keep track of available spots on each floor.
  2. Entry and Exit Points:

    • Multiple entry and exit points should be supported.
    • Each entry/exit point should have a unique identifier.
    • Entry points should be able to issue parking tickets.
    • Exit points should be able to process payments and allow vehicles to leave.
  3. Parking Tickets:

    • Tickets should be issued at entry points.
    • Each ticket should have a unique identifier.
    • Tickets should record entry time and date.
    • Tickets should be linked to the specific vehicle.
  4. Payment System:

    • Support both automated exit panels and human parking attendants.
    • Accept both cash and credit card payments.
    • Allow for payment at info portals on each floor.
    • Once paid, the ticket should be marked as "paid" to allow exit without additional payment.
  5. Capacity Management:

    • Track the total capacity of the parking lot.
    • Prevent entry when the lot is full.
    • Display "Full" message at entrance panels and on ground floor display board when at capacity.
  6. Parking Spot Types:

    • Support multiple types: Compact, Large, Handicapped, Motorcycle, Electric.
    • Each floor should have a mix of different spot types.
    • Track availability of each spot type separately.
  7. Electric Vehicle Charging:

    • Designate some spots for electric vehicles.
    • These spots should have charging panels.
    • Allow payment for both parking and charging at these spots.
  8. Vehicle Types:

    • Support different vehicle types: Car, Truck, Van, Motorcycle, etc.
    • Match vehicle types to appropriate spot types.
  9. Display Boards:

    • Each floor should have a display board.
    • Display boards should show free spots for each spot type on that floor.
  10. Pricing Model:

    • Implement a per-hour fee structure.
    • First hour: $4
    • Second and third hours: $3.5 each
    • All subsequent hours: $2.5 each
    • The system should be flexible to allow easy changes to this pricing model.
  11. Admin Functionality:

    • Allow adding, removing, and modifying parking floors.
    • Allow adding, removing, and modifying parking spots.
    • Allow adding and removing parking attendants.
    • Allow modifying pricing structures.
  12. Parking Attendant Functionality:

    • Perform all customer actions on behalf of customers.
    • Handle cash payments.
  13. System Functionality:

    • Display messages on info panels.
    • Assign and remove vehicles from parking spots.
    • Calculate parking duration and fees.

Object-Oriented Design for a Multi-Entrance and Exit Parking Lot System

Problem Analysis and Requirements Gathering

The parking lot system needs to handle various types of vehicles, different parking spot types, multiple floors, multiple payment methods, and dynamic pricing models. The system should also manage entry and exit points, display available parking spots, and support electric vehicle charging.

Key Components and Classes

To design this system, we need to identify the key entities and their relationships. Here's an overview of the main classes and their responsibilities:

  1. ParkingLot: Manages the overall parking lot, including floors, entry, and exit points.
  2. ParkingFloor: Represents a floor in the parking lot, containing multiple parking spots and a display board.
  3. ParkingSpot: Represents a single parking spot, which can be of various types (Compact, Large, Handicapped, Motorcycle, Electric).
  4. Vehicle: Represents a vehicle, which can be a car, truck, van, motorcycle, etc.
  5. Ticket: Represents a parking ticket issued to a vehicle upon entry.
  6. Payment: Manages payment transactions, which can be through cash, credit card, or other methods.
  7. DisplayBoard: Shows available parking spots on each floor.
  8. EntrancePanel: Issues parking tickets at the entry points.
  9. ExitPanel: Processes payments and exits at the exit points.
  10. Admin: Manages the parking lot configuration, including adding/removing floors, spots, and attendants.
  11. Customer: Represents a customer using the parking lot.
  12. ParkingAttendant: Represents an attendant who assists customers.

Class Implementations

1. ParkingLot Class

public class ParkingLot
{
    private List<ParkingFloor> floors;
    private List<EntrancePanel> entrances;
    private List<ExitPanel> exits;

    public ParkingLot()
    {
        floors = new List<ParkingFloor>();
        entrances = new List<EntrancePanel>();
        exits = new List<ExitPanel>();
    }

    public void AddFloor(ParkingFloor floor)
    {
        floors.Add(floor);
    }

    public void RemoveFloor(ParkingFloor floor)
    {
        floors.Remove(floor);
    }

    public void AddEntrance(EntrancePanel entrance)
    {
        entrances.Add(entrance);
    }

    public void AddExit(ExitPanel exit)
    {
        exits.Add(exit);
    }
}
Enter fullscreen mode Exit fullscreen mode

2. ParkingFloor Class

public class ParkingFloor
{
    public int FloorNumber { get; private set; }
    private List<ParkingSpot> spots;
    public DisplayBoard DisplayBoard { get; private set; }

    public ParkingFloor(int number)
    {
        FloorNumber = number;
        spots = new List<ParkingSpot>();
        DisplayBoard = new DisplayBoard();
    }

    public void AddParkingSpot(ParkingSpot spot)
    {
        spots.Add(spot);
        DisplayBoard.Update(spots);
    }

    public void RemoveParkingSpot(ParkingSpot spot)
    {
        spots.Remove(spot);
        DisplayBoard.Update(spots);
    }
}
Enter fullscreen mode Exit fullscreen mode

3. ParkingSpot Class

public class ParkingSpot
{
    public int Id { get; private set; }
    public SpotType Type { get; private set; }
    public bool IsOccupied { get; private set; }

    public ParkingSpot(int id, SpotType type)
    {
        Id = id;
        Type = type;
        IsOccupied = false;
    }

    public void ParkVehicle()
    {
        IsOccupied = true;
    }

    public void RemoveVehicle()
    {
        IsOccupied = false;
    }
}

public enum SpotType
{
    Compact,
    Large,
    Handicapped,
    Motorcycle,
    Electric
}
Enter fullscreen mode Exit fullscreen mode

4. Vehicle Class

public class Vehicle
{
    public string LicensePlate { get; private set; }
    public VehicleType Type { get; private set; }

    public Vehicle(string licensePlate, VehicleType type)
    {
        LicensePlate = licensePlate;
        Type = type;
    }
}

public enum VehicleType
{
    Car,
    Truck,
    Van,
    Motorcycle
}
Enter fullscreen mode Exit fullscreen mode

5. Ticket Class

public class Ticket
{
    public DateTime IssueTime { get; private set; }
    public DateTime ExitTime { get; private set; }
    public ParkingSpot Spot { get; private set; }

    public Ticket(ParkingSpot spot)
    {
        IssueTime = DateTime.Now;
        Spot = spot;
    }

    public void CloseTicket()
    {
        ExitTime = DateTime.Now;
    }
}
Enter fullscreen mode Exit fullscreen mode

6. Payment Class

public class Payment
{
    public decimal Amount { get; private set; }
    public PaymentMethod Method { get; private set; }

    public Payment(decimal amount, PaymentMethod method)
    {
        Amount = amount;
        Method = method;
    }
}

public enum PaymentMethod
{
    Cash,
    CreditCard,
    Coupon
}
Enter fullscreen mode Exit fullscreen mode

7. DisplayBoard Class

public class DisplayBoard
{
    public void Update(List<ParkingSpot> spots)
    {
        // Update display logic to show available spots
    }
}
Enter fullscreen mode Exit fullscreen mode

8. EntrancePanel Class

public class EntrancePanel
{
    public Ticket IssueTicket(ParkingSpot spot)
    {
        return new Ticket(spot);
    }
}
Enter fullscreen mode Exit fullscreen mode

9. ExitPanel Class

public class ExitPanel
{
    public void ProcessPayment(Ticket ticket, Payment payment)
    {
        ticket.CloseTicket();
        // Process payment logic
    }
}
Enter fullscreen mode Exit fullscreen mode

10. Admin Class

public class Admin
{
    private ParkingLot parkingLot;

    public Admin(ParkingLot lot)
    {
        parkingLot = lot;
    }

    public void AddFloor(ParkingFloor floor)
    {
        parkingLot.AddFloor(floor);
    }

    public void RemoveFloor(ParkingFloor floor)
    {
        parkingLot.RemoveFloor(floor);
    }

    public void AddParkingSpot(ParkingFloor floor, ParkingSpot spot)
    {
        floor.AddParkingSpot(spot);
    }

    public void RemoveParkingSpot(ParkingFloor floor, ParkingSpot spot)
    {
        floor.RemoveParkingSpot(spot);
    }
}
Enter fullscreen mode Exit fullscreen mode

11. Customer Class

public class Customer
{
    public Ticket GetTicket(EntrancePanel entrance, ParkingSpot spot)
    {
        return entrance.IssueTicket(spot);
    }

    public void PayFee(ExitPanel exit, Ticket ticket, Payment payment)
    {
        exit.ProcessPayment(ticket, payment);
    }
}
Enter fullscreen mode Exit fullscreen mode

12. ParkingAttendant Class

public class ParkingAttendant
{
    public void AssistCustomer(Customer customer, EntrancePanel entrance, ParkingSpot spot)
    {
        Ticket ticket = customer.GetTicket(entrance, spot);
        // Additional assistance logic
    }
}
Enter fullscreen mode Exit fullscreen mode

Reasoning Behind the Design

  • Separation of Concerns: Each class is responsible for a single part of the system, making it easier to manage and extend.
  • Encapsulation: Data and methods are encapsulated within classes, preventing unintended interference.
  • Flexibility: The design can easily accommodate new requirements, such as additional payment methods or new types of parking spots.
  • Maintainability: With clear class responsibilities, the system is easier to maintain and troubleshoot.
  • Extensibility: The system can be extended with new features, such as different pricing models or additional vehicle types, without major changes to the existing codebase

.

This design covers the main aspects of a multi-floor, multi-entrance and exit parking lot system, ensuring it meets the outlined requirements while adhering to object-oriented design principles.

Areas for Improvement:

  1. Interface Segregation: We should consider creating interfaces for some of our classes. For example, IPaymentProcessor could be implemented by both ExitPanel and a separate class for in-floor payment kiosks.

  2. Pricing Model: The current design doesn't explicitly handle the complex pricing model described in the requirements. Consider adding a PricingStrategy class that can calculate fees based on parking duration.

  3. Electric Vehicle Charging: While you've included an Electric spot type, the special functionality for EV charging isn't fully implemented. Consider adding an ElectricChargingSpot class that extends ParkingSpot.

  4. Capacity Management: The design could benefit from a method to check if the parking lot is full and prevent new entries when at capacity.

  5. Error Handling: Consider adding exception handling for scenarios like trying to park in an occupied spot or removing a non-existent floor.

  6. Persistence: The current design doesn't address how data will be stored persistently. Consider adding a data access layer or using the repository pattern.

  7. Parking Duration Calculation: Add a method in the Ticket class to calculate parking duration and cost.

  8. Observer Pattern: Implement the observer pattern for the DisplayBoard to automatically update when ParkingSpot statuses change.

  9. Factory Pattern: Consider using a factory pattern for creating different types of ParkingSpots and Vehicles.

  10. Singleton Pattern: The ParkingLot class could potentially be a singleton, ensuring only one instance exists.

Revised Object-Oriented Design for the Parking Lot System

1. Interface Segregation

We will introduce interfaces for payment processing and other relevant components to adhere to the Interface Segregation Principle (ISP).

2. Pricing Model

We will add a PricingStrategy class to handle different pricing models based on parking duration.

3. Electric Vehicle Charging

We will create an ElectricChargingSpot class that extends ParkingSpot to handle EV charging functionalities.

4. Capacity Management

We will add methods to check parking lot capacity and prevent new entries when the lot is full.

5. Error Handling

We will add exception handling for various scenarios such as parking in an occupied spot or removing a non-existent floor.

6. Persistence

We will outline a data access layer using the repository pattern to handle persistent storage.

7. Parking Duration Calculation

We will add methods in the Ticket class to calculate parking duration and cost.

8. Observer Pattern

We will implement the observer pattern for the DisplayBoard to automatically update when ParkingSpot statuses change.

9. Factory Pattern

We will use the factory pattern for creating different types of ParkingSpots and Vehicles.

10. Singleton Pattern

We will make the ParkingLot class a singleton to ensure only one instance exists.

Revised Class Implementations

1. Interfaces

public interface IPaymentProcessor
{
    void ProcessPayment(Ticket ticket, Payment payment);
}

public interface IDisplay
{
    void Update(List<ParkingSpot> spots);
}
Enter fullscreen mode Exit fullscreen mode

2. PricingStrategy Class

public abstract class PricingStrategy
{
    public abstract decimal CalculatePrice(TimeSpan duration);
}

public class HourlyPricingStrategy : PricingStrategy
{
    public override decimal CalculatePrice(TimeSpan duration)
    {
        // Pricing logic here
        // Example: $4 for the first hour, $3.5 for the second and third hours, $2.5 for subsequent hours
    }
}
Enter fullscreen mode Exit fullscreen mode

3. ElectricChargingSpot Class

public class ElectricChargingSpot : ParkingSpot
{
    public ElectricChargingSpot(int id) : base(id, SpotType.Electric)
    {
    }

    public void ChargeVehicle()
    {
        // Charging logic here
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Capacity Management and Singleton Pattern

public class ParkingLot
{
    private static ParkingLot instance;
    private List<ParkingFloor> floors;
    private List<EntrancePanel> entrances;
    private List<ExitPanel> exits;
    private int capacity;

    private ParkingLot()
    {
        floors = new List<ParkingFloor>();
        entrances = new List<EntrancePanel>();
        exits = new List<ExitPanel>();
        capacity = 0; // Initialize capacity
    }

    public static ParkingLot GetInstance()
    {
        if (instance == null)
        {
            instance = new ParkingLot();
        }
        return instance;
    }

    public void AddFloor(ParkingFloor floor)
    {
        floors.Add(floor);
        capacity += floor.GetTotalSpots(); // Update capacity
    }

    public void RemoveFloor(ParkingFloor floor)
    {
        floors.Remove(floor);
        capacity -= floor.GetTotalSpots(); // Update capacity
    }

    public bool IsFull()
    {
        return GetOccupiedSpots() >= capacity;
    }

    private int GetOccupiedSpots()
    {
        return floors.Sum(floor => floor.GetOccupiedSpots());
    }
}
Enter fullscreen mode Exit fullscreen mode

5. Error Handling

public class ParkingSpot
{
    // Existing properties

    public void ParkVehicle()
    {
        if (IsOccupied)
        {
            throw new InvalidOperationException("Parking spot is already occupied.");
        }
        IsOccupied = true;
    }

    public void RemoveVehicle()
    {
        if (!IsOccupied)
        {
            throw new InvalidOperationException("Parking spot is not occupied.");
        }
        IsOccupied = false;
    }
}
Enter fullscreen mode Exit fullscreen mode

6. Persistence with Repository Pattern

public interface IParkingRepository
{
    void SaveTicket(Ticket ticket);
    Ticket GetTicket(int id);
    // Other CRUD operations
}

public class ParkingRepository : IParkingRepository
{
    public void SaveTicket(Ticket ticket)
    {
        // Save ticket to database
    }

    public Ticket GetTicket(int id)
    {
        // Retrieve ticket from database
    }
}
Enter fullscreen mode Exit fullscreen mode

7. Parking Duration Calculation

public class Ticket
{
    public DateTime IssueTime { get; private set; }
    public DateTime ExitTime { get; private set; }
    public ParkingSpot Spot { get; private set; }

    public Ticket(ParkingSpot spot)
    {
        IssueTime = DateTime.Now;
        Spot = spot;
    }

    public void CloseTicket()
    {
        ExitTime = DateTime.Now;
    }

    public TimeSpan GetParkingDuration()
    {
        return ExitTime - IssueTime;
    }

    public decimal CalculateCost(PricingStrategy pricingStrategy)
    {
        return pricingStrategy.CalculatePrice(GetParkingDuration());
    }
}
Enter fullscreen mode Exit fullscreen mode

8. Observer Pattern for DisplayBoard

public class DisplayBoard : IDisplay
{
    public void Update(List<ParkingSpot> spots)
    {
        // Update display logic to show available spots
    }
}

public class ParkingFloor
{
    private List<ParkingSpot> spots;
    private List<IDisplay> displays;

    public ParkingFloor()
    {
        spots = new List<ParkingSpot>();
        displays = new List<IDisplay>();
    }

    public void AddDisplay(IDisplay display)
    {
        displays.Add(display);
    }

    public void AddParkingSpot(ParkingSpot spot)
    {
        spots.Add(spot);
        NotifyDisplays();
    }

    public void RemoveParkingSpot(ParkingSpot spot)
    {
        spots.Remove(spot);
        NotifyDisplays();
    }

    private void NotifyDisplays()
    {
        foreach (var display in displays)
        {
            display.Update(spots);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

9. Factory Pattern

public class ParkingSpotFactory
{
    public static ParkingSpot CreateParkingSpot(int id, SpotType type)
    {
        switch (type)
        {
            case SpotType.Compact:
            case SpotType.Large:
            case SpotType.Handicapped:
            case SpotType.Motorcycle:
                return new ParkingSpot(id, type);
            case SpotType.Electric:
                return new ElectricChargingSpot(id);
            default:
                throw new ArgumentException("Invalid parking spot type.");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

10. Singleton Pattern for ParkingLot Class

public class ParkingLot
{
    private static readonly Lazy<ParkingLot> instance = new Lazy<ParkingLot>(() => new ParkingLot());
    private List<ParkingFloor> floors;
    private List<EntrancePanel> entrances;
    private List<ExitPanel> exits;
    private int capacity;

    private ParkingLot()
    {
        floors = new List<ParkingFloor>();
        entrances = new List<EntrancePanel>();
        exits = new List<ExitPanel>();
        capacity = 0;
    }

    public static ParkingLot Instance => instance.Value;

    // Other methods...
}
Enter fullscreen mode Exit fullscreen mode

Summary of Revisions

  • Interface Segregation: Introduced IPaymentProcessor and IDisplay interfaces.
  • Pricing Model: Added PricingStrategy and HourlyPricingStrategy classes.
  • Electric Vehicle Charging: Introduced ElectricChargingSpot class.
  • Capacity Management: Added capacity management methods in ParkingLot.
  • Error Handling: Included error handling in ParkingSpot.
  • Persistence: Defined IParkingRepository and ParkingRepository.
  • Parking Duration Calculation: Added duration calculation and cost calculation methods in Ticket.
  • Observer Pattern: Implemented observer pattern for DisplayBoard.
  • Factory Pattern: Used factory pattern for creating ParkingSpot and Vehicle objects.
  • Singleton Pattern: Made ParkingLot a singleton.

These revisions enhance the design, making it more modular, maintainable, and scalable, while adhering to object-oriented design principles and best practices.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player