lunedì 28 gennaio 2013

Extreme Design: una piccola provocazione (4° parte)


Nel precedente post abbiamo implementato il deposito, affrontiamo in questo post l'aggiunta della funzionalità per prelevare denaro.

Prima di procedere dobbiamo fare una premessa, il prelievo ha delle analogie al deposito per l'interazione con l'operatore. Infatti dopo l'esecuzione di richiesta di prelievo, l'oggetto Withdrawal notifica all'operatore che vuole sapere qual è l'importo desiderato da prelevare.
Ovviamente il prelievo non potrà essere confermato se la richiesta è superiore all'importo disponibile, questa problematica ci obbliga a realizzare due tipologie diverse di notifiche.

Vediamo come realizzare il tutto:

   // interfaccia per richiedere all'operatore l'importo
   public interface IWithdrawalInput
   {
      void Return(string value);
   }

Withdrawal deve rispettare il contratto con IOperation per fare parte della liste di operazioni disponibile all'operatore, inoltre come per Deposit interagisce con la View (IViewWithdrawal) tramite IWithdrawalInput per richiedere l'importo da prelevare. Tramite la notifica Return viene letto e trasformato da stringa ad Amount l'importo che si vuole prelevare e inviato ad Account.

   public class Withdrawal : IOperation, IWithdrawalInput, IWithdrawalResponse
   {
      private readonly IAccount account;
      private readonly IViewWithdrawal view;

      public Withdrawal(IAccount account, IViewWithdrawal view)
      {
         this.account = account;
         this.view = view;
      }

      public void Display(Action<string> action)
      {
         action("Prelievo");
      }

      public void Execute()
      {
         view.Response(string.Format("Inserire importo da prelevare"));
         view.Request(this);
      }

      void IWithdrawalInput.Return(string value)
      {
         decimal amount;
         if (decimal.TryParse(value, out amount))
         {
            account.Withdrawal(new Amount(amount),this);
            return;
         }
         view.Response(string.Format("Importo inserito non valido"));
         Execute();
      }

      void IWithdrawalResponse.Completed()
      {
         view.Response(string.Format("Prelievo eseguito."));
      }

      void IWithdrawalResponse.InsufficientCredit()
      {
         view.Response(string.Format("Importo non disponibile."));
      }
   }

Vediamo come View notifica l'importo da prelevare:

   public interface IViewWithdrawal : IViewResponse
   {
      void Request(IWithdrawalInput withdrawal);
   }

   public class View : IViewOperation, IViewDeposit, IViewWithdrawal
   {
      void IViewResponse.Response(string output)
      {
         Console.WriteLine(output);
      }

      void IViewOperation.Request(IOperationsInput operations)
      {
         operations.Return(Console.ReadLine());
      }

      void IViewDeposit.Request(IDepositInput deposit)
      {
         deposit.Return(Console.ReadLine());
      }

      void IViewWithdrawal.Request(IWithdrawalInput withdrawal)
      {
         withdrawal.Return(Console.ReadLine());
      }
   }

Vediamo come Withdrawal collabora con Account.

   public interface IAccount
   {
      void BalanceRequest(IBalanceResponse balance);
      void Deposit(Amount amount);
      void Withdrawal(Amount amount, IWithdrawalResponse withdrawal);
   }

   public class Account : IAccount
   {
      private Amount current;

      public Account()
      {
         current = new Amount(0);
      }

      public void BalanceRequest(IBalanceResponse balance)
      {
         balance.Return(current);
      }

      public void Deposit(Amount amount)
      {
         current = new Amount(current.Value + amount.Value);
      }

      public void Withdrawal(Amount amount, IWithdrawalResponse withdrawal)
      {
         if (current.Value >= amount.Value)
         {
            current = new Amount(current.Value - amount.Value);
            withdrawal.Completed();
            return;
         }
         withdrawal.InsufficientCredit();
      }
   }

Quando Account riceve la richiesta di prelievo, oltre l'importo vuole sapere l'oggetto a cui notificare lo stato della richiesta:

   // contratto per notificare all'operatore se il prelievo 
   // è confermato o se il credito non è sufficiente
   public interface IWithdrawalResponse
   {
      void Completed();
      void InsufficientCredit();
   }

Analizziamo lo stack per vedere i due tipi di feedback, conseguenza della richiesta di prelievo:

Caso 1: prelievo superiore al credito disponibile

   Withdrawal.IWithdrawalResponse.CreditInsufficient()
   Account.Withdrawal(Amount amount, IWithdrawalResponse withdrawal)
   Withdrawal.IWithdrawalInput.Return(string value)
   View.IViewWithdrawal.Request(IWithdrawalInput withdrawal)
   Withdrawal.Execute()
   Operations.IOperationsInput.Return(string value)
   View.IViewOperations.Request(IOperationsInput operations)
   Operations.Display()

Caso 2: prelievo valido

   Withdrawal.IWithdrawalResponse.Completed()
   Account.Withdrawal(Amount amount, IWithdrawalResponse withdrawal)
   Withdrawal.IWithdrawalInput.Return(string value)
   View.IViewWithdrawal.Request(IWithdrawalInput withdrawal)
   Withdrawal.Execute()
   Operations.IOperationsInput.Return(string value)
   View.IViewOperations.Request(IOperationsInput operations)
   Operations.Display()

Anche per lo sviluppo di Withdrawal abbiamo:
  • tutti i metodi sono void
  • abbiamo un if per gestire il parse da stringa ad amount
  • abbiamo un if per verificare il prelievo richiesto
  • solo un metodo ha due parametri
  • abbiamo isolato le notifiche nello scope di competenza
  • tutto il codice può essere messo sotto test

Nessun commento:

Posta un commento