Contract and API Definitions
Introduction: Why Contracts Matter
Section titled “Introduction: Why Contracts Matter”Contracts define how classes interact - they’re the agreements between components. Well-defined contracts lead to:
- ✔ Testable code - Clear interfaces to mock
- ✔ Flexible design - Can swap implementations
- ✔ Clear documentation - Self-documenting code
- ✔ Error prevention - Compile-time checks
Visual: The Power of Contracts
Section titled “Visual: The Power of Contracts”Part 1: Understanding Contracts
Section titled “Part 1: Understanding Contracts”What Are Contracts?
Section titled “What Are Contracts?”A contract defines:
- What a class/method does
- Inputs - Parameters and types
- Outputs - Return types
- Exceptions - What can go wrong
- Preconditions - What must be true before
- Postconditions - What will be true after
Types of Contracts
Section titled “Types of Contracts”- Interface Contracts - Abstract interfaces
- Method Contracts - Method signatures
- API Contracts - REST/HTTP APIs
- Class Contracts - Class responsibilities
Visual: Contract Types
Section titled “Visual: Contract Types”Part 2: Interface Contracts
Section titled “Part 2: Interface Contracts”What Are Interface Contracts?
Section titled “What Are Interface Contracts?”Interface contracts define what a class must implement without specifying how.
Example: Payment Processor Interface
Section titled “Example: Payment Processor Interface”from abc import ABC, abstractmethodfrom typing import Dict, Any
class PaymentProcessor(ABC): """ Interface for payment processing.
Contract: - Must process payments - Must handle refunds - Must validate payment methods """
@abstractmethod def process_payment(self, amount: float, payment_method: str, payment_data: Dict[str, Any]) -> Dict[str, Any]: """ Process a payment.
Args: amount: Payment amount (must be > 0) payment_method: Payment method (e.g., "credit_card", "paypal") payment_data: Payment-specific data (card token, email, etc.)
Returns: Dict containing: - status: "success" or "failed" - transaction_id: Unique transaction ID - message: Optional message
Raises: ValueError: If amount <= 0 or invalid payment_data PaymentFailedException: If payment processing fails """ pass
@abstractmethod def refund_payment(self, transaction_id: str, amount: float) -> Dict[str, Any]: """ Refund a payment.
Args: transaction_id: Original transaction ID amount: Refund amount (must be > 0 and <= original amount)
Returns: Dict containing: - status: "success" or "failed" - refund_id: Unique refund ID
Raises: ValueError: If amount <= 0 or transaction_id invalid RefundFailedException: If refund fails """ pass
@abstractmethod def validate_payment_method(self, payment_method: str) -> bool: """ Validate if payment method is supported.
Args: payment_method: Payment method to validate
Returns: True if supported, False otherwise """ passimport java.util.Map;
/** * Interface for payment processing. * * Contract: * - Must process payments * - Must handle refunds * - Must validate payment methods */public interface PaymentProcessor { /** * Process a payment. * * @param amount Payment amount (must be > 0) * @param paymentMethod Payment method (e.g., "credit_card", "paypal") * @param paymentData Payment-specific data (card token, email, etc.) * @return Map containing status, transaction_id, and optional message * @throws IllegalArgumentException If amount <= 0 or invalid payment_data * @throws PaymentFailedException If payment processing fails */ Map<String, Object> processPayment(double amount, String paymentMethod, Map<String, Object> paymentData);
/** * Refund a payment. * * @param transactionId Original transaction ID * @param amount Refund amount (must be > 0 and <= original amount) * @return Map containing status and refund_id * @throws IllegalArgumentException If amount <= 0 or transaction_id invalid * @throws RefundFailedException If refund fails */ Map<String, Object> refundPayment(String transactionId, double amount);
/** * Validate if payment method is supported. * * @param paymentMethod Payment method to validate * @return True if supported, False otherwise */ boolean validatePaymentMethod(String paymentMethod);}/** * Interface for payment processing. * * Contract: * - Must process payments * - Must handle refunds * - Must validate payment methods */export interface PaymentProcessor { /** * Process a payment. * * @param amount Payment amount (must be > 0) * @param paymentMethod Payment method (e.g., "credit_card", "paypal") * @param paymentData Payment-specific data (card token, email, etc.) * @returns Map containing status, transaction_id, and optional message * @throws Error If amount <= 0 or invalid payment_data * @throws PaymentFailedException If payment processing fails */ processPayment( amount: number, paymentMethod: string, paymentData: Record<string, any> ): Record<string, any>;
/** * Refund a payment. * * @param transactionId Original transaction ID * @param amount Refund amount (must be > 0 and <= original amount) * @returns Map containing status and refund_id * @throws Error If amount <= 0 or transaction_id invalid * @throws RefundFailedException If refund fails */ refundPayment(transactionId: string, amount: number): Record<string, any>;
/** * Validate if payment method is supported. * * @param paymentMethod Payment method to validate * @returns True if supported, False otherwise */ validatePaymentMethod(paymentMethod: string): boolean;}#include <string>#include <map>#include <any>
/** * Interface for payment processing. * * Contract: * - Must process payments * - Must handle refunds * - Must validate payment methods */class PaymentProcessor {public: virtual ~PaymentProcessor() = default;
/** * Process a payment. * * @param amount Payment amount (must be > 0) * @param paymentMethod Payment method (e.g., "credit_card", "paypal") * @param paymentData Payment-specific data (card token, email, etc.) * @return Map containing status, transaction_id, and optional message * @throws std::invalid_argument If amount <= 0 or invalid payment_data * @throws PaymentFailedException If payment processing fails */ virtual std::map<std::string, std::any> processPayment( double amount, const std::string& paymentMethod, const std::map<std::string, std::any>& paymentData ) = 0;
/** * Refund a payment. * * @param transactionId Original transaction ID * @param amount Refund amount (must be > 0 and <= original amount) * @return Map containing status and refund_id * @throws std::invalid_argument If amount <= 0 or transaction_id invalid * @throws RefundFailedException If refund fails */ virtual std::map<std::string, std::any> refundPayment( const std::string& transactionId, double amount ) = 0;
/** * Validate if payment method is supported. * * @param paymentMethod Payment method to validate * @return True if supported, False otherwise */ virtual bool validatePaymentMethod(const std::string& paymentMethod) = 0;};using System.Collections.Generic;
/// <summary>/// Interface for payment processing.////// Contract:/// - Must process payments/// - Must handle refunds/// - Must validate payment methods/// </summary>public interface IPaymentProcessor{ /// <summary> /// Process a payment. /// </summary> /// <param name="amount">Payment amount (must be > 0)</param> /// <param name="paymentMethod">Payment method (e.g., "credit_card", "paypal")</param> /// <param name="paymentData">Payment-specific data (card token, email, etc.)</param> /// <returns>Dictionary containing status, transaction_id, and optional message</returns> /// <exception cref="ArgumentException">If amount <= 0 or invalid payment_data</exception> /// <exception cref="PaymentFailedException">If payment processing fails</exception> Dictionary<string, object> ProcessPayment( double amount, string paymentMethod, Dictionary<string, object> paymentData );
/// <summary> /// Refund a payment. /// </summary> /// <param name="transactionId">Original transaction ID</param> /// <param name="amount">Refund amount (must be > 0 and <= original amount)</param> /// <returns>Dictionary containing status and refund_id</returns> /// <exception cref="ArgumentException">If amount <= 0 or transaction_id invalid</exception> /// <exception cref="RefundFailedException">If refund fails</exception> Dictionary<string, object> RefundPayment(string transactionId, double amount);
/// <summary> /// Validate if payment method is supported. /// </summary> /// <param name="paymentMethod">Payment method to validate</param> /// <returns>True if supported, False otherwise</returns> bool ValidatePaymentMethod(string paymentMethod);}Visual: Interface Contract
Section titled “Visual: Interface Contract”Key Elements of Interface Contracts
Section titled “Key Elements of Interface Contracts”- Method signatures - What methods exist
- Parameter types - What inputs are expected
- Return types - What outputs are guaranteed
- Exceptions - What can go wrong
- Documentation - Clear description of behavior
Part 3: Method Contracts
Section titled “Part 3: Method Contracts”What Are Method Contracts?
Section titled “What Are Method Contracts?”Method contracts define the behavior of individual methods - inputs, outputs, and exceptions.
Example: ParkingLot Methods
Section titled “Example: ParkingLot Methods”from typing import Optionalfrom datetime import datetime
class ParkingLot: """ Manages parking operations. """
def park_vehicle(self, vehicle: Vehicle) -> Ticket: """ Parks a vehicle and returns a ticket.
Contract: - Precondition: Vehicle must be valid and parking lot must have available spots - Postcondition: Vehicle is parked, ticket is issued, spot is occupied
Args: vehicle: Vehicle to park (must not be None)
Returns: Ticket: Parking ticket with entry details
Raises: ValueError: If vehicle is None or invalid ParkingLotFullException: If no spots available for vehicle type InvalidVehicleException: If vehicle type not supported
Example: >>> vehicle = Car("ABC123") >>> ticket = parking_lot.park_vehicle(vehicle) >>> print(ticket.ticket_id) "TICKET_12345" """ if vehicle is None: raise ValueError("Vehicle cannot be None")
if not self._is_vehicle_type_supported(vehicle.get_vehicle_type()): raise InvalidVehicleException(f"Vehicle type {vehicle.get_vehicle_type()} not supported")
spot = self._find_available_spot(vehicle.get_vehicle_type()) if spot is None: raise ParkingLotFullException("No available spots")
spot.park_vehicle(vehicle) ticket = Ticket(vehicle, spot, datetime.now()) self._tickets[ticket.ticket_id] = ticket
return ticket
def unpark_vehicle(self, ticket_id: str) -> Payment: """ Unparks a vehicle and processes payment.
Contract: - Precondition: Ticket must be valid and vehicle must be parked - Postcondition: Vehicle is unparked, spot is available, payment is processed
Args: ticket_id: Parking ticket ID (must not be None or empty)
Returns: Payment: Payment transaction details
Raises: ValueError: If ticket_id is None or empty InvalidTicketException: If ticket is invalid or already used VehicleNotParkedException: If vehicle is not currently parked
Example: >>> payment = parking_lot.unpark_vehicle("TICKET_12345") >>> print(payment.amount) 25.50 """ if not ticket_id or ticket_id.strip() == "": raise ValueError("Ticket ID cannot be None or empty")
if ticket_id not in self._tickets: raise InvalidTicketException(f"Ticket {ticket_id} not found")
ticket = self._tickets[ticket_id]
if ticket.is_returned(): raise InvalidTicketException(f"Ticket {ticket_id} already used")
spot = ticket.spot vehicle = spot.unpark_vehicle()
duration = ticket.calculate_duration() amount = self._calculate_payment(duration, vehicle.get_vehicle_type())
payment = Payment(ticket, amount, datetime.now()) ticket.mark_as_returned()
return payment
def find_available_spots(self, vehicle_type: VehicleType) -> List[ParkingSpot]: """ Finds all available spots for a vehicle type.
Contract: - Precondition: Vehicle type must be valid - Postcondition: Returns list of available spots (may be empty)
Args: vehicle_type: Type of vehicle (must not be None)
Returns: List[ParkingSpot]: List of available spots (empty if none available)
Raises: ValueError: If vehicle_type is None
Example: >>> spots = parking_lot.find_available_spots(VehicleType.CAR) >>> print(len(spots)) 5 """ if vehicle_type is None: raise ValueError("Vehicle type cannot be None")
return [spot for spot in self._spots if spot.is_available() and spot.get_type() == vehicle_type]import java.util.List;import java.util.Optional;
public class ParkingLot { /** * Parks a vehicle and returns a ticket. * * Contract: * - Precondition: Vehicle must be valid and parking lot must have available spots * - Postcondition: Vehicle is parked, ticket is issued, spot is occupied * * @param vehicle Vehicle to park (must not be null) * @return Parking ticket with entry details * @throws IllegalArgumentException If vehicle is null or invalid * @throws ParkingLotFullException If no spots available for vehicle type * @throws InvalidVehicleException If vehicle type not supported */ public Ticket parkVehicle(Vehicle vehicle) { if (vehicle == null) { throw new IllegalArgumentException("Vehicle cannot be null"); }
if (!isVehicleTypeSupported(vehicle.getVehicleType())) { throw new InvalidVehicleException("Vehicle type not supported"); }
ParkingSpot spot = findAvailableSpot(vehicle.getVehicleType()) .orElseThrow(() -> new ParkingLotFullException("No available spots"));
spot.parkVehicle(vehicle); Ticket ticket = new Ticket(vehicle, spot, LocalDateTime.now()); tickets.put(ticket.getTicketId(), ticket);
return ticket; }
/** * Unparks a vehicle and processes payment. * * Contract: * - Precondition: Ticket must be valid and vehicle must be parked * - Postcondition: Vehicle is unparked, spot is available, payment is processed * * @param ticketId Parking ticket ID (must not be null or empty) * @return Payment transaction details * @throws IllegalArgumentException If ticket_id is null or empty * @throws InvalidTicketException If ticket is invalid or already used */ public Payment unparkVehicle(String ticketId) { if (ticketId == null || ticketId.trim().isEmpty()) { throw new IllegalArgumentException("Ticket ID cannot be null or empty"); }
Ticket ticket = tickets.get(ticketId); if (ticket == null) { throw new InvalidTicketException("Ticket not found"); }
if (ticket.isReturned()) { throw new InvalidTicketException("Ticket already used"); }
ParkingSpot spot = ticket.getSpot(); Vehicle vehicle = spot.unparkVehicle();
double duration = ticket.calculateDuration(); double amount = calculatePayment(duration, vehicle.getVehicleType());
Payment payment = new Payment(ticket, amount, LocalDateTime.now()); ticket.markAsReturned();
return payment; }
/** * Finds all available spots for a vehicle type. * * Contract: * - Precondition: Vehicle type must be valid * - Postcondition: Returns list of available spots (may be empty) * * @param vehicleType Type of vehicle (must not be null) * @return List of available spots (empty if none available) * @throws IllegalArgumentException If vehicle_type is null */ public List<ParkingSpot> findAvailableSpots(VehicleType vehicleType) { if (vehicleType == null) { throw new IllegalArgumentException("Vehicle type cannot be null"); }
return spots.stream() .filter(spot -> spot.isAvailable() && spot.getType() == vehicleType) .collect(Collectors.toList()); }}import { Vehicle, VehicleType, Ticket, Payment, ParkingSpot } from './types';
class ParkingLot { private tickets: Map<string, Ticket> = new Map(); private spots: ParkingSpot[] = [];
/** * Parks a vehicle and returns a ticket. * * Contract: * - Precondition: Vehicle must be valid and parking lot must have available spots * - Postcondition: Vehicle is parked, ticket is issued, spot is occupied * * @param vehicle Vehicle to park (must not be null) * @returns Parking ticket with entry details * @throws Error If vehicle is null or invalid * @throws ParkingLotFullException If no spots available for vehicle type * @throws InvalidVehicleException If vehicle type not supported */ parkVehicle(vehicle: Vehicle): Ticket { if (!vehicle) { throw new Error("Vehicle cannot be null"); }
if (!this.isVehicleTypeSupported(vehicle.getVehicleType())) { throw new Error("Vehicle type not supported"); }
const spot = this.findAvailableSpot(vehicle.getVehicleType()); if (!spot) { throw new Error("No available spots"); }
spot.parkVehicle(vehicle); const ticket = new Ticket(vehicle, spot, new Date()); this.tickets.set(ticket.getTicketId(), ticket);
return ticket; }
/** * Unparks a vehicle and processes payment. * * Contract: * - Precondition: Ticket must be valid and vehicle must be parked * - Postcondition: Vehicle is unparked, spot is available, payment is processed * * @param ticketId Parking ticket ID (must not be null or empty) * @returns Payment transaction details * @throws Error If ticket_id is null or empty * @throws InvalidTicketException If ticket is invalid or already used */ unparkVehicle(ticketId: string): Payment { if (!ticketId || ticketId.trim().length === 0) { throw new Error("Ticket ID cannot be null or empty"); }
const ticket = this.tickets.get(ticketId); if (!ticket) { throw new Error("Ticket not found"); }
if (ticket.isReturned()) { throw new Error("Ticket already used"); }
const spot = ticket.getSpot(); const vehicle = spot.unparkVehicle();
const duration = ticket.calculateDuration(); const amount = this.calculatePayment(duration, vehicle.getVehicleType());
const payment = new Payment(ticket, amount, new Date()); ticket.markAsReturned();
return payment; }
/** * Finds all available spots for a vehicle type. * * Contract: * - Precondition: Vehicle type must be valid * - Postcondition: Returns list of available spots (may be empty) * * @param vehicleType Type of vehicle (must not be null) * @returns List of available spots (empty if none available) * @throws Error If vehicle_type is null */ findAvailableSpots(vehicleType: VehicleType): ParkingSpot[] { if (!vehicleType) { throw new Error("Vehicle type cannot be null"); }
return this.spots.filter( spot => spot.isAvailable() && spot.getType() === vehicleType ); }
private isVehicleTypeSupported(type: VehicleType): boolean { // Implementation return true; }
private findAvailableSpot(type: VehicleType): ParkingSpot | null { // Implementation return null; }
private calculatePayment(duration: number, type: VehicleType): number { // Implementation return 0; }}#include <string>#include <vector>#include <map>#include <memory>#include <stdexcept>#include <algorithm>
class ParkingLot {private: std::map<std::string, std::shared_ptr<Ticket>> tickets; std::vector<std::shared_ptr<ParkingSpot>> spots;
public: /** * Parks a vehicle and returns a ticket. * * Contract: * - Precondition: Vehicle must be valid and parking lot must have available spots * - Postcondition: Vehicle is parked, ticket is issued, spot is occupied * * @param vehicle Vehicle to park (must not be null) * @return Parking ticket with entry details * @throws std::invalid_argument If vehicle is null or invalid * @throws ParkingLotFullException If no spots available for vehicle type * @throws InvalidVehicleException If vehicle type not supported */ std::shared_ptr<Ticket> parkVehicle(std::shared_ptr<Vehicle> vehicle) { if (!vehicle) { throw std::invalid_argument("Vehicle cannot be null"); }
if (!isVehicleTypeSupported(vehicle->getVehicleType())) { throw std::runtime_error("Vehicle type not supported"); }
auto spot = findAvailableSpot(vehicle->getVehicleType()); if (!spot) { throw std::runtime_error("No available spots"); }
spot->parkVehicle(vehicle); auto ticket = std::make_shared<Ticket>(vehicle, spot, std::time(nullptr)); tickets[ticket->getTicketId()] = ticket;
return ticket; }
/** * Unparks a vehicle and processes payment. * * Contract: * - Precondition: Ticket must be valid and vehicle must be parked * - Postcondition: Vehicle is unparked, spot is available, payment is processed * * @param ticketId Parking ticket ID (must not be null or empty) * @return Payment transaction details * @throws std::invalid_argument If ticket_id is null or empty * @throws InvalidTicketException If ticket is invalid or already used */ std::shared_ptr<Payment> unparkVehicle(const std::string& ticketId) { if (ticketId.empty()) { throw std::invalid_argument("Ticket ID cannot be empty"); }
auto it = tickets.find(ticketId); if (it == tickets.end()) { throw std::runtime_error("Ticket not found"); }
auto ticket = it->second; if (ticket->isReturned()) { throw std::runtime_error("Ticket already used"); }
auto spot = ticket->getSpot(); auto vehicle = spot->unparkVehicle();
double duration = ticket->calculateDuration(); double amount = calculatePayment(duration, vehicle->getVehicleType());
auto payment = std::make_shared<Payment>(ticket, amount, std::time(nullptr)); ticket->markAsReturned();
return payment; }
/** * Finds all available spots for a vehicle type. * * Contract: * - Precondition: Vehicle type must be valid * - Postcondition: Returns list of available spots (may be empty) * * @param vehicleType Type of vehicle * @return List of available spots (empty if none available) */ std::vector<std::shared_ptr<ParkingSpot>> findAvailableSpots(VehicleType vehicleType) { std::vector<std::shared_ptr<ParkingSpot>> availableSpots;
std::copy_if(spots.begin(), spots.end(), std::back_inserter(availableSpots), [vehicleType](const auto& spot) { return spot->isAvailable() && spot->getType() == vehicleType; });
return availableSpots; }
private: bool isVehicleTypeSupported(VehicleType type) { // Implementation return true; }
std::shared_ptr<ParkingSpot> findAvailableSpot(VehicleType type) { // Implementation return nullptr; }
double calculatePayment(double duration, VehicleType type) { // Implementation return 0.0; }};using System;using System.Collections.Generic;using System.Linq;
public class ParkingLot{ private Dictionary<string, Ticket> tickets = new Dictionary<string, Ticket>(); private List<ParkingSpot> spots = new List<ParkingSpot>();
/// <summary> /// Parks a vehicle and returns a ticket. /// /// Contract: /// - Precondition: Vehicle must be valid and parking lot must have available spots /// - Postcondition: Vehicle is parked, ticket is issued, spot is occupied /// </summary> /// <param name="vehicle">Vehicle to park (must not be null)</param> /// <returns>Parking ticket with entry details</returns> /// <exception cref="ArgumentNullException">If vehicle is null</exception> /// <exception cref="ParkingLotFullException">If no spots available for vehicle type</exception> /// <exception cref="InvalidVehicleException">If vehicle type not supported</exception> public Ticket ParkVehicle(Vehicle vehicle) { if (vehicle == null) { throw new ArgumentNullException(nameof(vehicle), "Vehicle cannot be null"); }
if (!IsVehicleTypeSupported(vehicle.GetVehicleType())) { throw new InvalidVehicleException("Vehicle type not supported"); }
var spot = FindAvailableSpot(vehicle.GetVehicleType()); if (spot == null) { throw new ParkingLotFullException("No available spots"); }
spot.ParkVehicle(vehicle); var ticket = new Ticket(vehicle, spot, DateTime.Now); tickets[ticket.TicketId] = ticket;
return ticket; }
/// <summary> /// Unparks a vehicle and processes payment. /// /// Contract: /// - Precondition: Ticket must be valid and vehicle must be parked /// - Postcondition: Vehicle is unparked, spot is available, payment is processed /// </summary> /// <param name="ticketId">Parking ticket ID (must not be null or empty)</param> /// <returns>Payment transaction details</returns> /// <exception cref="ArgumentException">If ticket_id is null or empty</exception> /// <exception cref="InvalidTicketException">If ticket is invalid or already used</exception> public Payment UnparkVehicle(string ticketId) { if (string.IsNullOrWhiteSpace(ticketId)) { throw new ArgumentException("Ticket ID cannot be null or empty", nameof(ticketId)); }
if (!tickets.TryGetValue(ticketId, out var ticket)) { throw new InvalidTicketException("Ticket not found"); }
if (ticket.IsReturned) { throw new InvalidTicketException("Ticket already used"); }
var spot = ticket.Spot; var vehicle = spot.UnparkVehicle();
double duration = ticket.CalculateDuration(); double amount = CalculatePayment(duration, vehicle.GetVehicleType());
var payment = new Payment(ticket, amount, DateTime.Now); ticket.MarkAsReturned();
return payment; }
/// <summary> /// Finds all available spots for a vehicle type. /// /// Contract: /// - Precondition: Vehicle type must be valid /// - Postcondition: Returns list of available spots (may be empty) /// </summary> /// <param name="vehicleType">Type of vehicle</param> /// <returns>List of available spots (empty if none available)</returns> public List<ParkingSpot> FindAvailableSpots(VehicleType vehicleType) { return spots .Where(spot => spot.IsAvailable() && spot.GetType() == vehicleType) .ToList(); }
private bool IsVehicleTypeSupported(VehicleType type) { // Implementation return true; }
private ParkingSpot FindAvailableSpot(VehicleType type) { // Implementation return null; }
private double CalculatePayment(double duration, VehicleType type) { // Implementation return 0; }}Key Elements of Method Contracts
Section titled “Key Elements of Method Contracts”- Method signature - Name, parameters, return type
- Preconditions - What must be true before calling
- Postconditions - What will be true after calling
- Exceptions - What can go wrong
- Documentation - Clear description
Visual: Method Contract Elements
Section titled “Visual: Method Contract Elements”Part 4: API Contracts (REST APIs)
Section titled “Part 4: API Contracts (REST APIs)”What Are API Contracts?
Section titled “What Are API Contracts?”API contracts define how external systems interact with your system via HTTP/REST APIs.
Example: Parking Lot API
Section titled “Example: Parking Lot API”# OpenAPI/Swagger Specification
paths: /api/v1/parking/park: post: summary: Park a vehicle description: Parks a vehicle and returns a parking ticket requestBody: required: true content: application/json: schema: type: object required: - vehicleType - licensePlate properties: vehicleType: type: string enum: [CAR, MOTORCYCLE, TRUCK] description: Type of vehicle licensePlate: type: string minLength: 1 maxLength: 20 description: Vehicle license plate responses: '200': description: Vehicle parked successfully content: application/json: schema: type: object properties: ticketId: type: string example: "TICKET_12345" entryTime: type: string format: date-time spotId: type: string example: "SPOT_A1" '400': description: Bad request (invalid input) content: application/json: schema: type: object properties: error: type: string example: "Invalid vehicle type" '404': description: No available spots content: application/json: schema: type: object properties: error: type: string example: "Parking lot is full"
/api/v1/parking/unpark: post: summary: Unpark a vehicle description: Unparks a vehicle and processes payment requestBody: required: true content: application/json: schema: type: object required: - ticketId properties: ticketId: type: string description: Parking ticket ID responses: '200': description: Vehicle unparked successfully content: application/json: schema: type: object properties: paymentId: type: string example: "PAYMENT_67890" amount: type: number format: float example: 25.50 duration: type: number format: float example: 2.5 '400': description: Bad request '404': description: Ticket not found
/api/v1/parking/spots: get: summary: Get available spots description: Returns available parking spots for a vehicle type parameters: - name: vehicleType in: query required: true schema: type: string enum: [CAR, MOTORCYCLE, TRUCK] responses: '200': description: List of available spots content: application/json: schema: type: object properties: spots: type: array items: type: object properties: spotId: type: string spotType: type: string location: type: stringfrom flask import Flask, request, jsonifyfrom typing import Dict, Any
app = Flask(__name__)
@app.route('/api/v1/parking/park', methods=['POST'])def park_vehicle(): """ API Contract: - Endpoint: POST /api/v1/parking/park - Request Body: { vehicleType: str, licensePlate: str } - Response 200: { ticketId: str, entryTime: str, spotId: str } - Response 400: { error: str } - Bad request - Response 404: { error: str } - No spots available """ data = request.get_json()
# Validate input if not data or 'vehicleType' not in data or 'licensePlate' not in data: return jsonify({'error': 'Missing required fields'}), 400
vehicle_type = data['vehicleType'] license_plate = data['licensePlate']
# Validate vehicle type if vehicle_type not in ['CAR', 'MOTORCYCLE', 'TRUCK']: return jsonify({'error': 'Invalid vehicle type'}), 400
try: # Create vehicle and park vehicle = create_vehicle(vehicle_type, license_plate) ticket = parking_lot.park_vehicle(vehicle)
return jsonify({ 'ticketId': ticket.ticket_id, 'entryTime': ticket.entry_time.isoformat(), 'spotId': ticket.spot.spot_id }), 200
except ParkingLotFullException as e: return jsonify({'error': str(e)}), 404 except Exception as e: return jsonify({'error': str(e)}), 400
@app.route('/api/v1/parking/unpark', methods=['POST'])def unpark_vehicle(): """ API Contract: - Endpoint: POST /api/v1/parking/unpark - Request Body: { ticketId: str } - Response 200: { paymentId: str, amount: float, duration: float } - Response 400: { error: str } - Bad request - Response 404: { error: str } - Ticket not found """ data = request.get_json()
if not data or 'ticketId' not in data: return jsonify({'error': 'Missing ticketId'}), 400
ticket_id = data['ticketId']
try: payment = parking_lot.unpark_vehicle(ticket_id)
return jsonify({ 'paymentId': payment.payment_id, 'amount': payment.amount, 'duration': payment.ticket.calculate_duration() }), 200
except InvalidTicketException as e: return jsonify({'error': str(e)}), 404 except Exception as e: return jsonify({'error': str(e)}), 400
@app.route('/api/v1/parking/spots', methods=['GET'])def get_available_spots(): """ API Contract: - Endpoint: GET /api/v1/parking/spots?vehicleType=CAR - Query Parameter: vehicleType (required) - Response 200: { spots: [{ spotId: str, spotType: str, location: str }] } - Response 400: { error: str } - Invalid vehicle type """ vehicle_type = request.args.get('vehicleType')
if not vehicle_type: return jsonify({'error': 'Missing vehicleType parameter'}), 400
if vehicle_type not in ['CAR', 'MOTORCYCLE', 'TRUCK']: return jsonify({'error': 'Invalid vehicle type'}), 400
spots = parking_lot.find_available_spots(VehicleType[vehicle_type])
return jsonify({ 'spots': [{ 'spotId': spot.spot_id, 'spotType': spot.spot_type.value, 'location': spot.location } for spot in spots] }), 200import org.springframework.web.bind.annotation.*;import org.springframework.http.ResponseEntity;import java.util.*;
@RestController@RequestMapping("/api/v1/parking")public class ParkingController {
@PostMapping("/park") public ResponseEntity<?> parkVehicle(@RequestBody Map<String, String> data) { /* API Contract: - Endpoint: POST /api/v1/parking/park - Request Body: { vehicleType: str, licensePlate: str } - Response 200: { ticketId: str, entryTime: str, spotId: str } - Response 400: { error: str } - Bad request - Response 404: { error: str } - No spots available */
if (data == null || !data.containsKey("vehicleType") || !data.containsKey("licensePlate")) { return ResponseEntity.badRequest().body(Map.of("error", "Missing required fields")); }
String vehicleType = data.get("vehicleType"); String licensePlate = data.get("licensePlate");
// Validate vehicle type if (!isValidVehicleType(vehicleType)) { return ResponseEntity.badRequest().body(Map.of("error", "Invalid vehicle type")); }
try { Vehicle vehicle = createVehicle(vehicleType, licensePlate); Ticket ticket = parkingLot.parkVehicle(vehicle);
Map<String, Object> response = new HashMap<>(); response.put("ticketId", ticket.getTicketId()); response.put("entryTime", ticket.getEntryTime().toString()); response.put("spotId", ticket.getSpot().getSpotId());
return ResponseEntity.ok(response);
} catch (ParkingLotFullException e) { return ResponseEntity.status(404).body(Map.of("error", e.getMessage())); } catch (Exception e) { return ResponseEntity.badRequest().body(Map.of("error", e.getMessage())); } }
@PostMapping("/unpark") public ResponseEntity<?> unparkVehicle(@RequestBody Map<String, String> data) { /* API Contract: - Endpoint: POST /api/v1/parking/unpark - Request Body: { ticketId: str } - Response 200: { paymentId: str, amount: float, duration: float } - Response 400: { error: str } - Bad request - Response 404: { error: str } - Ticket not found */
if (data == null || !data.containsKey("ticketId")) { return ResponseEntity.badRequest().body(Map.of("error", "Missing ticketId")); }
String ticketId = data.get("ticketId");
try { Payment payment = parkingLot.unparkVehicle(ticketId);
Map<String, Object> response = new HashMap<>(); response.put("paymentId", payment.getPaymentId()); response.put("amount", payment.getAmount()); response.put("duration", payment.getTicket().calculateDuration());
return ResponseEntity.ok(response);
} catch (InvalidTicketException e) { return ResponseEntity.status(404).body(Map.of("error", e.getMessage())); } catch (Exception e) { return ResponseEntity.badRequest().body(Map.of("error", e.getMessage())); } }
@GetMapping("/spots") public ResponseEntity<?> getAvailableSpots(@RequestParam String vehicleType) { /* API Contract: - Endpoint: GET /api/v1/parking/spots?vehicleType=CAR - Query Parameter: vehicleType (required) - Response 200: { spots: [{ spotId: str, spotType: str, location: str }] } - Response 400: { error: str } - Invalid vehicle type */
if (vehicleType == null || vehicleType.isEmpty()) { return ResponseEntity.badRequest().body(Map.of("error", "Missing vehicleType parameter")); }
if (!isValidVehicleType(vehicleType)) { return ResponseEntity.badRequest().body(Map.of("error", "Invalid vehicle type")); }
List<ParkingSpot> spots = parkingLot.findAvailableSpots(VehicleType.valueOf(vehicleType));
List<Map<String, String>> spotsList = new ArrayList<>(); for (ParkingSpot spot : spots) { Map<String, String> spotData = new HashMap<>(); spotData.put("spotId", spot.getSpotId()); spotData.put("spotType", spot.getSpotType().toString()); spotData.put("location", spot.getLocation()); spotsList.add(spotData); }
return ResponseEntity.ok(Map.of("spots", spotsList)); }}import express, { Request, Response } from 'express';
const app = express();app.use(express.json());
/** * API Contract: * - Endpoint: POST /api/v1/parking/park * - Request Body: { vehicleType: string, licensePlate: string } * - Response 200: { ticketId: string, entryTime: string, spotId: string } * - Response 400: { error: string } - Bad request * - Response 404: { error: string } - No spots available */app.post('/api/v1/parking/park', (req: Request, res: Response) => { const data = req.body;
// Validate input if (!data || !data.vehicleType || !data.licensePlate) { return res.status(400).json({ error: 'Missing required fields' }); }
const vehicleType = data.vehicleType; const licensePlate = data.licensePlate;
// Validate vehicle type if (!['CAR', 'MOTORCYCLE', 'TRUCK'].includes(vehicleType)) { return res.status(400).json({ error: 'Invalid vehicle type' }); }
try { // Create vehicle and park const vehicle = createVehicle(vehicleType, licensePlate); const ticket = parkingLot.parkVehicle(vehicle);
return res.status(200).json({ ticketId: ticket.ticketId, entryTime: ticket.entryTime.toISOString(), spotId: ticket.spot.spotId }); } catch (e: any) { if (e.message.includes('full')) { return res.status(404).json({ error: e.message }); } return res.status(400).json({ error: e.message }); }});
/** * API Contract: * - Endpoint: POST /api/v1/parking/unpark * - Request Body: { ticketId: string } * - Response 200: { paymentId: string, amount: number, duration: number } * - Response 400: { error: string } - Bad request * - Response 404: { error: string } - Ticket not found */app.post('/api/v1/parking/unpark', (req: Request, res: Response) => { const data = req.body;
if (!data || !data.ticketId) { return res.status(400).json({ error: 'Missing ticketId' }); }
const ticketId = data.ticketId;
try { const payment = parkingLot.unparkVehicle(ticketId);
return res.status(200).json({ paymentId: payment.paymentId, amount: payment.amount, duration: payment.ticket.calculateDuration() }); } catch (e: any) { if (e.message.includes('not found')) { return res.status(404).json({ error: e.message }); } return res.status(400).json({ error: e.message }); }});
/** * API Contract: * - Endpoint: GET /api/v1/parking/spots?vehicleType=CAR * - Query Parameter: vehicleType (required) * - Response 200: { spots: [{ spotId: string, spotType: string, location: string }] } * - Response 400: { error: string } - Invalid vehicle type */app.get('/api/v1/parking/spots', (req: Request, res: Response) => { const vehicleType = req.query.vehicleType as string;
if (!vehicleType) { return res.status(400).json({ error: 'Missing vehicleType parameter' }); }
if (!['CAR', 'MOTORCYCLE', 'TRUCK'].includes(vehicleType)) { return res.status(400).json({ error: 'Invalid vehicle type' }); }
const spots = parkingLot.findAvailableSpots(VehicleType[vehicleType]);
return res.status(200).json({ spots: spots.map(spot => ({ spotId: spot.spotId, spotType: spot.spotType, location: spot.location })) });});#include <pistache/endpoint.h>#include <pistache/http.h>#include <pistache/router.h>#include <nlohmann/json.hpp>
using namespace Pistache;using json = nlohmann::json;
class ParkingController {public: /** * API Contract: * - Endpoint: POST /api/v1/parking/park * - Request Body: { vehicleType: string, licensePlate: string } * - Response 200: { ticketId: string, entryTime: string, spotId: string } * - Response 400: { error: string } - Bad request * - Response 404: { error: string } - No spots available */ void parkVehicle(const Rest::Request& request, Http::ResponseWriter response) { try { auto data = json::parse(request.body());
// Validate input if (!data.contains("vehicleType") || !data.contains("licensePlate")) { json error = {{"error", "Missing required fields"}}; response.send(Http::Code::Bad_Request, error.dump()); return; }
std::string vehicleType = data["vehicleType"]; std::string licensePlate = data["licensePlate"];
// Validate vehicle type if (vehicleType != "CAR" && vehicleType != "MOTORCYCLE" && vehicleType != "TRUCK") { json error = {{"error", "Invalid vehicle type"}}; response.send(Http::Code::Bad_Request, error.dump()); return; }
// Create vehicle and park auto vehicle = createVehicle(vehicleType, licensePlate); auto ticket = parkingLot.parkVehicle(vehicle);
json responseData = { {"ticketId", ticket->getTicketId()}, {"entryTime", ticket->getEntryTime()}, {"spotId", ticket->getSpot()->getSpotId()} };
response.send(Http::Code::Ok, responseData.dump()); } catch (const std::exception& e) { std::string errorMsg = e.what(); if (errorMsg.find("full") != std::string::npos) { json error = {{"error", errorMsg}}; response.send(Http::Code::Not_Found, error.dump()); } else { json error = {{"error", errorMsg}}; response.send(Http::Code::Bad_Request, error.dump()); } } }
/** * API Contract: * - Endpoint: POST /api/v1/parking/unpark * - Request Body: { ticketId: string } * - Response 200: { paymentId: string, amount: double, duration: double } * - Response 400: { error: string } - Bad request * - Response 404: { error: string } - Ticket not found */ void unparkVehicle(const Rest::Request& request, Http::ResponseWriter response) { try { auto data = json::parse(request.body());
if (!data.contains("ticketId")) { json error = {{"error", "Missing ticketId"}}; response.send(Http::Code::Bad_Request, error.dump()); return; }
std::string ticketId = data["ticketId"]; auto payment = parkingLot.unparkVehicle(ticketId);
json responseData = { {"paymentId", payment->getPaymentId()}, {"amount", payment->getAmount()}, {"duration", payment->getTicket()->calculateDuration()} };
response.send(Http::Code::Ok, responseData.dump()); } catch (const std::exception& e) { std::string errorMsg = e.what(); if (errorMsg.find("not found") != std::string::npos) { json error = {{"error", errorMsg}}; response.send(Http::Code::Not_Found, error.dump()); } else { json error = {{"error", errorMsg}}; response.send(Http::Code::Bad_Request, error.dump()); } } }
/** * API Contract: * - Endpoint: GET /api/v1/parking/spots?vehicleType=CAR * - Query Parameter: vehicleType (required) * - Response 200: { spots: [{ spotId: string, spotType: string, location: string }] } * - Response 400: { error: string } - Invalid vehicle type */ void getAvailableSpots(const Rest::Request& request, Http::ResponseWriter response) { auto query = request.query(); if (!query.has("vehicleType")) { json error = {{"error", "Missing vehicleType parameter"}}; response.send(Http::Code::Bad_Request, error.dump()); return; }
std::string vehicleType = query.get("vehicleType").value();
if (vehicleType != "CAR" && vehicleType != "MOTORCYCLE" && vehicleType != "TRUCK") { json error = {{"error", "Invalid vehicle type"}}; response.send(Http::Code::Bad_Request, error.dump()); return; }
auto spots = parkingLot.findAvailableSpots(stringToVehicleType(vehicleType));
json spotsList = json::array(); for (const auto& spot : spots) { spotsList.push_back({ {"spotId", spot->getSpotId()}, {"spotType", vehicleTypeToString(spot->getSpotType())}, {"location", spot->getLocation()} }); }
json responseData = {{"spots", spotsList}}; response.send(Http::Code::Ok, responseData.dump()); }};using Microsoft.AspNetCore.Mvc;using System.Collections.Generic;
[ApiController][Route("/api/v1/parking")]public class ParkingController : ControllerBase{ private readonly ParkingLot parkingLot;
public ParkingController(ParkingLot parkingLot) { this.parkingLot = parkingLot; }
/// <summary> /// API Contract: /// - Endpoint: POST /api/v1/parking/park /// - Request Body: { vehicleType: string, licensePlate: string } /// - Response 200: { ticketId: string, entryTime: string, spotId: string } /// - Response 400: { error: string } - Bad request /// - Response 404: { error: string } - No spots available /// </summary> [HttpPost("park")] public IActionResult ParkVehicle([FromBody] Dictionary<string, string> data) { if (data == null || !data.ContainsKey("vehicleType") || !data.ContainsKey("licensePlate")) { return BadRequest(new { error = "Missing required fields" }); }
string vehicleType = data["vehicleType"]; string licensePlate = data["licensePlate"];
// Validate vehicle type if (!IsValidVehicleType(vehicleType)) { return BadRequest(new { error = "Invalid vehicle type" }); }
try { var vehicle = CreateVehicle(vehicleType, licensePlate); var ticket = parkingLot.ParkVehicle(vehicle);
return Ok(new { ticketId = ticket.TicketId, entryTime = ticket.EntryTime.ToString("o"), spotId = ticket.Spot.SpotId }); } catch (ParkingLotFullException ex) { return NotFound(new { error = ex.Message }); } catch (Exception ex) { return BadRequest(new { error = ex.Message }); } }
/// <summary> /// API Contract: /// - Endpoint: POST /api/v1/parking/unpark /// - Request Body: { ticketId: string } /// - Response 200: { paymentId: string, amount: double, duration: double } /// - Response 400: { error: string } - Bad request /// - Response 404: { error: string } - Ticket not found /// </summary> [HttpPost("unpark")] public IActionResult UnparkVehicle([FromBody] Dictionary<string, string> data) { if (data == null || !data.ContainsKey("ticketId")) { return BadRequest(new { error = "Missing ticketId" }); }
string ticketId = data["ticketId"];
try { var payment = parkingLot.UnparkVehicle(ticketId);
return Ok(new { paymentId = payment.PaymentId, amount = payment.Amount, duration = payment.Ticket.CalculateDuration() }); } catch (InvalidTicketException ex) { return NotFound(new { error = ex.Message }); } catch (Exception ex) { return BadRequest(new { error = ex.Message }); } }
/// <summary> /// API Contract: /// - Endpoint: GET /api/v1/parking/spots?vehicleType=CAR /// - Query Parameter: vehicleType (required) /// - Response 200: { spots: [{ spotId: string, spotType: string, location: string }] } /// - Response 400: { error: string } - Invalid vehicle type /// </summary> [HttpGet("spots")] public IActionResult GetAvailableSpots([FromQuery] string vehicleType) { if (string.IsNullOrEmpty(vehicleType)) { return BadRequest(new { error = "Missing vehicleType parameter" }); }
if (!IsValidVehicleType(vehicleType)) { return BadRequest(new { error = "Invalid vehicle type" }); }
var spots = parkingLot.FindAvailableSpots(Enum.Parse<VehicleType>(vehicleType));
var spotsList = spots.Select(spot => new { spotId = spot.SpotId, spotType = spot.SpotType.ToString(), location = spot.Location }).ToList();
return Ok(new { spots = spotsList }); }
private bool IsValidVehicleType(string type) { return type == "CAR" || type == "MOTORCYCLE" || type == "TRUCK"; }
private Vehicle CreateVehicle(string type, string licensePlate) { // Implementation return null; }}- Endpoint - URL and HTTP method
- Request - Body, parameters, headers
- Response - Status codes, body format
- Error handling - Error codes and messages
- Authentication - How to authenticate
Visual: API Contract Structure
Section titled “Visual: API Contract Structure”Part 5: Exception Handling in Contracts
Section titled “Part 5: Exception Handling in Contracts”Why Exceptions Matter
Section titled “Why Exceptions Matter”Exceptions are part of the contract - they define what can go wrong and how to handle it.
Exception Types
Section titled “Exception Types”- Validation Exceptions - Invalid input
- Business Logic Exceptions - Business rule violations
- System Exceptions - System failures
- Not Found Exceptions - Resource not found
Example: Custom Exceptions
Section titled “Example: Custom Exceptions”class ParkingLotException(Exception): """Base exception for parking lot operations""" pass
class ParkingLotFullException(ParkingLotException): """Raised when parking lot is full""" pass
class InvalidVehicleException(ParkingLotException): """Raised when vehicle type is invalid""" pass
class InvalidTicketException(ParkingLotException): """Raised when ticket is invalid""" pass
class VehicleNotParkedException(ParkingLotException): """Raised when vehicle is not currently parked""" pass
# Usage in contractdef park_vehicle(self, vehicle: Vehicle) -> Ticket: """ Parks a vehicle.
Raises: ValueError: If vehicle is None InvalidVehicleException: If vehicle type not supported ParkingLotFullException: If no spots available """ if vehicle is None: raise ValueError("Vehicle cannot be None")
if not self._is_vehicle_type_supported(vehicle.get_vehicle_type()): raise InvalidVehicleException(f"Vehicle type {vehicle.get_vehicle_type()} not supported")
spot = self._find_available_spot(vehicle.get_vehicle_type()) if spot is None: raise ParkingLotFullException("No available spots")
# ... rest of implementationpublic class ParkingLotException extends Exception { public ParkingLotException(String message) { super(message); }}
public class ParkingLotFullException extends ParkingLotException { public ParkingLotFullException(String message) { super(message); }}
public class InvalidVehicleException extends ParkingLotException { public InvalidVehicleException(String message) { super(message); }}
public class InvalidTicketException extends ParkingLotException { public InvalidTicketException(String message) { super(message); }}
// Usage in contractpublic Ticket parkVehicle(Vehicle vehicle) throws InvalidVehicleException, ParkingLotFullException { if (vehicle == null) { throw new IllegalArgumentException("Vehicle cannot be null"); }
if (!isVehicleTypeSupported(vehicle.getVehicleType())) { throw new InvalidVehicleException("Vehicle type not supported"); }
ParkingSpot spot = findAvailableSpot(vehicle.getVehicleType()) .orElseThrow(() -> new ParkingLotFullException("No available spots"));
// ... rest of implementation}// Base exception for parking lot operationsexport class ParkingLotException extends Error { constructor(message: string) { super(message); this.name = 'ParkingLotException'; }}
// Raised when parking lot is fullexport class ParkingLotFullException extends ParkingLotException { constructor(message: string) { super(message); this.name = 'ParkingLotFullException'; }}
// Raised when vehicle type is invalidexport class InvalidVehicleException extends ParkingLotException { constructor(message: string) { super(message); this.name = 'InvalidVehicleException'; }}
// Raised when ticket is invalidexport class InvalidTicketException extends ParkingLotException { constructor(message: string) { super(message); this.name = 'InvalidTicketException'; }}
// Raised when vehicle is not currently parkedexport class VehicleNotParkedException extends ParkingLotException { constructor(message: string) { super(message); this.name = 'VehicleNotParkedException'; }}
// Usage in contractfunction parkVehicle(vehicle: Vehicle): Ticket { /** * Parks a vehicle. * * @throws Error If vehicle is null * @throws InvalidVehicleException If vehicle type not supported * @throws ParkingLotFullException If no spots available */ if (!vehicle) { throw new Error("Vehicle cannot be null"); }
if (!isVehicleTypeSupported(vehicle.getVehicleType())) { throw new InvalidVehicleException( `Vehicle type ${vehicle.getVehicleType()} not supported` ); }
const spot = findAvailableSpot(vehicle.getVehicleType()); if (!spot) { throw new ParkingLotFullException("No available spots"); }
// ... rest of implementation}#include <exception>#include <string>
// Base exception for parking lot operationsclass ParkingLotException : public std::exception {protected: std::string message;
public: explicit ParkingLotException(const std::string& msg) : message(msg) {}
virtual const char* what() const noexcept override { return message.c_str(); }};
// Raised when parking lot is fullclass ParkingLotFullException : public ParkingLotException {public: explicit ParkingLotFullException(const std::string& msg) : ParkingLotException(msg) {}};
// Raised when vehicle type is invalidclass InvalidVehicleException : public ParkingLotException {public: explicit InvalidVehicleException(const std::string& msg) : ParkingLotException(msg) {}};
// Raised when ticket is invalidclass InvalidTicketException : public ParkingLotException {public: explicit InvalidTicketException(const std::string& msg) : ParkingLotException(msg) {}};
// Raised when vehicle is not currently parkedclass VehicleNotParkedException : public ParkingLotException {public: explicit VehicleNotParkedException(const std::string& msg) : ParkingLotException(msg) {}};
// Usage in contractTicket parkVehicle(Vehicle* vehicle) { /** * Parks a vehicle. * * @throws std::invalid_argument If vehicle is null * @throws InvalidVehicleException If vehicle type not supported * @throws ParkingLotFullException If no spots available */ if (!vehicle) { throw std::invalid_argument("Vehicle cannot be null"); }
if (!isVehicleTypeSupported(vehicle->getVehicleType())) { throw InvalidVehicleException( "Vehicle type " + std::to_string(static_cast<int>(vehicle->getVehicleType())) + " not supported" ); }
ParkingSpot* spot = findAvailableSpot(vehicle->getVehicleType()); if (!spot) { throw ParkingLotFullException("No available spots"); }
// ... rest of implementation}using System;
// Base exception for parking lot operationspublic class ParkingLotException : Exception{ public ParkingLotException(string message) : base(message) { }}
// Raised when parking lot is fullpublic class ParkingLotFullException : ParkingLotException{ public ParkingLotFullException(string message) : base(message) { }}
// Raised when vehicle type is invalidpublic class InvalidVehicleException : ParkingLotException{ public InvalidVehicleException(string message) : base(message) { }}
// Raised when ticket is invalidpublic class InvalidTicketException : ParkingLotException{ public InvalidTicketException(string message) : base(message) { }}
// Raised when vehicle is not currently parkedpublic class VehicleNotParkedException : ParkingLotException{ public VehicleNotParkedException(string message) : base(message) { }}
// Usage in contractpublic Ticket ParkVehicle(Vehicle vehicle){ /// <summary> /// Parks a vehicle. /// </summary> /// <exception cref="ArgumentNullException">If vehicle is null</exception> /// <exception cref="InvalidVehicleException">If vehicle type not supported</exception> /// <exception cref="ParkingLotFullException">If no spots available</exception>
if (vehicle == null) { throw new ArgumentNullException(nameof(vehicle), "Vehicle cannot be null"); }
if (!IsVehicleTypeSupported(vehicle.GetVehicleType())) { throw new InvalidVehicleException( $"Vehicle type {vehicle.GetVehicleType()} not supported" ); }
var spot = FindAvailableSpot(vehicle.GetVehicleType()); if (spot == null) { throw new ParkingLotFullException("No available spots"); }
// ... rest of implementation}Part 6: Best Practices
Section titled “Part 6: Best Practices”✔ Be explicit - Clear parameter and return types
✔ Document exceptions - What can go wrong?
✔ Use meaningful names - Self-documenting code
✔ Define preconditions - What must be true before?
✔ Define postconditions - What will be true after?
✔ Handle edge cases - Null checks, validation
✔ Use interfaces - For abstraction and flexibility
Don’ts
Section titled “Don’ts”❌ Don’t be vague - Unclear contracts lead to bugs
❌ Don’t forget exceptions - Document what can fail
❌ Don’t expose implementation - Hide internal details
❌ Don’t break contracts - Once defined, maintain them
❌ Don’t over-complicate - Keep contracts simple
Visual: Good vs Bad Contracts
Section titled “Visual: Good vs Bad Contracts”Summary: Contract and API Definitions
Section titled “Summary: Contract and API Definitions”Key Takeaways
Section titled “Key Takeaways”✔ Contracts define agreements - What classes/methods will do
✔ Interfaces - Abstract contracts for flexibility
✔ Method contracts - Preconditions, postconditions, exceptions
✔ API contracts - REST/HTTP API specifications
✔ Exception handling - Part of the contract
✔ Documentation - Clear and comprehensive
✔ Best practices - Explicit, documented, maintainable
Contract Checklist
Section titled “Contract Checklist”When defining contracts, ensure:
- Clear signatures - Parameters and return types
- Documented exceptions - What can go wrong?
- Preconditions - What must be true before?
- Postconditions - What will be true after?
- Well documented - Clear descriptions
- Edge cases handled - Null checks, validation
- Consistent - Follow same patterns
Visual Summary
Section titled “Visual Summary”Complete LLD Interview Process
Section titled “Complete LLD Interview Process”Congratulations! You’ve now mastered all the key steps of LLD interviews:
- ✔ Understanding LLD Interviews - What they are and why they matter
- ✔ Steps in LLD Interview - The systematic approach
- ✔ Identifying Actors & Entities - Foundation of design
- ✔ Assign Responsibilities - Single Responsibility Principle
- ✔ Class Diagrams - Visualize your design
- ✔ Contract and API Definitions - Define interfaces
Final Checklist
Section titled “Final Checklist”Before your LLD interview, make sure you can:
- Understand the problem - Ask clarifying questions
- Identify actors - Who uses the system?
- Identify entities - What are the core objects?
- Assign responsibilities - One per class
- Create class diagrams - Visualize relationships
- Define contracts - Clear interfaces
- Handle edge cases - Think about errors
- Implement cleanly - Follow your design