www.tostring.it

Non mi ha voluto offrire la pizza… ma una birra riuscirò a scroccarla :D

Di chi sto parlando???

Del proprietario di questo blog: www.tostring.it

Troverete un sacco di interessanti e utilissimi articoli.

Buona lettura.

Tags:

LinqToXml: esempio con Dictionary

XML sorgente:

<?xml version="1.0" encoding="utf-8"?>
<Configurations>
  <ListFormStruct Name="ListFormPippo" DataFileName="DataFileNamePippo">
    <Struct>
      <Column Name="Column1" StartByte="0" Length="12" Type="String" />
<Column Name="Column2" StartByte="13" Length="10" Type="Int" /> <Column Name="Column3" StartByte="24" Length="10" Type="DateTime" /> <Column Name="Column4" StartByte="35" Length="10" Type="String" /> </Struct> </ListFormStruct> </Configurations>

 

Le entità che utilizzeremo sono:

public sealed class ColumnStruct
{
public int Length { get; set; }
public int StartByte { get; set; }
public DataType Type { get; set; }
}

 

per le colonne e:

public class ListFormStruct
{
    public string FileDataName { get; set; }
    public string Name { get; set; }
    public Dictionary<string, ColumnStruct> Columns { get; set; }
}

 

per la lista che conterrà anche l’elenco delle colonne.
Per la lista delle colonne abbiamo inserito una dictionary in modo da avere le colonne con una chiave univoca che sarà il nome della colonna stessa.

Ed ecco come leggeremo questo XML in modo da istanziare direttamente l’oggetto:

class Program
{
    static void Main(string[] args)
    {
        XDocument xdoc = XDocument.Load(@"..\..\XMLFile4.xml");

        ListFormStruct lfs = (from q in xdoc.Descendants("ListFormStruct")
                              select new ListFormStruct
                              {
                                  FileDataName = q.Attribute("DataFileName").Value,
                                  Name = q.Attribute("Name").Value,
                                  Columns =
                                  q
                                  .Element("Struct")
                                  .Elements("Column")
                                  .ToDictionary(c => (string)c.Attribute("Name"),
                                  c => new ColumnStruct()
                                  {
                                      Length = (int)c.Attribute("Length"),
                                      StartByte = (int)c.Attribute("StartByte"),
                                      Type = (string)c.Attribute("Type")
                                  })

                              }).FirstOrDefault();

        if (lfs != null)
        {
            Console.WriteLine("Name: {0}; FileDataName: {1}", lfs.Name, lfs.FileDataName);
            foreach (string colName in lfs.Columns.Keys)
            {
                Console.WriteLine("Column Name: {0}:", colName);
                ColumnStruct col = lfs.Columns[colName];
                Console.WriteLine(@"\tLength: {0}; StartByte: {1}; Type: {2}", col.Length, col.StartByte, col.Type);
                Console.WriteLine();
            }
        }
    }
}

 

Divertitevi

Tags:

Conditional Complexity: Replace Conditional Logic with Strategy

 

Continuiamo con lo smell: Conditional Complexity

400px-Control_flow_graph_of_function_with_two_if_else_statements.svg

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:

 conditional_complexity

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

possiamo semplificarne la leggibilità ma avremo tanti piccoli metodi che verranno utilizzati dall’algoritmo.

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
}

 

 refactored_conditional_complexity

 

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.

E non prendete come scusa: “il mio sistema ormai è troppo evoluto per poterne apportare queste migliorie. E’ troppo tardi.”

Se fosse realmente così non esisterebbe il Refactoring :)

Per questo post ho preso, molto spunto da libro Refactoring To Patterns di Joshua Kerievsky.