Sto facendo un carrello della spesa. Un carrello della spesa avrà un importo totale che devi pagare per i prodotti che hai selezionato.
Mi piacerebbe affrontare il problema usando entrambi gli OOP (incapsulandolo) e più anemicamente o funzionalmente (senza incapsulamento).
Sto utilizzando il modello di prodotto più semplice:
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
}
1. Utilizzo dell'incapsulamento (ovvero modellazione di domini avanzati)
TotalPrice può essere calcolato solo all'interno della classe del carrello degli acquisti.
a) Calcolo al volo
public class ShoppingCart
{
private List<Product> _products = new List<Product>();
public decimal TotalPrice { get; private set; }
public IReadOnlyCollection<Product> Products => _products;
public void AddProduct(Product product)
{
_products.Add(product);
TotalPrice += product.Price;
}
public void RemoveProduct(Product product)
{
_products.Remove(product);
TotalPrice -= product.Price;
}
}
b) Calcolo al volo con il metodo RecalculateTotalPrice
Sembra meno performante dell'approccio sopra o usando la proprietà calcolata. Stiamo eseguendo un ciclo su Aggiungi e Rimuovi.
public class ShoppingCart
{
private List<Product> _products = new List<Product>();
public decimal TotalPrice { get; private set; }
public void AddProduct(Product product)
{
_products.Add(product);
RecalculateTotalPrice();
}
public void RemoveProduct(Product product)
{
_products.Remove(product);
RecalculateTotalPrice();
}
private void RecalculateTotalPrice()
{
var totalPrice = 0m;
foreach (var product in _products)
{
totalPrice += product.Price;
}
TotalPrice = totalPrice;
}
}
c) Proprietà calcolata
Calcolato solo quando si accede a TotalPrice
. AddProduct
e RemoveProduct
non calcolati. Lo svantaggio è che non possiamo facilmente persistere con un ORM.
public class ShoppingCart
{
private List<Product> _products = new List<Product>();
public decimal TotalPrice => CalculateTotalPrice();
public void AddProduct(Product product)
{
_products.Add(product);
}
public void RemoveProduct(Product product)
{
_products.Remove(product);
}
private decimal CalculateTotalPrice()
{
var totalPrice = 0m;
foreach (var product in _products)
{
totalPrice += product.Price;
}
return totalPrice;
}
}
2. Nessun incapsulamento
Il nostro TotalPrice verrà calcolato al di fuori della classe.
public class ShoppingCart
{
public ICollection<Product> Products { get; set; } = new List<Product>();
public decimal TotalPrice { get; set; }
}
Il calcolo di TotalPrice può avvenire da qualsiasi luogo. Il vantaggio di questo è che possiamo disaccoppiare il calcolo dal carrello della spesa e persino avere una speciale classe calcolatrice per esso che può essere estesa con regole di calcolo personalizzate e così via. Alcune interfacce come IShoppingCartCalculator
.
Attualmente, sto usando l'approccio 1a, ma mi chiedevo se renderlo un POCO avrebbe comportato un codice più estensibile e gestibile gestito dall'esterno.
Nessun incapsulamento produce un codice più semplice che è più facile da seguire. C'è un livello aziendale in meno per mantenere . Stiamo perdendo i vantaggi dell'incapsulamento, ma stiamo acquisendo più possibilità e siamo più resistenti ai cambiamenti
Quindi la nostra pipeline funziona come (richiesta → gestore → invocazione di un metodo di servizio della calcolatrice) anziché (richiesta → gestore → richiamo di un metodo ShoppingCart)
Dovrei rinunciare all'incapsulamento per ottenere più possibilità e più classi riutilizzabili o persino funzioni in caso di programmazione funzionale?
L'incapsulamento è una cosa del passato che non ha soddisfatto le aspettative della realtà?
Sono aperto a qualsiasi suggerimento.