Problema:
Esiste un metodo di controllo con una logica condizionale composta da diverse varianti i quali valori si conosceranno solamente a runtime.La stessa logica di controllo viene riprodotta in varie classi affinchè si ottenga il valore desiderato.
Un esempio di logica errata:
Di seguito un esempio di logica errata, sul quale lavoreremo:
public double Capital()
{
if (expiry == null && maturity != null)
return commitment * Duration() * RiskFactor();
if (expiry != null && maturity == null)
{
if (GetUnusedPercentage() != 1.0)
return commitment * GetUnusedPercentage() * Duration() *
RiskFactor();
else
return (OutStandingRiskAmount() * Duration() *
RiskFactor()) +
(UnUsedRiskAmount() * Duration() * UnUsedRiskFactor());
}
return 0.0;
}
Il codice di sopra è raffigurabile nella seguente maniera:
Motivazione:
Nel momento in cui iniziamo a scrivere delle logiche di controllo, solitamente, si parte da un paio di controlli che, se li volessimo togliere, porterebbero solamente a complicare la leggibilità del codice stesso.Il problema nasce quando i controlli da effettuare continuano a crescere, senza preoccuparsi di migliorare la leggibilità del codice tramite l’applicazione di un Pattern ne, tanto meno, tentando di rimpicciolire le condizioni.
Soluzione:
Tramite il pattern Strategy semplificheremo il mantenimento delle varianti che potranno nascere.
In pratica, utilizzandolo, avremo tutte le varianti in un’unico punto del codice.
Un’altra soluzione applicabile è tramite il pattern Decorator.
Le condizioni vengono solitamente scritte per capire quale algoritmo utilizzare; tramite:
- Decompose Conditional
- Compose Method
Questo è il codice che otteniamo applicando il pattern Strategy:
class Program
{
static void Main(string[] args)
{
ILoan loan1 = new StandardLoan();
loan1.Capital();
}
}
public class CapitalStrategy
{
private readonly ILoan _loan;
private object RiskFactor;
private object UnusedRiskFactor;
public CapitalStrategy(ILoan loan, object riskFactor, object unusedRiskFactor)
{
_loan = loan;
RiskFactor = riskFactor;
UnusedRiskFactor = unusedRiskFactor;
}
public double Capital()
{
switch (_loan.LoanType)
{
case LoanType.Standard:
return _loan.Commitment * _loan.Duration() * RiskFactorFor();
case LoanType.NonStandardUnUsedPercentage:
return _loan.Commitment * _loan.UnusedPercentage() * _loan.Duration() * RiskFactorFor();
case LoanType.NonStandardUnUsedPercentageDotOne:
return (_loan.OutstandingRiskAmount() * _loan.Duration() * RiskFactorFor())
+ (_loan.UnusedRiskAmount() * _loan.Duration() * UnusedRiskFactorFor());
default:
return 0.0;
}
}
private double UnusedRiskFactorFor()
{
return UnusedRiskFactor.GetFactor(_load).GetUnusedRiskFactor();
}
private double RiskFactorFor()
{
return RiskFactor.GetFactor(_load).GetRiskFactor();
}
}
public enum LoanType
{
Standard,
NonStandardUnUsedPercentage,
NonStandardUnUsedPercentageDotOne
}
public interface ILoan
{
double Capital();
LoanType LoanType { get; }
double Commitment { get; }
double Duration();
double UnusedPercentage();
double OutstandingRiskAmount();
double UnusedRiskAmount();
}
public class StandardLoan : ILoan
{
readonly CapitalStrategy _capitalStrategy;
private object myRiskFactor = new object();
private object myUnusedRiskFactor = new object();
public StandardLoan()
{
_capitalStrategy = new CapitalStrategy(this, myRiskFactor, myUnusedRiskFactor);
}
#region ILoan Members
public double Capital()
{
return _capitalStrategy.Capital();
}
public LoanType LoanType
{
get { return LoanType.Standard; }
}
public double Commitment
{
get { throw new NotImplementedException(); }
}
public double Duration()
{
throw new NotImplementedException();
}
public double UnusedPercentage()
{
throw new NotImplementedException();
}
public double OutstandingRiskAmount()
{
throw new NotImplementedException();
}
public double UnusedRiskAmount()
{
throw new NotImplementedException();
}
#endregion
}
public class NonStandardUnUsedPercentage : ILoan
{
readonly CapitalStrategy _capitalStrategy;
private object myRiskFactor = new object();
private object myUnusedRiskFactor = new object();
public NonStandardUnUsedPercentage()
{
_capitalStrategy = new CapitalStrategy(this, myRiskFactor, myUnusedRiskFactor);
}
#region ILoan Members
public double Capital()
{
return _capitalStrategy.Capital();
}
public LoanType LoanType
{
get { return LoanType.NonStandardUnUsedPercentage; }
}
public double Commitment { get; private set; }
public double Duration()
{
throw new System.NotImplementedException();
}
public double UnusedPercentage()
{
throw new System.NotImplementedException();
}
public double OutstandingRiskAmount()
{
throw new System.NotImplementedException();
}
public double UnusedRiskAmount()
{
throw new System.NotImplementedException();
}
#endregion
}
Benefici e non + Rende gli algoritmi più chiari diminuendo o eliminando le condizioni logiche. + Semplifica le classi muovendo le variazioni dell’algoritmo nella gerarchia. + Abilita la possibilità di swappare tra un algoritmo e un’altro a run-time. - Complica il design. - Complica la visione di come l’algoritmo ottiene/riceve i dati. |
Se fosse realmente così non esisterebbe il Refactoring :)
Per questo post ho preso, molto spunto da libro Refactoring To Patterns di Joshua Kerievsky.
No comments:
Post a Comment