Con questo articolo cominceremo a rivedere alcuni dei design pattern più importanti e dei principi di di programmazione più usati nello sviluppo di applicazioni.

Nei vari meetup che stiamo svolgendo in collaborazione con Codemotion, sempre più spesso capita di fare riferimento a questi argomenti e da qui la decisione di redigere una serie di articoli che vanno a rivedere questui aspetti.

Nello specifico iniziamo rivedendo i primi 5 principi dello sviluppo di software OOP (Object-Oriented Programming) che vengono detti SOLID Principles (Principi Solidi).

SOLID rappresenta un acronimo formato da:

  • SRP : Single Responibility principle
  • OCP : Open/closed principle
  • LSP  : Liskov substitution principle
  • ISP    : Interface segregation principle
  • DIP    : Dependency inversion principle
Il principio SRP afferma che "ogni classe deve avere una e una sola ragione per cambiare".

Nello specifico questo comporta che ogni classe o modulo deve avere la responsibilità su una singola funzionalità e questa responsabilità deve essere incapsulata in essa.

Immaginiamo di avere una nostra classe utente:

    public class User
    {
        public string Username { get; set; }
        public string Password { get; set; }
        public string Email { get; set; }
    }

e una classe di servizio che implementa le funzionalità di login e registrazione di un utente alla nostra applicazione.

 public class UserHelper
    {
        public void Register(User user)
        {
            try
            {
                if (!ValidateEmail(user.Email))
                    throw new ArgumentException("Email not valid");

                using (var userDal = new UserDal())
                {
                    userDal.Register(user);
                }

                SendEmail(new MailMessage("system@dotnetcode.it", user.Email,
                    "Registrazione dotnetcode.it", "Registrazione effettuata con successo"));

            }
            catch (Exception ex)
            {
                System.IO.File.WriteAllText(@"c:\Error.log", ex.ToString());
                throw;
            }
        }

        public string Login(User user)
        {
            try
            {
                string sessionToken;
                using (var userDal = new UserDal())
                {
                    sessionToken = userDal.Login(user);
                }

                return sessionToken;
            }
            catch (Exception ex)
            {
                System.IO.File.WriteAllText(@"c:\Error.log", ex.ToString());
                throw;
            }
        }

        public bool ValidateEmail(string email)
        {
            return email.Contains("@");
        }
        public void SendEmail(MailMessage message)
        {
            using (var smtpSender = new SmtpClient())
            {
                smtpSender.Send(message);
            }
        }
    }

    public class UserDal:IDisposable
    {
        public void Register(User user)
        {
        }

        public string Login(User user)
        {
            return Guid.NewGuid().ToString();
        }

        public void Dispose()
        {
            
        }
    }

Prendiamo in esempio il metodo per effettuare la registrazione dell'utente alla nostra applicazione: questo metodo riceve in input la email dell'utente si occupa di validarla e in caso di esito positivo invia una email all'utente stesso e si occupa di creare sul nostro sistema il nuovo utente.

La validazione della email dell'utente e l'invio della mail di conferma dell'avvenuta registrazione, non fanno propriamente parte del dominio funzionale legato alla registrazione e autenticazione di un utente, quindi in caso cambi la modalità di validazione delle email e/o la modalità di invio della email di conferma della avvenuta registrazione, dovrà cambiare la nostra classe violando così il principio SRP. 

Modifichiamo la nostra classe per eliminare questa violazione, creando una classe che si occuperà di gestire le operazioni riguardanti le email.

    public class EmailHelper
    {
        public static bool ValidateEmail(string email)
        {
            return email.Contains("@");
        }
        public static void SendEmail(MailMessage message)
        {
            using (var smtpSender = new SmtpClient())
            {
                smtpSender.Send(message);
            }
        }
    }

e quindi di conseguenza la nostra classe UserHelper

public class UserHelper
    {
        public void Register(User user)
        {
            try
            {
                if (!EmailHelper.ValidateEmail(user.Email))
                    throw new ArgumentException("Email not valid");
                using (var userDal = new UserDal())
                {
                    userDal.Register(user);
                }
                EmailHelper.SendEmail(new MailMessage("system@dotnetcode.it", user.Email,
                    "Registrazione dotnetcode.it", "Registrazione effettuata con successo"));
            }
            catch (Exception ex)
            {
                System.IO.File.WriteAllText(@"c:\Error.log", ex.ToString());
                throw;
            }
        }
        public string Login(User user)
        {
            try
            {
                string sessionToken;
                using (var userDal = new UserDal())
                {
                    sessionToken = userDal.Login(user);
                }
                return sessionToken;
            }
            catch (Exception ex)
            {
                System.IO.File.WriteAllText(@"c:\Error.log", ex.ToString());
                throw;
            }
        }
    }

In realtà, anche così, la nostra classe viola lo stesso l' SRP: il log degli eventuali errori non è una funzionalità direttamente legata alla gestione delle utenze della nostra applicazione e può cambiare in base a varie esigenze.

Scorporiamo anche questa funzionalità, creando una apposita classe che gestisce le nostre attività di log 

    public class LogHelper
    {
        public static void Log(Exception ex)
        {
            System.IO.File.WriteAllText(@"c:\Error.log", ex.ToString());
        }
    }

e modifichiamo la nostra classe di servizio di conseguenza.

 public class UserHelper
    {
        public void Register(User user)
        {
            try
            {
                if (!EmailHelper.ValidateEmail(user.Email))
                    throw new ArgumentException("Email not valid");
                using (var userDal = new UserDal())
                {
                    userDal.Register(user);
                }
                EmailHelper.SendEmail(new MailMessage("system@dotnetcode.it", user.Email,
                    "Registrazione dotnetcode.it", "Registrazione effettuata con successo"));
            }
            catch (Exception ex)
            {
                LogHelper.Log(ex);
                throw;
            }
        }
        public string Login(User user)
        {
            try
            {
                string sessionToken;
                using (var userDal = new UserDal())
                {
                    sessionToken = userDal.Login(user);
                }
                return sessionToken;
            }
            catch (Exception ex)
            {
                LogHelper.Log(ex);
                throw;
            }
        }
    }
}

A questo punto la nostra classe è conforme al SRP. 

Quali sono i vantaggi nell' uso di questo principio?

  • Riduzione della complessità del codice
  • Notevole miglioramento della riusabilità, leggibilità ed estensibilità dello stesso.
  • Migliore manutenibilità e testabilità dello stesso.
  • Migliore disaccoppiamento tra le classi



Autore:


blog comments powered by Disqus

 

Calendar

<<  novembre 2017  >>
lunmarmergiovensabdom
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

Vedi i post nel calendario più grande

Category list