Home » Approfondimenti » Computer Science » Design pattern
  []

  1. Come é un buon codice
  2. Design principles
  3. Design practices oppure pratiche a livello di progettazione
  4. Design patterns oppure pratiche a livello di codice (Singleton, Prototype, Factory, Builder, Facade, Proxy, Strategy, Iterator, Observer, Mediator, State)
  5. Architecture patterns oppure pratiche a livello di intero progetto (Client/Server, DDD, SOA; MVC…)
  6. Architecture styles oppure principi architetturali da seguire (REST, SOAP, IoC; Microservices)

Come è un buon codice?

  • Funzionale.
  • Testabile.
  • Leggibile.
  • Rispecchia principi SOLID.
  • Rispecchia principi della buona programmazione.

 

Design principles (Principi di programmazione  o fattori guida)  

  • Idea alla base =>
    • Aiutano ad ottenere un software architetturalmente buono.
    • Nascondere la complessità dei moduli componendo la soluzione.
    • Fornire componenti o funzioni facilmente adottabili che potremmo riutilizzare anche con altre applicazioni.
  • SOLID (C# article)
    • Single responsibility principle (Responsabilité unique): une classe, une methode, une fonction doit avoir une et une seule responsabilité
      • Quando si sviluppa un metodo è meglio che questi faccia una e una sola operazione.
    • Open/Closed (Ouvert/Fermé): une classe doit être ouverte à l’estension mais fermée à la modification
    • Liskov substitution (Substitution de Liskov): une instance T doit pouvoir être remplacée par une istante G (G sous-type de T) sans que cohérence du programme change.
    • Interface segregation (Sègrègation des interfaces): préférer plusieurs interfaces spécifiques pour chaque client plutôt qu’une seule générale.
    • Dependency Inversion (Inversion des dèpendances):
      • Cosa è?
        • Bisogna dipendere dalle astrazioni e non dalle implementazioni.
        • Implementa la IoC.
        • Implementa la Dependency Injection.
        • Invece di inizializzare un’oggetto che serve alla nostra classe direttamente in essa è meglio delegarne la creazione al di fuori.
      • Perché abbiamo bisogno?
        • Permette di avere un sistema disaccoppiato.
        • Permette di centralizzare i cambiamenti e avere quindi un’architettura migliore.
      • Prendendo l’esempio di MVC.
        • Sappiamo che il gran numero di logica è messa nei controlli.
        • Piuttosto che inserire tutta questa logica (logging, validation, dal… ) direttamente negli stessi
          • Meglio creare dei componenti da poter riusare nei vari controller.
          • Meglio che i controlli si interfaccino con tali componenti invece che interagire direttamente con un’implementazione stretta.
        • Così facendo, è possibile modificare tali componenti senza avere conseguenze nei controller che li usano.

  • Separation of concern :
    • Uno stile di implementazione del codice in un modo pulito dove per esempio la business logic e l’UI non dovrebbero essere mischiati cioé risiedere in file separati.
  • RIP
    • Replace IF with polymorphism.
  • Least Knowledge
    • Uh componente non dovrebbe conosce i dettagli degli altri componenti.
  • Simple factory
    • Spostare la parola chiave “NEW” in una classe centralizzata.
  • Lazy loading
    • Load objects on demand.
  • KISS (Keep it simple, stupid)
    • Raccomanda la semplicità. Cioé evitare nel limite del possibile le cose non indispensabilii.
  • DRY (Don’t repeat yourself)
    • Evitare la ridondanza facilitando allo stesso tempo: la manutenzione,  i test e il debug .
    • Se c’è una certa operazione che viene ripetuta più volte all’interno del codice, è meglio pensare ad un refactoring per centralizzare questa funzionalità.
  • Law of demeter
    • Parlare solo agli amici immediati
    • Un oggetto A puo’ richiedere un servizio da un oggetto B ma A non dovrebbe utilizzare B per accedere a un terzo oggetto e richiederne i servizi.
  • MoSCoW
    • Technique visant à prioriser des besoins ou des exigences en matière d’assistance à maîtrise d’ouvrage et de développement logiciel
    • M : must have this, c’est-à-dire ‘doit être fait’ (vital).
    • S : should have this if at all possible, c’est-à-dire devrait être fait dans la mesure du possible (essentiel).
    • C : could have this if it does not affect anything else, pourrait être fait dans la mesure où cela n’a pas d’impact sur les autres tâches (confort).
    • W : won’t have this time but would like in the future, ne sera pas fait cette fois mais sera fait plus tard (luxe, c’est votre zone d’optimisation budgétaire).
  • YAGNI (You ain’t gonna need it) (Vous n’en aurez pas besoin)
    • C’est un principe d’extreme programming qui déclare que les programmeurs ne devraient pas ajouter de fonctionnalité à un logiciel tant que celle-ci n’est pas absolument nécessaire.

 

Design practices (Pratiche di progettazione)

  • OOP =>
    • Aiuta a risolvere problemi di sviluppo noti (RESM).
    • Consente di estendere e mantenere le applicazioni in un modo migliore rispetto all’approccio monolitico.
  • Domain model =>
    • Standardizza e centralizza il modello dati dell’applicazione.
    • Usando i domain model (o oggetti) per inviare e ricevere dati attraverso i livelli ogni volta che è necessario, in modo che l’applicazione abbia lo stesso formato di dati ovunque.
  • Layer =>
    • In base alla funzionalità dei componenti, suddividili in diversi livelli come
      • Accesso ai dati (DAL).
      • Business Logic.
      • Helper o Utility .
      • UI.
  • Interfacce =>
    • Utilizzare sempre l’astrazione per ottenere lo sviluppo di componenti ad accoppiamento debole.

 

Design Patterns (Strategie a livello di codice)

  • Offrono soluzioni standard e testate =>
    • Per rispondere a problemi ricorrenti e tipici della programmazione OOP.
    • Riconosciute come buone pratiche in risposta a un problema.
  • Tipi di design pattern
    • Creational (corrispondono alla fase di istanziazione nella OOP, come gli oggetti sono creati/istanziati).
    • Structural  (corrispondono alla fase di progettazione/template nella OOP phase , come gli oggetti sono strutturati).
    • Behavioral (corrispondono alla fase di esecuzione nella OOP, come gli oggetti comunicano tra di loro).
  • Creational (Singleton, Prototype, Factory, Builder) =>
    • Singleton :
      • Si lavora con una sola istanza di una classe.
      • Se si lavora con multithread è necessario gestire la concorrenza.
      • Serve per mantenere lo stato in memory di un oggetto per tutta la durata dell’applicazione.
      • Un metodo semplice per creare una classe Singleton in c# è tramite il meccanismo built-in di dependency injection.=
        • //program.cs
          //Creare un'implementazione dell'interfaccia desiderata e registrarla nel DI container
          services.AddSingleton<IUserManager, UserManager>();
      • Come creare una classe singleton:
        • Definire il costruttore come privato.
        • Definire metodi static.
        • [Opzionalmente] E’ possibile esporre una proprietà che ritorna un’istanza delle classe.
      • Esempio:
        • //La funziona che recupera tale oggetto e 
          // prima di ritornarlo verifica che non sia già stato inizializzato 
          // solo in quel caso lo inizializza
          public class clsSingleton
          {
             private static int counter;
          
             private static readonly Lazy<clsSingleton> istance = new Lazy<clsSingleton>(() => new clsSingleton());
             public static clsSingleton Istance = istance.value;
             
             private clsSingleton()
             {
             }
          
             public static void Hit()
             {
                 counter++;
             }
          
          
             public static int getTotalHits()
             {
                return counter;
             }
          }
    • Prototype :
      • Creare un oggetto indipendente sulla base di un’altro per estenderne le proprietà, copiandone l’esatto stato.

      • Deep cloning 
        • E’ clonato l’oggetto parent più tutti gli oggetti da lui contenuti.
        • Per ottenerlo è necessario procedere clonando tutti gli oggetti figli.
      • Shallow cloning
        • E’ copiato solo l’oggetto parent.
      • Esempio :
        • //Javascript
          const Zombie = {
             eatBeans() {
               return 'yum';
             }
          }
          
          const chad = Object.create(zombie, { name: {value: chad});
          Object.getPrototypeOf(chad)
          
          
          //c#
          //Classe da clonare
          var user = new User("Nike", 34);
          var userCloned = user.Clone() as IUser;
          public class Customer : IClonable  
          {
             public string Name;
             public int Age;
          
             public Customer(string name, int age)
             {
                 this.Name = name;
                 this.Age = age;
             }
             public Customer Clone()
             {
                return (Customer) this.MemberwiseClone();
             }
          }
          
          Customer customerCloned = new Customer(); //creo una copia del primo oggetto
          customerCloned .name = "myname"; //setto i valori del primo oggetto
          Customer customerclone; //creo una copia del secondo oggetto
          customerclone = customerCloned.getClone(); //copio il primo oggetto nel secondo
    • Factory :
      • Invece di usare la parola chiave new per istanziare un oggetto si usa un metodo o una funzione che lo fa per te.
      • Serve per non dovere ripetere più volte uno stesso codice.
      • Server per organizzare il codice.
      • Factory, Abstract Factory, Method Factory.
      • Il metodo non ritorna un valore concreto ma un recipiente.
      • class IosButton() : BaseButton { } ; 
        class AndroidButton() : BaseButton { } ; 
        
        //Design pattern: Factory 
        class MyFactory { 
           private static Dictionary<string, BaseButton> osTypesDict = new Dictionary<string, BaseButton>(); 
        
           public static BaseButton createButton(string osType) { 
               //Design pattern: Lazy loading 
               if(osTypesDict.count == 0) 
               { 
                   osTypesDict.Add("Ios", new IosButton()); 
                   osTypesDict.Add("Android", new AndroidButton()); 
               } 
               
               //Design pattern: RIP replace if with poly 
               return osTypesDict["osType"]; 
           } 
        } 
        //Consuming MyFactory class 
        //UI 
        const button1 = MyFactory.createButton(osType);
      • Può essere implementato anche tramite l’uso di un delegate :
        • var calculator = CreateCalculator();
          console.writeline(calculator(2))  //84
          
          Func<int, int> CreateCalculator()
          {
             var factor = 42;
             return (n) => n * factor;
          }
    • Builder :
      • Da usare
        • Se il processo di inizializzazione di una classe è particolarmente complesso.
        • Se devo disaccoppiare la rappresentazione dal processo di inizializzazione (ho bisogno di più rappresentazioni).
      • Non inizializzo una classe con tutte le proprietà in un solo passaggio (nel costruttore) 
      • Creo un metodo per ogni proprietà della classe da inizializzare (builder)
      • Li chiamo uno dopo l’altro o nella sequenza desiderata per creare l’instanza desiderata detta product (director)
      • Posso avere così ottenere così diverse rappresentazioni dello stesso oggetto (product)
      • In JQuery è spesso usato questo pattern

      • Esempio:
        • //program.cs
          
          services.AddSingleton<IUser>(new User.Builder()
                                               .WithName("Nike")
                                               .WithAge(29)
                                               .Build());
          
          
          //user.cs
          
          public interface IUser
          {
              public string Name {get;} 
              public int Age {get;} 
          }
          
          class User : IUser
          {
             private string name;
             private int age;
          
             public string Name {
                get { return name; }
             }
          
             public string Age {
                get { return age; }
             }
          
             private new()
             {
             }
          
             public Builder 
             {
                private readonly User user;
                
                public Builder()
                {
                    user = new User();
                }
          
          
                public Builder WithName(string name)
                {
                    user.name = name;
                    return this;
                } 
          
                public Builder WithAge(int age)
                {
                    user.age = age;
                    return this;
                } 
          
                public Build()
                {
                    return user;
                }
             }
          }
  • Structural (Facade, Proxy) =>

    • Facade :
      • Lo scopo è esporre un’interfaccia (servizio) unificata invece che esporre le interfacce dei singoli sottoinsiemi che vengono usati all’interfaccia principale.
      • Orchestratore =>
        • Ho una classe (al top del mio sistema) che mostra all’esterno un’interfaccia semplice.
        • Nascondendo le implementazioni complesse che ci stanno dietro.
      • Sotto sistemi =>
        • Ho una serie di sottosistemi che vengono usati all’interno della mia classe orchestrandoli come necessario. 
      • Evita che il client debba fare molte chiamate a i vari sottosistemi a causa dell’esposizione di troppi sottoservizi in maniera troppo granulare.
      • Un esempio sono i compiler che fanno un sacco di cose ma espongono solo alcune funzionalità semplici da usare.
      • Permette di comunicare secondo uno standard.
      • Permette di semplificare la UI delegando della logica alla classe orchestratore.
      • JQuery usa spesso questo pattern.

      • Esempio:
        • Esempio di facade pattern:
          Implementare un unico controller che richiami più servizi (sottosistemi) piuttosto che più controller (uno per ogni servizio).
    • Proxy :
      • Lo scopo di questo pattern è fornire un placeholder per un altro oggetto e gestirne l’accesso.
      • Non viene esposto al client direttamente un oggetto ma il suo proxy. In modo tale si possa controllare come l’end user acceda all’oggetto stesso.
      • Classe Proxy :
        • Invece di interagire direttamente con la classe che contiene i dati.
        • Uso una classe proxy (che funziona come un’interfaccia) che punta verso la vera classe contenente i dati.
        • Sostituisco la classe richiesta con un sostituto (proxy).
        • E’ possibile allora creare molti classi proxy che puntano verso il grosso oggetto in memoria .
        • Evitando così di doverlo duplicare, migliorando le prestazioni.
      • Classe Subject :
        • Definisce un’interfaccia comune tra la classe proxy e la classe reale così che ovunque la classe reale è attesa è possibile usare la classe proxy.
      • Classe RealSubject :
        • Definisce l’oggetto reale che il proxy definisce.
      • Reactivity system in VUE.js
      • I proxy sono utili quando si lavora con oggetti molto grossi che costa duplicarli in memoria.

      • Esempio :
        • using System;
          
          namespace Proxy.RealWorld
          {
            /// <summary>
            /// Proxy Design Pattern
            /// </summary>
          
            public class Program
            {
                 public static void Main(string[] args)
                 {
                    // Create math proxy
                    MathProxy proxy = new MathProxy();
          
                    // Do the math
                   Console.WriteLine("4 + 2 = " + proxy.Add(4, 2));
                   Console.WriteLine("4 - 2 = " + proxy.Sub(4, 2));
                   Console.WriteLine("4 * 2 = " + proxy.Mul(4, 2)); 
                   Console.WriteLine("4 / 2 = " + proxy.Div(4, 2));
          
                   // Wait for user
                   Console.ReadKey();
                 }
             }
          
             /// <summary>
             /// The 'Subject interface
             /// </summary>
             public interface IMath
             {
                 double Add(double x, double y);
                 double Sub(double x, double y);
                 double Mul(double x, double y);
                 double Div(double x, double y);
             }
          
              /// <summary>
              /// The 'RealSubject' class
              /// </summary>
          
              public class Math : IMath
              {
                  public double Add(double x, double y) { return x + y; }
                  public double Sub(double x, double y) { return x - y; }
                  public double Mul(double x, double y) { return x * y; }
                  public double Div(double x, double y) { return x / y; }
              }
          
              /// <summary>
              /// The 'Proxy Object' class
              /// </summary>
          
              public class MathProxy : IMath
              {
                  private Math math = new Math();
          
                  public double Add(double x, double y)
                  {
                     return math.Add(x, y);
                  }
                  
                  public double Sub(double x, double y)
                  {
                     return math.Sub(x, y);
                  }
                
                  public double Mul(double x, double y)
                  {
                      return math.Mul(x, y);
                  }
          
                  public double Div(double x, double y)
                  {
                      return math.Div(x, y);
                  }
               }
          }
  • Behavioral (Strategy, Iterator, Observer, Mediator, State) =>
    • Strategy (Add algorithms dynamically) :
      • Definisce una famiglia di algoritmi (ConcreteStrategy:  Strategy), incapsula ciascuno di essi e li rende intercambiabili (Context) indipendentemente dai client che lo utilizzano.
      • classe Strategy =>
        • Dichiara un’interfaccia comune a tutti gli algoritmi supportati.
        • Context usa questa interfaccia per chiamare l’implementazione concreta definita da ConcreteStrategy
      • classe ConcreteStrategy => 
        • Implementa l’argoritmo desiderato usando l’interfaccia Strategy
      • classe Context =>
        • E’ configurato con un oggetto ConcreteStrategy
        • Mostra il modello di strategia che incapsula la funzionalità sotto forma di un oggetto.
        • Ciò consente ai clienti di modificare dinamicamente le strategie algoritmiche.

        • using System;
          
          public class Program
          {
             public static void Main(string[] args)
             {
                Context context;
          
                // Three contexts following different strategies
                context = new Context(new ConcreteStrategyA());
                context.ContextInterface();
          
                 context = new Context(new ConcreteStrategyB());
                 context.ContextInterface();
          
                 context = new Context(new ConcreteStrategyC());
                 
              }
          }
          
          /// The 'Strategy' abstract class
          public abstract class Strategy
          {
              public abstract void AlgorithmInterface();
          }
          
          /// A 'ConcreteStrategy' class
          public class ConcreteStrategyA : Strategy
          {
                public override void AlgorithmInterface()
                {
                    ...
                }
          }
          
          /// B 'ConcreteStrategy' class
          public class ConcreteStrategyB : Strategy
          {
                public override void AlgorithmInterface()
                { 
                    ...
                }
          }
          
          /// The 'Context' class
          public class Context
          {
              Strategy strategy;
          
              public Context(Strategy strategy)
              {
                   this.strategy = strategy;
              }
          
              public void ContextInterface()
              {
                   strategy.AlgorithmInterface();
              }
          }
      • Delegate =>
        • Lo ‘stategy’ pattern può essere implementato anche con i delegate
        • public class Program
          {
             delegate int MathOp(int member1, int member2);
          
             static void Main(string[] args)
             {
                CalculateAndPrint(1, 2, Add);
             }
          
             static void CalculateAndPrint(int mermber1, int member2, MathOp mathOp)
             {
                var result = mathOp(mermber1, member2);
                Console.WriteLine(result);
              }
          
              static int Add(int x, int y)
              {
                 return x + y;
              }
          
              static int Sub(int a, int b)
              {
                 var ret = a - b;
                 return ret < 0 ? 0 : ret;
              }
          }
    1. Iterator :
      • Cos’è =>
        • Fornisce un modo per accedere agli elementi di una raccolta di oggetti in modo sequenziale senza conoscerne la struttura sottostante.
        • Questo modello è comunemente usato nei sistemi di menu di molte applicazioni come Editor, IDE, ecc.
      • Client => E’ la classe che contiene una raccolta di oggetti e utilizza l’operazione Next dell’iterator per recuperare elementi dall’aggregato in una sequenza appropriata.
      • Iterator => E’ un’interfaccia che definisce le operazioni per l’accesso agli elementi della raccolta in una sequenza.
      • ConcreteIterator => E’ la classe che implemente l’interfaccia Iterator
      • Aggregate =>  E’ un’interfaccia che definisce un’operazione per creare un iteratore
      • Concrete Aggregate => E’ la classe che implementa l’interfaccia Aggregate

      • public class Client
        {
             public void UseIterator()
             {
                  ConcreteAggregate aggr = new ConcreteAggregate();
                  aggr.Add("One");
                  aggr.Add("Two");
                  aggr.Add("Three");
                  aggr.Add("Four");
                  aggr.Add("Five");
        
                  Iterator iterator = aggr.CreateIterator();
                  while (iterator.Next())
                  {
                      string item = (string)iterator.Current;
                      Console.WriteLine(item);
                  }
             }
        }
        
        public interface Aggregate
        {
           Iterator CreateIterator();
        }
        
        public class ConcreteAggregate : Aggregate
        {
            private ArrayList items = new ArrayList();
        
            public Iterator CreateIterator()
            {
                return new ConcreteIterator(this);
            }
        
            public object this[int index]
            {
               get { return items[index]; }
            }
        
            public int Count
            {
              get { return items.Count; }
            }
        
            public void Add(object o)
            {
               items.Add(o);
            }
        }
        public interface Iterator
        {
            object Current { get; }
            bool Next();
        }
        
        public class ConcreteIterator : Iterator
        {
            private ConcreteAggregate aggregate;
            int index;
        
            public ConcreteIterator(ConcreteAggregate aggregate)
            {
              this.aggregate = aggregate;
              index = -1;
            }
        
            public bool Next()
            {
               index++;
                return index < aggregate.Count;
            }
        
            public object Current
            {
               get
               {
                  if (index < aggregate.Count)
                     return aggregate[index];
                  else
                    throw new InvalidOperationException();
               }
            }
        }
    • Observer :
      • E’ un’impostazione di tipo push-based => Il cambiamento (nel subject) è visibile (agli observer) non appena avviene (method Update) 
      • Questo pattern è usato in una relazioni uno-a-molti tra oggetti così che se un’oggetto è modificato tutti i suoi oggetti dipendenti lo sono automaticamente.
      • Permette ad un a un singolo oggetto, noto come soggetto, di pubblicare le modifiche al suo stato.
      • Permette a molti oggetti di sottoscrivere eventi che sono trasmessi da un oggetto.
      • Un esempio è una radio che emette signali
      • Un esempio è Firebase, quando un dato cambia sul server, tutti i client che erano collegati cambiano la visualizzazione di quel dato
      • Un modo semplice per implementare l’observer pattern è tramite un meccanismo event-delegate  
        • Una classe subject espone un evento che può essere sottoscritto da una classe observer
        • Quando lo stato della classe subject cambia, viene lanciato l’evento e l’observer ricevera un callback
      • Subject => E’  un’interfaccia che conitiene una collezione privata di observers che hanno sottoscritto eventi trasmesi dalla classe usando Notify
      • ConcreteSubject =>  Implementa un subject e quando viene apportata una modifica al suo stato, l’oggetto chiama l’operazione di notifica della classe base per indicarlo a tutti i suoi osservatori
      • Observer =>  E’ un’interfaccia che implementa un’operatozione Update che viene chiamata quando lo stato del Subject cambia
      • ConcreteObserver => Implementa l’observer e esamina il Subject per determinare quali informaioni sono cambiate

      • public abstract class Subject
        {
           private ArrayList observers = new ArrayList();
        
           public void Attach(IObserver o)
           {
              observers.Add(o);
           }
        
           public void Detach(IObserver o)
           {
              observers.Remove(o);
           }
        
           public void Notify()
           {
                foreach (IObserver o in observers)
                {
                   o.Update();
                }
           }
        }
        
        public class ConcreteSubject : Subject
        {
           private string state;
        
           public string GetState()
           {
              return state;
           }
        
            public void SetState(string newState)
            {
               state = newState;
               Notify();
            }
        }
        
        public interface IObserver
        {
           void Update();
        }
        
        public class ConcreteObserver : IObserver
        {
           private ConcreteSubject subject;
        
           public ConcreteObserver(ConcreteSubject sub)
           {
             subject = sub;
           }
        
           public void Update()
           {
                string subjectState = subject.GetState();
                Console.WriteLine(subjectState);
           }
        }
    • Mediator (E’ come un middleman) :
      • Consente a più oggetti di comunicare tra loro (secondo una relazione molti a molti) senza conoscere la struttura dell’altro.
      • Questo modello definisce un oggetto che incapsula il modo in cui gli oggetti interagiranno tra loro e permette il loose coupling
      • Questo modello è comunemente usato nei sistemi di menu di molte applicazioni come Editor, IDE, ecc.
      • In informatica lo strato Middleware di comunicazione opera secondo questo pattern. Intercetta le richiesta e crea il formato giusto da inviare al componente che gestira la risposta.
      • Divisione delle responsabilità  e elimina duplicazione del codice
      • Mediator => E’ un’interfaccia che definisce le operazioni possono essere effettuate tra oggetti Colleague per comunicare tra loro.
      • ConcreteMediator => E’ la classe che implementa le operazini di comunicazione del mediator
      • Colleague => E’ una classe che definisce un’unico campo protected che ospita una referenza al mediator
      • ConcreteCollegueA/B => Sono classi che comunicano tra loro tramite il mediator

      • public abstract class Colleague
        {
            protected IMediator _mediator;
        
            public Colleague(IMediator mediator)
            {
                _mediator = mediator;
            }
        }
        
        public class ConcreteColleagueA : Colleague
        {
             public ConcreteColleagueA(IMediator mediator) : base(mediator) { }
        
             public void Send(string msg)
             {
                Console.WriteLine("A send message:" + msg);
                _mediator.SendMessage(this, msg);
             }
        
             public void Receive(string msg)
             {
                Console.WriteLine("A receive message:" + msg);
             }
        }
        
        public class ConcreteColleagueB : Colleague
        {
             public ConcreteColleagueB(IMediator mediator) : base(mediator) { }
        
             public void Send(string msg)
             {
                Console.WriteLine("B send message:" + msg);
               _mediator.SendMessage(this, msg);
             }
        
             public void Receive(string msg)
             {
                Console.WriteLine("B receive message:" + msg);
             }
        }
        
        public interface IMediator
        {
            void SendMessage(Colleague caller, string msg);
        }
        
        public class ConcreteMediator : IMediator
        {
            public ConcreteColleagueA Colleague1 { get; set; }
        
            public ConcreteColleagueB Colleague2 { get; set; }
        
            public void SendMessage(Colleague caller, string msg)
            {
               if (caller == Colleague1)
                 Colleague2.Receive(msg);
               else
                  Colleague1.Receive(msg);
            }
        }
        
        
        
    • State :
      • Un oggetto si comporta in modo diverso in rapporto ad un numero finito di stati.
      • E’ possibile creare una classe per ogni possibile stato. Tutte queste classi conddividono un metodo che agisce diversamente in ognuna di essere
      • L’oggetto esporrà il suo stato all’esterno
      • L’oggetto non avrà nessuna implementazione al suo interno ma chiamerà il metodo comune di queste classi 
      • //Classe umano pensa in modo diverso in rapporto al proprio mood
        class Human
        {
          think(mod){
            switch (mod) {
              case 'happy':
                  return 'I am happy';
              case 'sad':
                  return 'I am sad';
             }
          }
        }
        
        
        
        //E' possibile riscrivere la classe usando delle classi per ogni stato da implementare
        //con uno stesso metodo ma che si comporta in modo diverso
        
        interface State {
          string Think();
        }
        
        class Happy : State
        {
           think() {
              return 'I am happy';
           }
        }
        
        
        class Sad: State
        {
            think() {
               return 'I am sad';
            }
        }
        
        class Human {
          state State;
        
          Human() {
            this.state = new Happy();
          }
        
          think() {
             return this.state.think();
          }
        
          changeState(state) {
             this.state = state;
          } 
        }
        
        

 

 

Architecture pattern (Tecniche a livello di intero progetto)

Definiscono le strutture fisiche o logiche di soluzioni standard testate riguardanti le componenti di un’applicazione

  • Client / Server :
    • Distribuisce il sistema in due applicazioni in cui il client effettua richieste al server.
    • In molti casi, il server è un database o qualsiasi altra applicazione che funge da server.
  • Component-Based :
    • Scompone la logica dell’applicazione in componenti funzionali o logici riutilizzabili che espongono proprietà o metodi ben definiti per interagire con i componenti come Grid, Button, Data Adapter ecc.
  • DDD (Domain Driven Design) :
    • Evitare ‘big ball of mud’:
      • Invita a smettere di costruire grandi applicazioni (o, in discorsi DDD, “big ball of mud”).
    • Domain:
      • Il presupposto fondamentale del DDD è che gli esseri umani sono incapaci di creare applicazioni aziendali, ma sono in grado di comprendere problemi piccoli e ben delimitati (un “dominio”).
      • Questi ‘domain’ possono essere descritti utilizzando parole ben comprese dagli sviluppatori e dagli utenti finali
      • Tali parole posso assumere un valore diverso a seconda delle parti dell’applicazioni in cui vengono usate
      • La parola ‘cliente’ ha un significato diverso per una persona del marketing e un addetto del supporto clienti.
    • Evitare applicazioni aziendali:
      • La DDD presume che la creazione di applicazioni che “integrano l’intera organizzazione” o la creazione di “modelli a oggetti a livello aziendale” sia un gioco da pazzi e destinato al fallimento.
      • La DDD suggerisce di creare applicazioni solo per il marketing, solo per l’assistenza clienti, di creare applicazioni all’interno di quelle parti dell’organizzazione in cui le parole hanno un significato costante
      • La definizione di “dominio” è l’area dove le parole usate per definire ciò che fa l’azienda hanno un solo significato.
      • Lo sviluppatore deve scrive applicazioni che supportano un singolo dominio
  • Service-Oriented Architecture (SOA) :
    • SOA è semplicemente un’applicazione che:
      • fornisce servizi ad altre applicazioni indipendentemente da qualsiasi piattaforma o tecnologia/
      • espone e utilizza funzionalità come servizio utilizzando contratti e messaggi.
    • Nella creazione di un’applicazione per un dominio potresti, ovviamente, aver bisogno di parlare con un altro dominio.
      • E’ per questo che abbiamo architetture orientate ai servizi => per consentire ai domini di comunicare tra loro.
    • Limita CRAP =>
      • Permette di limitare i clicli di creazione / riparazione / abbando / riscruttura tipici di un approccio classico.
  • MVC:
    • Model = Business entities
    • View = Presentation Logic
    • Controller = Business Logic (l’entry point), Binding layer (connette tra loro gli altri 2 livelli)
  • MVP (Model View Presenter)
  • MVVM (adatto ad applicazioni desktop) :
    • Model
    • View (l’entry point)
    • View Model (il controller di MVC)
  • Repository
  • Unit of Work
  • N-tiers =>
    • Distribuisce la funzionalità in parti separate con ogni parte che è un livello situato su un computer fisicamente separato.
    • Suddivide i problemi dell’applicazione in gruppi impilati (livelli) e vengono distribuiti sullo stesso computer.

 

  • CQRS (Command Query Responsibility Segregation)
    • Si base sul fatto che ottimizzare un DB per la lettura non é come ottimizzalo per la scrittura.
    • Supera la visione classi di un sistema CRUD (lettura e scrittura in un unico sistema) separando la parte di lettura e scrittura dei dati 
      • Separazione logica
        • Divido l’applicazione i più componenti. Alla UI espongo un’interfaccia comune.

      • Separazione fisica
        • Più di un DB + processo di copia (oppure bus) tra i DB di scrittura e quelli di lettura.

 

Architecture styles (Indicano dei principi da seguire)

  • REST (Representational State Transfer).
  • SOAP (Simple Object Access Protocol).
  • IoC (Inversion of Control):
    • E’ una tecnica di programmazione in cui la logica che non riguarda direttamente un modulo venga delegata a componenti esterni al modulo stesso.
      • Source Locator.
      • Event.
      • Delegates.
      • Dependency Injection (Constructor, Property, Method).
        • Pattern usato per fornire le dipendenze necessarie ad una classe.
      • Strategy design pattern.
      • Factory pattern.
  • Microservices :
    • Supera i limite delle architetture N-tiers che sono di tipo monolitico.
    • Cioé quando faccio un cambio ad un modulo sono verosibilmente costretto a farlo anche negli altri layers della mia architettura.
    • Self-contained indipendent deployable unit :
      • Microservices è uno architecture style in cui si sviluppano servizi contenenti piccole porzioni di business logic che
        • vengono eseguiti in processi separati.
        • comunicano tra loro tramite HTTP.
        • vengono automaticamente deployati.
      • Il progetto è diviso in piccole strutture fisiche secondo la natura del domain.
      • Per esempio in un ERP è possibile creare microservizi per le parti Invoice e Accounting separatamente. 
      • Ognuna di queste unità fisiche ha il loro DB e la loro struttura completa.
    •