Avtomobil Dayanacağının Dizaynı
Problem Bəyanatı
Müxtəlif növ nəqliyyat vasitələri üçün dayanacaq yerlərini səmərəli idarə edə bilən, avtomobillərin giriş və çıxışını idarə edən, dayanacaq haqqını hesablayan və doluluk məlumatlarını qoruyub saxlayan avtomobil dayanacaq sistemini dizayn edin.
Tələblər
-
Funksional Tələblər:
- Müxtəlif növ nəqliyyat vasitələrini dəstəkləmək (Avtomobil, Motosikl, Avtobus və s.)
- Müxtəlif ölçülü dayanacaq yerlərini dəstəkləmək
- Girişdə dayanacaq biletləri vermək
- Müddət və nəqliyyat vasitəsi növünə görə dayanacaq haqqını hesablamaq
- Dayanacaq üçün ödənişləri emal etmək
- Verilmiş nəqliyyat vasitəsi üçün əlçatan dayanacaq yerləri tapmaq
- Dayanacaq yerlərinin dolulugunu və əlçatanlığını izləmək
-
Qeyri-Funksional Tələblər:
- Paralel əməliyyatlar üçün thread təhlükəsizliyi
- Böyük dayanacaqları idarə etmək üçün miqyaslanma
- Nəqliyyat vasitələri və ödənişlərin izlənməsində etibarlılıq
- Əlçatan yerləri tez tapmaqda performans
Əsas Komponentlər
- ParkingLot: Ümumi dayanacaq sistemini idarə edən əsas sinif
- ParkingSpot: Tək dayanacaq yerini təmsil edir
- Vehicle: Müxtəlif növ nəqliyyat vasitələri üçün baza sinfi
- ParkingTicket: Dayanacaq müddəti, haqq və s. haqqında məlumat ehtiva edir
- PaymentProcessor: Dayanacaq üçün ödənişi idarə edir
- ParkingFloor: Çox mərtəbəli dayanacaqda bir mərtəbəni təmsil edir
İmplementasiya
Koda bax
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// Nəqliyyat vasitəsi növləri üçün enum
enum VehicleType {
MOTORCYCLE(1),
CAR(2),
BUS(4);
private final int spotSize;
VehicleType(int spotSize) {
this.spotSize = spotSize;
}
public int getSpotSize() {
return spotSize;
}
}
// Dayanacaq yeri növləri üçün enum
enum ParkingSpotType {
MOTORCYCLE(1),
COMPACT(2),
LARGE(4);
private final int size;
ParkingSpotType(int size) {
this.size = size;
}
public int getSize() {
return size;
}
}
// Nəqliyyat vasitəsi sinfi
class Vehicle {
private final String licensePlate;
private final VehicleType type;
public Vehicle(String licensePlate, VehicleType type) {
this.licensePlate = licensePlate;
this.type = type;
}
public String getLicensePlate() {
return licensePlate;
}
public VehicleType getType() {
return type;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Vehicle vehicle = (Vehicle) o;
return licensePlate.equals(vehicle.licensePlate);
}
@Override
public int hashCode() {
return Objects.hash(licensePlate);
}
}
// Dayanacaq yeri sinfi
class ParkingSpot {
private final String id;
private final ParkingSpotType type;
private boolean isOccupied;
private Vehicle vehicle;
private final Lock lock;
public ParkingSpot(String id, ParkingSpotType type) {
this.id = id;
this.type = type;
this.isOccupied = false;
this.lock = new ReentrantLock();
}
public String getId() {
return id;
}
public ParkingSpotType getType() {
return type;
}
public boolean isOccupied() {
lock.lock();
try {
return isOccupied;
} finally {
lock.unlock();
}
}
public boolean park(Vehicle vehicle) {
lock.lock();
try {
if (!isOccupied && vehicle.getType().getSpotSize() <= type.getSize()) {
this.vehicle = vehicle;
isOccupied = true;
return true;
}
return false;
} finally {
lock.unlock();
}
}
public Vehicle removeVehicle() {
lock.lock();
try {
if (isOccupied) {
Vehicle parkedVehicle = this.vehicle;
this.vehicle = null;
isOccupied = false;
return parkedVehicle;
}
return null;
} finally {
lock.unlock();
}
}
}
// Dayanacaq bileti sinfi
class ParkingTicket {
private final String ticketId;
private final Vehicle vehicle;
private final LocalDateTime entryTime;
private LocalDateTime exitTime;
private double fee;
private boolean isPaid;
public ParkingTicket(String ticketId, Vehicle vehicle) {
this.ticketId = ticketId;
this.vehicle = vehicle;
this.entryTime = LocalDateTime.now();
this.isPaid = false;
}
public String getTicketId() {
return ticketId;
}
public Vehicle getVehicle() {
return vehicle;
}
public LocalDateTime getEntryTime() {
return entryTime;
}
public LocalDateTime getExitTime() {
return exitTime;
}
public void setExitTime(LocalDateTime exitTime) {
this.exitTime = exitTime;
}
public double getFee() {
return fee;
}
public void setFee(double fee) {
this.fee = fee;
}
public boolean isPaid() {
return isPaid;
}
public void markAsPaid() {
this.isPaid = true;
}
@Override
public String toString() {
return "Bilet ID: " + ticketId +
"\nNəqliyyat vasitəsi: " + vehicle.getLicensePlate() +
"\nGiriş vaxtı: " + entryTime +
(exitTime != null ? "\nÇıxış vaxtı: " + exitTime : "") +
(fee > 0 ? "\nHaqq: $" + fee : "") +
"\nÖdənilib: " + (isPaid ? "Bəli" : "Xeyr");
}
}
// Haqq kalkulyatoru interfeysi
interface FeeCalculator {
double calculateFee(VehicleType vehicleType, Duration parkingDuration);
}
// Saatlıq haqq kalkulyatoru
class HourlyFeeCalculator implements FeeCalculator {
private final Map<VehicleType, Double> hourlyRates;
public HourlyFeeCalculator() {
hourlyRates = new HashMap<>();
hourlyRates.put(VehicleType.MOTORCYCLE, 1.0);
hourlyRates.put(VehicleType.CAR, 2.0);
hourlyRates.put(VehicleType.BUS, 4.0);
}
@Override
public double calculateFee(VehicleType vehicleType, Duration parkingDuration) {
double hourlyRate = hourlyRates.getOrDefault(vehicleType, 2.0);
long hours = parkingDuration.toHours();
// Minimum 1 saat haqq
return Math.max(1, hours) * hourlyRate;
}
}
// Ödəniş emal edəni
class PaymentProcessor {
public boolean processPayment(ParkingTicket ticket, String paymentMethod) {
// Həqiqi implementasiyada bu, ödəniş şlüzü ilə inteqrasiya olardı
System.out.println(paymentMethod + " istifadə edərək $" + ticket.getFee() + " ödənişini emal edirik");
// Ödəniş emalını simulyasiya edirik
boolean paymentSuccessful = Math.random() > 0.1; // 90% müvəffəqiyyət nisbəti
if (paymentSuccessful) {
ticket.markAsPaid();
System.out.println("Ödəniş uğurlu oldu");
} else {
System.out.println("Ödəniş uğursuz oldu");
}
return paymentSuccessful;
}
}
// Dayanacaq mərtəbəsi sinfi
class ParkingFloor {
private final String floorId;
private final List<ParkingSpot> parkingSpots;
private final Map<String, ParkingSpot> occupiedSpots; // Nömrə nişanından yer xəritələməsi
private final Lock lock;
public ParkingFloor(String floorId, int motorcycleSpots, int compactSpots, int largeSpots) {
this.floorId = floorId;
this.parkingSpots = new ArrayList<>();
this.occupiedSpots = new ConcurrentHashMap<>();
this.lock = new ReentrantLock();
// Dayanacaq yerlərini inisializasiya et
initializeSpots(motorcycleSpots, compactSpots, largeSpots);
}
private void initializeSpots(int motorcycleSpots, int compactSpots, int largeSpots) {
// Motosikl yerləri yarat
for (int i = 1; i <= motorcycleSpots; i++) {
parkingSpots.add(new ParkingSpot(floorId + "-M-" + i, ParkingSpotType.MOTORCYCLE));
}
// Kompakt yerlər yarat
for (int i = 1; i <= compactSpots; i++) {
parkingSpots.add(new ParkingSpot(floorId + "-C-" + i, ParkingSpotType.COMPACT));
}
// Böyük yerlər yarat
for (int i = 1; i <= largeSpots; i++) {
parkingSpots.add(new ParkingSpot(floorId + "-L-" + i, ParkingSpotType.LARGE));
}
}
public ParkingSpot findAvailableSpot(VehicleType vehicleType) {
lock.lock();
try {
int requiredSize = vehicleType.getSpotSize();
// Əvvəl lazım olan dəqiq ölçüdə yer tapmağa çalış
for (ParkingSpot spot : parkingSpots) {
if (!spot.isOccupied() && spot.getType().getSize() == requiredSize) {
return spot;
}
}
// Əgər dəqiq uyğunluq yoxdursa, nəqliyyat vasitəsini yerləşdirə biləcək istənilən yer tap
for (ParkingSpot spot : parkingSpots) {
if (!spot.isOccupied() && spot.getType().getSize() >= requiredSize) {
return spot;
}
}
return null; // Əlçatan yer yoxdur
} finally {
lock.unlock();
}
}
public boolean parkVehicle(Vehicle vehicle, ParkingSpot spot) {
if (spot.park(vehicle)) {
occupiedSpots.put(vehicle.getLicensePlate(), spot);
return true;
}
return false;
}
public ParkingSpot getOccupiedSpotByVehicle(String licensePlate) {
return occupiedSpots.get(licensePlate);
}
public Vehicle removeVehicle(String licensePlate) {
ParkingSpot spot = occupiedSpots.get(licensePlate);
if (spot != null) {
Vehicle vehicle = spot.removeVehicle();
if (vehicle != null) {
occupiedSpots.remove(licensePlate);
return vehicle;
}
}
return null;
}
public int getAvailableSpotsCount() {
lock.lock();
try {
int count = 0;
for (ParkingSpot spot : parkingSpots) {
if (!spot.isOccupied()) {
count++;
}
}
return count;
} finally {
lock.unlock();
}
}
public String getFloorId() {
return floorId;
}
}
// Dayanacaq sinfi
class ParkingLot {
private final String name;
private final List<ParkingFloor> floors;
private final Map<String, ParkingTicket> activeTickets; // Nömrə nişanından bilet xəritələməsi
private final FeeCalculator feeCalculator;
private final PaymentProcessor paymentProcessor;
private final Lock lock;
public ParkingLot(String name, int numFloors, int motorcycleSpotsPerFloor,
int compactSpotsPerFloor, int largeSpotsPerFloor) {
this.name = name;
this.floors = new ArrayList<>();
this.activeTickets = new ConcurrentHashMap<>();
this.feeCalculator = new HourlyFeeCalculator();
this.paymentProcessor = new PaymentProcessor();
this.lock = new ReentrantLock();
// Mərtəbələri inisializasiya et
for (int i = 1; i <= numFloors; i++) {
floors.add(new ParkingFloor("F" + i, motorcycleSpotsPerFloor,
compactSpotsPerFloor, largeSpotsPerFloor));
}
}
public ParkingTicket parkVehicle(Vehicle vehicle) {
lock.lock();
try {
// Nəqliyyat vasitəsinin artıq dayanıb-dayanmadığını yoxla
if (activeTickets.containsKey(vehicle.getLicensePlate())) {
System.out.println("Nəqliyyat vasitəsi artıq dayanıb");
return null;
}
// Əlçatan yer tap
for (ParkingFloor floor : floors) {
ParkingSpot spot = floor.findAvailableSpot(vehicle.getType());
if (spot != null) {
if (floor.parkVehicle(vehicle, spot)) {
// Bilet yarat və qaytar
String ticketId = generateTicketId();
ParkingTicket ticket = new ParkingTicket(ticketId, vehicle);
activeTickets.put(vehicle.getLicensePlate(), ticket);
System.out.println("Nəqliyyat vasitəsi " + spot.getId() + " yerində uğurla dayanıb");
return ticket;
}
}
}
System.out.println("Bu nəqliyyat vasitəsi növü üçün əlçatan yer yoxdur");
return null;
} finally {
lock.unlock();
}
}
public double calculateFee(String licensePlate) {
ParkingTicket ticket = activeTickets.get(licensePlate);
if (ticket != null) {
LocalDateTime exitTime = LocalDateTime.now();
ticket.setExitTime(exitTime);
Duration parkingDuration = Duration.between(ticket.getEntryTime(), exitTime);
double fee = feeCalculator.calculateFee(ticket.getVehicle().getType(), parkingDuration);
ticket.setFee(fee);
return fee;
}
return 0;
}
public boolean processPayment(String licensePlate, String paymentMethod) {
ParkingTicket ticket = activeTickets.get(licensePlate);
if (ticket != null && !ticket.isPaid()) {
if (ticket.getFee() == 0) {
calculateFee(licensePlate);
}
return paymentProcessor.processPayment(ticket, paymentMethod);
}
return false;
}
public Vehicle removeVehicle(String licensePlate) {
lock.lock();
try {
ParkingTicket ticket = activeTickets.get(licensePlate);
if (ticket != null) {
if (!ticket.isPaid()) {
System.out.println("Nəqliyyat vasitəsi götürülmədən əvvəl ödəniş tələb olunur");
return null;
}
// Nəqliyyat vasitəsini tap və çıxart
for (ParkingFloor floor : floors) {
ParkingSpot spot = floor.getOccupiedSpotByVehicle(licensePlate);
if (spot != null) {
Vehicle vehicle = floor.removeVehicle(licensePlate);
if (vehicle != null) {
activeTickets.remove(licensePlate);
System.out.println("Nəqliyyat vasitəsi " + spot.getId() + " yerindən uğurla çıxarıldı");
return vehicle;
}
}
}
}
System.out.println("Nəqliyyat vasitəsi dayanacaqda tapılmadı");
return null;
} finally {
lock.unlock();
}
}
public ParkingTicket getTicket(String licensePlate) {
return activeTickets.get(licensePlate);
}
public int getTotalAvailableSpots() {
lock.lock();
try {
int count = 0;
for (ParkingFloor floor : floors) {
count += floor.getAvailableSpotsCount();
}
return count;
} finally {
lock.unlock();
}
}
public int getAvailableSpots(ParkingSpotType spotType) {
lock.lock();
try {
int count = 0;
for (ParkingFloor floor : floors) {
for (int i = 0; i < floor.parkingSpots.size(); i++) {
ParkingSpot spot = floor.parkingSpots.get(i);
if (!spot.isOccupied() && spot.getType() == spotType) {
count++;
}
}
}
return count;
} finally {
lock.unlock();
}
}
private String generateTicketId() {
return "T" + System.currentTimeMillis();
}
public String getName() {
return name;
}
}
// İstifadə nümunəsi
public class ParkingLotDemo {
public static void main(String[] args) {
// 3 mərtəbəli dayanacaq yarat
ParkingLot parkingLot = new ParkingLot("Şəhər Mərkəzi Dayanacaq", 3, 10, 20, 5);
// Nəqliyyat vasitələri yarat
Vehicle car1 = new Vehicle("ABC123", VehicleType.CAR);
Vehicle car2 = new Vehicle("DEF456", VehicleType.CAR);
Vehicle motorcycle = new Vehicle("MNO789", VehicleType.MOTORCYCLE);
Vehicle bus = new Vehicle("XYZ999", VehicleType.BUS);
// Nəqliyyat vasitələrini dayan
ParkingTicket ticket1 = parkingLot.parkVehicle(car1);
ParkingTicket ticket2 = parkingLot.parkVehicle(car2);
ParkingTicket ticket3 = parkingLot.parkVehicle(motorcycle);
ParkingTicket ticket4 = parkingLot.parkVehicle(bus);
// Biletləri göstər
System.out.println("\ncar1 üçün bilet:");
System.out.println(ticket1);
// Vaxtın keçməsini simulyasiya et (nümayiş məqsədilə)
try {
System.out.println("\nVaxtın keçməsini simulyasiya etmək üçün 2 saniyə gözləyirik...");
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// Haqqları hesabla
double fee1 = parkingLot.calculateFee(car1.getLicensePlate());
System.out.println("\ncar1 üçün haqq: $" + fee1);
// Ödənişi emal et
boolean paymentSuccess = parkingLot.processPayment(car1.getLicensePlate(), "Kredit Kartı");
// Ödənişdən sonra nəqliyyat vasitəsini çıxart
if (paymentSuccess) {
Vehicle removedVehicle = parkingLot.removeVehicle(car1.getLicensePlate());
if (removedVehicle != null) {
System.out.println("\n" + removedVehicle.getLicensePlate() + " nömrəli nəqliyyat vasitəsi dayanacaqdan çıxdı");
}
}
// Əlçatan yerləri göstər
System.out.println("\nÜmumi əlçatan yerlər: " + parkingLot.getTotalAvailableSpots());
System.out.println("Əlçatan motosikl yerləri: " +
parkingLot.getAvailableSpots(ParkingSpotType.MOTORCYCLE));
System.out.println("Əlçatan kompakt yerlər: " +
parkingLot.getAvailableSpots(ParkingSpotType.COMPACT));
System.out.println("Əlçatan böyük yerlər: " +
parkingLot.getAvailableSpots(ParkingSpotType.LARGE));
}
}
Thread Təhlükəsizliyi Mülahizələri
- ReentrantLock: Dayanacaq yerləri və mərtəbələrində thread-safe əməliyyatları təmin etmək üçün istifadə olunur
- ConcurrentHashMap: Aktiv biletlərin və məşğul yerlərin thread-safe saxlanması üçün istifadə olunur
- Atomik Əməliyyatlar: Dayanma və nəqliyyat vasitələrinin çıxarılması kimi kritik əməliyyatlar atomik olaraq yerinə yetirilir
- Dəyişməz Obyektlər: Nəqliyyat vasitəsi və bilet ID-ləri dəyişməzdir
Dayanacaq Yeri Bölgüsü Strategiyası
- Ölçü Əsaslı Bölgü: Nəqliyyat vasitələri öz ölçü tələblərinə uyğun yerlərə təyin edilir
- Optimal Uyğunluq: Sistem nəqliyyat vasitəsinin ölçüsünə tam uyğun gələn yer tapmağa çalışır
- Geri Dönüş Strategiyası: Əgər dəqiq uyğunluq tapılmasa, istənilən böyük yer istifadə edilir
- Mərtəbə-Mərtəbə Axtarış: Nəqliyyat vasitələrini bərabər paylamaq üçün yerlər mərtəbə-mərtəbə axtarılır
Haqq Hesablaması
- Vaxt Əsaslı Qiymətləndirmə: Haqqlar dayanacaq müddətinə görə hesablanır
- Nəqliyyat Vasitəsi Növü Qiymətləndirməsi: Müxtəlif nəqliyyat vasitəsi növləri üçün müxtəlif tariflər
- Minimum Haqq: Qısa müddətli dayanacaq üçün minimum haqq
- Genişlənə Bilən Dizayn: Haqq kalkulyatoru interfeysi müxtəlif qiymətləndirmə strategiyalarına imkan verir
Əlavə Xüsusiyyətlər
- Çox Mərtəbə Dəstəyi: Dayanacaqda çoxlu mərtəbələrin dəstəklənməsi
- Ödəniş Emalı: Ödəniş emal sistemləri ilə inteqrasiya
- Əlçatanlıq İzlənməsi: Növ üzrə əlçatan yerlərin real vaxtda izlənməsi
- Bilet İdarəetməsi: Giriş və çıxış üçün hərtərəfli bilet sistemi