Əsas məzmuna keçin

Avtomatik Satış Maşınının Dizaynı

Problem Bəyanatı​

İstifadəçilərə məhsul seçməyə, müxtəlif ödəniş üsullarını qəbul etməyə, məhsulları paylama və lazım olduqda qalığı qaytarmağa imkan verən avtomatik satış maşını dizayn edin.

Tələblər​

  1. Funksional Tələblər:

    • Mövcud məhsulları qiymətləri ilə birlikdə göstərmək
    • Məhsul seçiminə icazə vermək
    • Müxtəlif ödəniş üsullarını qəbul etmək (sikkələr, əskinaslar, kartlar)
    • Seçilmiş məhsulları paylamaq
    • Lazım olduqda qalığı qaytarmaq
    • Məhsul inventarını saxlamaq
    • Uğursuz əməliyyatlar üçün geri ödəməni dəstəkləmək
  2. Qeyri-Funksional Tələblər:

    • Paralel əməliyyatlar üçün thread təhlükəsizliyi
    • Etibarlılıq (xətaları zərif şəkildə idarə etmək)
    • Saxlanılma qabiliyyəti (yeni məhsul və ya ödəniş üsulları əlavə etməyin asanlığı)
    • Ödəniş emalında təhlükəsizlik

Əsas Komponentlər​

  1. Məhsul İnventarı: Mövcud məhsulları və onların miqdarlarını idarə edir
  2. Ödəniş Emal Edəni: Müxtəlif ödəniş üsullarını idarə edir
  3. Məhsul Paylaşdırıcı: Seçilmiş məhsulları paylayır
  4. Qalıq Qaytarıcı: Lazım olduqda qalığı qaytarır
  5. Ekran: İstifadəçiyə mövcud məhsulları, qiymətləri və mesajları göstərir
  6. Nəzarətçi: Avtomatik satış maşınının ümumi əməliyyatını koordinasiya edir

Dizayn Yanaşması​

Avtomatik satış maşınının əməliyyatını modelləşdirmək üçün State Pattern istifadə edəcəyik:

  1. Gözləmə: Başlanğıc vəziyyət, istifadəçi qarşılıqlı təsirini gözləyir
  2. Məhsul Seçildi: İstifadəçi məhsul seçib
  3. Ödəniş Emalı: Maşın ödənişi emal edir
  4. Paylama: Maşın məhsulu paylayır
  5. Qalıq Qaytarma: Maşın qalığı qaytarır
  6. Xidmət Rejimi: Maşın baxım rejimindədir

İmplementasiya​

Koda bax
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

// Məhsul sinfi
class Product {
private final String id;
private final String name;
private final double price;
private int quantity;

public Product(String id, String name, double price, int quantity) {
this.id = id;
this.name = name;
this.price = price;
this.quantity = quantity;
}

public String getId() {
return id;
}

public String getName() {
return name;
}

public double getPrice() {
return price;
}

public int getQuantity() {
return quantity;
}

public void decrementQuantity() {
if (quantity > 0) {
quantity--;
}
}

public void incrementQuantity() {
quantity++;
}

@Override
public String toString() {
return id + ": " + name + " - $" + price + " (" + quantity + " mövcud)";
}
}

// Ödəniş üsulu interfeysi
interface PaymentMethod {
boolean processPayment(double amount);
void refund(double amount);
}

// Sikkə ödəniş üsulu
class CoinPayment implements PaymentMethod {
private final Map<Double, Integer> coinInventory;
private final Lock lock = new ReentrantLock();

public CoinPayment() {
coinInventory = new HashMap<>();
// Bəzi sikkələrlə inisializasiya et
coinInventory.put(0.25, 20); // Çeyrek
coinInventory.put(0.10, 20); // On sent
coinInventory.put(0.05, 20); // Beş sent
coinInventory.put(0.01, 20); // Sent
}

@Override
public boolean processPayment(double amount) {
// Həqiqi implementasiyada bu, sikkə qəbuledicisi aparatı ilə qarşılıqlı təsir göstərərdi
System.out.println("$" + amount + " məbləğində sikkə ödənişini emal edirik");
return true;
}

@Override
public void refund(double amount) {
System.out.println("$" + amount + " məbləğini sikkələrlə geri qaytarırıq");
}

public Map<Double, Integer> calculateChange(double amount) {
lock.lock();
try {
Map<Double, Integer> change = new HashMap<>();
double[] coinValues = {0.25, 0.10, 0.05, 0.01};

for (double coinValue : coinValues) {
int count = (int) (amount / coinValue);
int available = coinInventory.getOrDefault(coinValue, 0);

if (count > 0 && available > 0) {
int actualCount = Math.min(count, available);
change.put(coinValue, actualCount);
amount -= actualCount * coinValue;
coinInventory.put(coinValue, available - actualCount);
}
}

// Əgər dəqiq qalıq verə bilmədiksə
if (amount > 0.001) { // Ondalıq nöqtə müqayisəsi üçün kiçik epsilon istifadə edirik
// İnventardakı dəyişiklikləri geri al
for (Map.Entry<Double, Integer> entry : change.entrySet()) {
double coinValue = entry.getKey();
int count = entry.getValue();
coinInventory.put(coinValue, coinInventory.get(coinValue) + count);
}
return null;
}

return change;
} finally {
lock.unlock();
}
}
}

// Kart ödəniş üsulu
class CardPayment implements PaymentMethod {
@Override
public boolean processPayment(double amount) {
// Həqiqi implementasiyada bu, kart oxucu aparatı ilə qarşılıqlı təsir göstərərdi
System.out.println("$" + amount + " məbləğində kart ödənişini emal edirik");
return Math.random() > 0.1; // Simulyasiya üçün 90% müvəffəqiyyət nisbəti
}

@Override
public void refund(double amount) {
System.out.println("$" + amount + " məbləğini karta geri qaytarırıq");
}
}

// Avtomatik satış maşını vəziyyət interfeysi
interface VendingMachineState {
void selectProduct(VendingMachine machine, String productId);
void insertPayment(VendingMachine machine, PaymentMethod method, double amount);
void dispenseProduct(VendingMachine machine);
void returnChange(VendingMachine machine);
void cancel(VendingMachine machine);
}

// Gözləmə vəziyyəti
class IdleState implements VendingMachineState {
@Override
public void selectProduct(VendingMachine machine, String productId) {
Product product = machine.getInventory().get(productId);
if (product != null && product.getQuantity() > 0) {
machine.setSelectedProduct(product);
machine.setDisplay("Seçildi: " + product.getName() + " - $" + product.getPrice());
machine.setState(new ProductSelectedState());
} else {
machine.setDisplay("Məhsul mövcud deyil");
}
}

@Override
public void insertPayment(VendingMachine machine, PaymentMethod method, double amount) {
machine.setDisplay("Zəhmət olmasa əvvəl məhsul seçin");
}

@Override
public void dispenseProduct(VendingMachine machine) {
machine.setDisplay("Zəhmət olmasa əvvəl məhsul seçin");
}

@Override
public void returnChange(VendingMachine machine) {
machine.setDisplay("Qaytarılacaq qalıq yoxdur");
}

@Override
public void cancel(VendingMachine machine) {
machine.setDisplay("Ləğv ediləcək heç nə yoxdur");
}
}

// Məhsul seçilmiş vəziyyət
class ProductSelectedState implements VendingMachineState {
@Override
public void selectProduct(VendingMachine machine, String productId) {
Product product = machine.getInventory().get(productId);
if (product != null && product.getQuantity() > 0) {
machine.setSelectedProduct(product);
machine.setDisplay("Seçildi: " + product.getName() + " - $" + product.getPrice());
} else {
machine.setDisplay("Məhsul mövcud deyil");
}
}

@Override
public void insertPayment(VendingMachine machine, PaymentMethod method, double amount) {
machine.setPaymentMethod(method);
machine.setAmountPaid(amount);
machine.setDisplay("Ödəniş emal edilir...");
machine.setState(new PaymentProcessingState());
machine.processPayment();
}

@Override
public void dispenseProduct(VendingMachine machine) {
machine.setDisplay("Zəhmət olmasa əvvəl ödəniş edin");
}

@Override
public void returnChange(VendingMachine machine) {
machine.setDisplay("Qaytarılacaq ödəniş yoxdur");
}

@Override
public void cancel(VendingMachine machine) {
machine.setSelectedProduct(null);
machine.setDisplay("Əməliyyat ləğv edildi");
machine.setState(new IdleState());
}
}

// Ödəniş emalı vəziyyəti
class PaymentProcessingState implements VendingMachineState {
@Override
public void selectProduct(VendingMachine machine, String productId) {
machine.setDisplay("Ödəniş davam edir, zəhmət olmasa gözləyin");
}

@Override
public void insertPayment(VendingMachine machine, PaymentMethod method, double amount) {
machine.setDisplay("Ödəniş artıq davam edir");
}

@Override
public void dispenseProduct(VendingMachine machine) {
if (machine.isPaymentSuccessful()) {
machine.setState(new DispensingState());
machine.dispenseProduct();
} else {
machine.setDisplay("Ödəniş uğursuz oldu");
machine.setState(new IdleState());
}
}

@Override
public void returnChange(VendingMachine machine) {
machine.setDisplay("Ödəniş davam edir, zəhmət olmasa gözləyin");
}

@Override
public void cancel(VendingMachine machine) {
machine.refundPayment();
machine.setSelectedProduct(null);
machine.setDisplay("Əməliyyat ləğv edildi");
machine.setState(new IdleState());
}
}

// Paylama vəziyyəti
class DispensingState implements VendingMachineState {
@Override
public void selectProduct(VendingMachine machine, String productId) {
machine.setDisplay("Məhsul paylanır, zəhmət olmasa gözləyin");
}

@Override
public void insertPayment(VendingMachine machine, PaymentMethod method, double amount) {
machine.setDisplay("Məhsul paylanır, zəhmət olmasa gözləyin");
}

@Override
public void dispenseProduct(VendingMachine machine) {
// Artıq paylanır
}

@Override
public void returnChange(VendingMachine machine) {
machine.setState(new ReturningChangeState());
machine.returnChange();
}

@Override
public void cancel(VendingMachine machine) {
machine.setDisplay("Ləğv etmək üçün artıq gec, məhsul paylanır");
}
}

// Qalıq qaytarma vəziyyəti
class ReturningChangeState implements VendingMachineState {
@Override
public void selectProduct(VendingMachine machine, String productId) {
machine.setDisplay("Qalıq qaytarılır, zəhmət olmasa gözləyin");
}

@Override
public void insertPayment(VendingMachine machine, PaymentMethod method, double amount) {
machine.setDisplay("Qalıq qaytarılır, zəhmət olmasa gözləyin");
}

@Override
public void dispenseProduct(VendingMachine machine) {
machine.setDisplay("Qalıq qaytarılır, zəhmət olmasa gözləyin");
}

@Override
public void returnChange(VendingMachine machine) {
// Artıq qalıq qaytarılır
}

@Override
public void cancel(VendingMachine machine) {
machine.setDisplay("Qalıq qaytarılır, zəhmət olmasa gözləyin");
}

public void changeReturned(VendingMachine machine) {
machine.setDisplay("Alış-verişiniz üçün təşəkkürlər");
machine.setState(new IdleState());
}
}

// Xidmət rejimi vəziyyəti
class ServiceModeState implements VendingMachineState {
@Override
public void selectProduct(VendingMachine machine, String productId) {
machine.setDisplay("Maşın xidmət rejimindədir");
}

@Override
public void insertPayment(VendingMachine machine, PaymentMethod method, double amount) {
machine.setDisplay("Maşın xidmət rejimindədir");
}

@Override
public void dispenseProduct(VendingMachine machine) {
machine.setDisplay("Maşın xidmət rejimindədir");
}

@Override
public void returnChange(VendingMachine machine) {
machine.setDisplay("Maşın xidmət rejimindədir");
}

@Override
public void cancel(VendingMachine machine) {
machine.setDisplay("Xidmət rejimindən çıxılır");
machine.setState(new IdleState());
}
}

// Avtomatik satış maşını sinfi
class VendingMachine {
private final Map<String, Product> inventory;
private final Lock lock;
private VendingMachineState state;
private String display;
private Product selectedProduct;
private PaymentMethod paymentMethod;
private double amountPaid;
private boolean paymentSuccessful;

public VendingMachine() {
this.inventory = new ConcurrentHashMap<>();
this.lock = new ReentrantLock();
this.state = new IdleState();
this.display = "Xoş gəlmisiniz! Zəhmət olmasa məhsul seçin";

// Bəzi məhsullarla inisializasiya et
addProduct(new Product("A1", "Kola", 1.25, 10));
addProduct(new Product("A2", "Diet Kola", 1.25, 10));
addProduct(new Product("B1", "Su", 1.00, 10));
addProduct(new Product("B2", "Qazsız Su", 1.50, 10));
addProduct(new Product("C1", "Çips", 0.75, 10));
addProduct(new Product("C2", "Şokolad", 1.00, 10));
}

public Map<String, Product> getInventory() {
return inventory;
}

public void addProduct(Product product) {
inventory.put(product.getId(), product);
}

public void setState(VendingMachineState state) {
this.state = state;
}

public void setDisplay(String display) {
this.display = display;
System.out.println("Ekran: " + display);
}

public void setSelectedProduct(Product selectedProduct) {
this.selectedProduct = selectedProduct;
}

public Product getSelectedProduct() {
return selectedProduct;
}

public void setPaymentMethod(PaymentMethod paymentMethod) {
this.paymentMethod = paymentMethod;
}

public void setAmountPaid(double amountPaid) {
this.amountPaid = amountPaid;
}

public double getAmountPaid() {
return amountPaid;
}

public boolean isPaymentSuccessful() {
return paymentSuccessful;
}

public void selectProduct(String productId) {
lock.lock();
try {
state.selectProduct(this, productId);
} finally {
lock.unlock();
}
}

public void insertPayment(PaymentMethod method, double amount) {
lock.lock();
try {
state.insertPayment(this, method, amount);
} finally {
lock.unlock();
}
}

public void processPayment() {
if (paymentMethod != null && selectedProduct != null) {
paymentSuccessful = paymentMethod.processPayment(selectedProduct.getPrice());
if (paymentSuccessful) {
setDisplay("Ödəniş uğurlu oldu");
dispenseProduct();
} else {
setDisplay("Ödəniş uğursuz oldu");
refundPayment();
setState(new IdleState());
}
}
}

public void dispenseProduct() {
lock.lock();
try {
if (selectedProduct != null && selectedProduct.getQuantity() > 0) {
setDisplay(selectedProduct.getName() + " paylanır");
selectedProduct.decrementQuantity();

// Paylamanı simulyasiya et
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}

setDisplay("Zəhmət olmasa " + selectedProduct.getName() + " götürün");

// Lazım olduqda qalığı hesabla
if (amountPaid > selectedProduct.getPrice()) {
returnChange();
} else {
setState(new IdleState());
}
} else {
setDisplay("Məhsul qalmayıb");
refundPayment();
setState(new IdleState());
}
} finally {
lock.unlock();
}
}

public void returnChange() {
lock.lock();
try {
if (amountPaid > selectedProduct.getPrice()) {
double changeAmount = amountPaid - selectedProduct.getPrice();
setDisplay("Qalıq qaytarılır: $" + String.format("%.2f", changeAmount));

if (paymentMethod instanceof CoinPayment) {
Map<Double, Integer> change = ((CoinPayment) paymentMethod).calculateChange(changeAmount);
if (change != null) {
// Qalığın təfərrüatını göstər
StringBuilder sb = new StringBuilder("Qalığınız: ");
for (Map.Entry<Double, Integer> entry : change.entrySet()) {
sb.append(entry.getValue()).append(" x $").append(entry.getKey()).append(", ");
}
setDisplay(sb.toString());
} else {
setDisplay("Dəqiq qalıq verə bilmərik, ödənişi geri qaytarırıq");
refundPayment();
}
}

// Qalıq qaytarmağı simulyasiya et
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}

if (state instanceof ReturningChangeState) {
((ReturningChangeState) state).changeReturned(this);
} else {
setState(new IdleState());
}
} else {
setState(new IdleState());
}
} finally {
lock.unlock();
}
}

public void refundPayment() {
if (paymentMethod != null && amountPaid > 0) {
paymentMethod.refund(amountPaid);
amountPaid = 0;
}
}

public void cancel() {
lock.lock();
try {
state.cancel(this);
} finally {
lock.unlock();
}
}

public void enterServiceMode() {
lock.lock();
try {
setDisplay("Xidmət rejiminə daxil olunur");
setState(new ServiceModeState());
} finally {
lock.unlock();
}
}

public void displayInventory() {
System.out.println("Cari İnventar:");
for (Product product : inventory.values()) {
System.out.println(product);
}
}
}

// İstifadə nümunəsi
public class VendingMachineDemo {
public static void main(String[] args) {
VendingMachine machine = new VendingMachine();

// İnventarı göstər
machine.displayInventory();

// Məhsul seç
machine.selectProduct("A1");

// Ödəniş et
machine.insertPayment(new CoinPayment(), 2.00);

// Nəticəni görmək üçün bir az gözlə
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}

// Başqa əməliyyat et
machine.selectProduct("C1");
machine.insertPayment(new CardPayment(), 1.00);

// Nəticəni görmək üçün bir az gözlə
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}

// Əməliyyatı ləğv et
machine.selectProduct("B2");
machine.cancel();
}
}

Thread Təhlükəsizliyi Mülahizələri​

  1. ReentrantLock: Avtomatik satış maşınında thread-safe əməliyyatları təmin etmək üçün istifadə olunur
  2. ConcurrentHashMap: Thread-safe məhsul inventar idarəetməsi üçün istifadə olunur
  3. State Pattern: Avtomatik satış maşını vəziyyət keçidlərinin atomik və ardıcıl olmasını təmin edir
  4. Dəyişməz Obyektlər: Məhsul ID-ləri, adları və qiymətləri dəyişməzdir

Ödəniş Emalı Mülahizələri​

  1. Çoxlu Ödəniş Üsulları: Sikkələr, əskinaslar və kartlar üçün dəstək
  2. Qalıq Hesablaması: Mövcud sikkələrdən istifadə edərək optimal qalığı hesablamaq alqoritmi
  3. Ödəniş Doğrulaması: Məhsulları paylamadan əvvəl ödənişin doğrulanması
  4. Geri Ödəmə Mexanizmi: Əməliyyatlar uğursuz olduqda ödənişləri geri qaytarmaq dəstəyi

İnventar İdarəetməsi​

  1. Məhsul İzləmə: Mövcud məhsulların və miqdarların izlənməsi
  2. Tükənmiş Məhsul İdarəetməsi: Tükənmiş məhsulların seçilməsinin qarşısının alınması
  3. İnventar Doldurulması: Xidmət rejimində yeni məhsulların əlavə edilməsi dəstəyi
  4. Məhsul Kateqoriyaları: Məhsulların növ və ya yerə görə təşkil edilməsi

Əlavə Xüsusiyyətlər​

  1. İstifadəçi İnterfeysi: İstifadəçi geri bildirimi üçün interaktiv ekran
  2. Qeyd Aparma: Audit və problemlərin həlli üçün əməliyyatları qeyd etmək
  3. Temperatur Nəzarəti: Tez xarab olan məhsullar üçün temperaturun izlənməsi və nəzarəti
  4. Uzaqdan İzləmə: İnventar və maşın statusunun uzaqdan yoxlanmasına imkan vermək
  5. Promosyon Təklifləri: Endirim və ya "bir al, bir pulsuz" təkliflərinin dəstəklənməsi