Ə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