Programmare coesione e disaccoppiamento – 3

 

Proseguo la terza ed ultima parte del post fornendo altri suggerimenti su come programmare coesione e disaccoppiamento delle classi con relativi esempi, se non hai ancora letto i due precedenti post ti conviene leggerli prima di proseguire, li trovi qui e qui.

 

Non parlare con gli estranei ma solo con chi conosci

Esiste un modo per capire in tempo utile se il codice che scriviamo in un metodo può portare potenziali problemi, si chiama “Non parlare con gli estranei ma solo con chi conosci“, a seguire l’esempio:

public interface DataService
{
   Orders[] FindOrders(Customer customer);
}

public class Repository
{
   public DataService InnerService { get; set; }
}

public class UseOrders
{
   private Repository _repository;

   public UseOrders(Repository <span class="hiddenGrammarError" pre=""><span class="hiddenGrammarError" pre="">repository) 
   {
      _repository</span></span> = repository;
   }

   public void DoOrders(Customer customer) 
   {
      // La seguente riga viola la legge di Demetra
      Orders[] orders = _repository.InnerService.FindOrders(customer);
   }
}

Nel suddetto codice la classe DoOrders deve avere informazioni su Orders e ha un riferimento alla classe Repository la quale possiede un oggetto DataService; DoOrders attraversa Repository, mediante DataService, per invocare Repository.FindOrders e farsi restituire i dati desiderati.

In tal modo DoOrders invoca un metodo di un’altra classe tramite una sua proprietà, ciò vìola la legge di Demetra e, più in generale, non favorisce il disaccoppiamento poichè chi consuma Repository è fortemente legato alla sua implementazione; a seguire, invece, l’esempio “buono”:

public class Repository
{
   private DataService _service;

   public Repository(DataService <span class="hiddenGrammarError" pre="">service) 
   {
      _service</span> = service;
   }
   public Orders[] FindOrders(Customer customer)
   {    
      return _service.FindOrders(customer);
   }
}

public class UseOrders
{
   private Repository _repository;

   public UseOrders(Repository repository)
   {
      _repository = repository;
   }

   public void DoSomething(Customer customer) 
   {
      // Ora siamo nei confini della legge...
      Orders[] orders = _repository.FindOrders(customer);
   }
}

Questa volta si invoca direttamente FindOrders grazie alla ridotta complessità di Repository; ed è possibile effettuare modifiche a questa classe senza preoccuparsi della classe consumer poichè debolmente accoppiata.

 

Dillo, non chiedere

Un altro modo per rendere fortemente disaccoppiate due classi è di renderle autonome a compiere azioni con messaggi che ricevono o con le proprie informazioni ma non con metodi di altre classi; sto parlando del principio del “Dillo, non chiedere“, a seguire un breve esempio:

public class Purchase
{
   public double SubTotal { get; set; }
   public double Discount { get; set; }
   public double Total { get; set; }
}

public class Account 
{
   public double Balance { get; set;}
}

public class UseEntities 
{
   public void MakePurchase(Purchase purchase, Account account) 
   {
      purchase.Discount = purchase.SubTotal > 5000 ? .20 : 0;
      purchase.Total = purchase.SubTotal*(1 - purchase.Discount);

      if (purchase.Total < account.Balance) 
      {
         account.Balance -= purchase.Total;
      }
      else
      {
         rejectPurchase(purchase, "Sorry. Non hai denaro sufficiente.");
      }
   }
} 

Le classi Purchase e Account, come noterete, non hanno business logic che è invece innaturalmente posta nella classe UseEntities, ora il “buon” esempio:

public class Purchase
{
   private readonly double _subTotal;

   public Purchase(double subTotal) 
   {
      _subTotal = subTotal;
   }

   public double Total 
   {
      get 
      {
         double discount = _subTotal > 5000 ? .20 : 0;
         return _subTotal*(1 - discount);
      }
   }
}

public class Account 
{
   private double _balance;

   public void Deduct(Purchase purchase, PurchaseMessenger messenger) 
   {
      if (purchase.Total < _balance) 
      {
         _balance -= purchase.Total;
      }
      else
      {
         messenger.RejectPurchase(purchase, this);
      }
   }
}

public class UseEntities 
{
   public void MakePurchase(Purchase purchase, Account account) 
   {
      PurchaseMessenger messenger = new PurchaseMessenger();
      account.Deduct(purchase, messenger);
   }
}

Stavolta il codice della business rule è giustamente posizionato nelle classi Purchase e Account le quali sanno esattamente cosa fare, come fare e non devono chiedere nulla alle altre.

Inoltre si riduce drasticamente la complessità, l’accoppiamento e la potenziale duplicazione del codice grazie al fatto che la logica, essendo interna alle due classi, può essere riutilizzatata facilmente attraverso il sistema.

 

Conclusioni

Concludo la terza ed ultima parte suggerendo alcune domande da porvi. In tal modo capirete se il design che state effettuando sulle classi è ad un livello non ancora sufficientemente alto per ritenervi soddisfatti:

  • Il nome che avete dato alla classe è generico? Se non implementa un solo ruolo significa che gli state facendo fare troppe cose;
  • Il nome che avete dato alla classe è semplice? Se il nome è complicato significa che la classe è troppo complessa;
  • La vostra classe è piena zeppa di metodi? Significa che sta facendo cose diverse oltre quella principale, cercate di suddividerla in più classi;
  • Il vostro metodo accetta una marea di parametri di input? Significa che avete perso poco tempo a progettarlo, e probabilmente, al suo interno vi è una struttura condizionale ad albero. Molto meglio suddividerlo in più metodi.

Benissimo, ora dovresti sapere tutto su come programmare coesione e disaccoppiamento, se ti e’ piaciuta questa serie di post ti invito caldamente a condividerla con i tuoi amici e a commentarla. Inoltre, se vuoi approfondire maggiormente l’argomento dai una occhiata a questa recensione.

Infine, acquista uno dei libri che ti consiglio cliccando sulla sua immagine qui sotto e in pochissimo tempo lo avrai tra le mani. Grazie per la tua attenzione.

Acquista su Amazon.it

 

🔥163
Vota questo articolo

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *