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
-
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
-
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
- Məhsul İnventarı: Mövcud məhsulları və onların miqdarlarını idarə edir
- Ödəniş Emal Edəni: Müxtəlif ödəniş üsullarını idarə edir
- Məhsul Paylaşdırıcı: Seçilmiş məhsulları paylayır
- Qalıq Qaytarıcı: Lazım olduqda qalığı qaytarır
- Ekran: İstifadəçiyə mövcud məhsulları, qiymətləri və mesajları göstərir
- 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:
- Gözləmə: Başlanğıc vəziyyət, istifadəçi qarşılıqlı təsirini gözləyir
- Məhsul Seçildi: İstifadəçi məhsul seçib
- Ödəniş Emalı: Maşın ödənişi emal edir
- Paylama: Maşın məhsulu paylayır
- Qalıq Qaytarma: Maşın qalığı qaytarır
- 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
- ReentrantLock: Avtomatik satış maşınında thread-safe əməliyyatları təmin etmək üçün istifadə olunur
- ConcurrentHashMap: Thread-safe məhsul inventar idarəetməsi üçün istifadə olunur
- State Pattern: Avtomatik satış maşını vəziyyət keçidlərinin atomik və ardıcıl olmasını təmin edir
- 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
- Çoxlu Ödəniş Üsulları: Sikkələr, əskinaslar və kartlar üçün dəstək
- Qalıq Hesablaması: Mövcud sikkələrdən istifadə edərək optimal qalığı hesablamaq alqoritmi
- Ödəniş Doğrulaması: Məhsulları paylamadan əvvəl ödənişin doğrulanması
- Geri Ödəmə Mexanizmi: Əməliyyatlar uğursuz olduqda ödənişləri geri qaytarmaq dəstəyi
İnventar İdarəetməsi
- Məhsul İzləmə: Mövcud məhsulların və miqdarların izlənməsi
- Tükənmiş Məhsul İdarəetməsi: Tükənmiş məhsulların seçilməsinin qarşısının alınması
- İnventar Doldurulması: Xidmət rejimində yeni məhsulların əlavə edilməsi dəstəyi
- Məhsul Kateqoriyaları: Məhsulların növ və ya yerə görə təşkil edilməsi
Əlavə Xüsusiyyətlər
- İstifadəçi İnterfeysi: İstifadəçi geri bildirimi üçün interaktiv ekran
- Qeyd Aparma: Audit və problemlərin həlli üçün əməliyyatları qeyd etmək
- Temperatur Nəzarəti: Tez xarab olan məhsullar üçün temperaturun izlənməsi və nəzarəti
- Uzaqdan İzləmə: İnventar və maşın statusunun uzaqdan yoxlanmasına imkan vermək
- Promosyon Təklifləri: Endirim və ya "bir al, bir pulsuz" təkliflərinin dəstəklənməsi