Login Login
MORE

WIDGETS

Widgets

Wanted articles
Who is online?
Article tools

CSharp:Design Pattern Observer

From Aino Wiki

Jump to: navigation, search

Introduzione

E' un Pattern comportamentale.
Definisce una dipendenza uno a molti tra oggetti così che se un oggetto cambia il proprio stato, tutti i suoi dipendenti sono avvisati con notifiche ed in definitiva aggiornati automaticamente. Semplicemente: un cambiamento in un oggetto causa il cambiamento o una azione in un altro.

Esempio

Materiale ottenuto dal video corso su Lynda.com di LinkedIn.

Diagramma delle classi generico applicabile a qualsiasi esempio di applicazione dell'Observer:

DesignPatterns - Observer ClassDiagram 01.png

Scenario concreto.
Faremo il caso di un Publisher (un blogger ad es.) e i suoi Subscriber (followers, gente che vuol conoscere i nuovi post\pubblicazioni e quindi vuol essere avvsata). Il pattern è applicabile in quanto al cambiamento di stato del Pubblisher, per nuovo articolo pubblicato, vogliamo che i Subscriber siano avvisati.

DesignPatterns - Observer Real Scenario 01.png

Altro buon esempio è nel sistema operativo Windows con quel che è implementato con l'event handling oppure col trigger del click del Mouse.

Codice

Come esempio si sceglie una applicazione Consol che avvisa tutti i fans (subscrivers) di una celebrità nel momento in cui questo pubblica un Twit.

DesignPatterns - Observer Solution 01.png

In questa implementazione vogliamo porre l'attenzione al processo che conduce alle notifiche di nuovi eventi (cambio di stato) e non alla notifica che è una semplice scrittura sulla console.

Classe Subject

E' la classe assegnata all'attore che si vuol tenere sott'occhio.
Nella seguente classe di interfaccia si predispongono gli attributi ed i metodi del soggetto\oggetto attore dei cambiamenti di stato da sottoscrivere.

In ICelebrity.cs:

namespace ObserverPattern
{
    // Subject
    public interface ICelebrity
    {
        string FullName { get; }    // Nome della celebrità da seguire
        string Tweet { get; set; }  // Testo del twit
        void Notify(string tweet);  // Metodo\Azione di Notifica
        void AddFollower(IFan fan);    // Metodo per aggiungere un fan / sottoscrittore
        void RemoveFollower(IFan fan); // Metodo per rimuovere un fan / sottoscrittore
    }
}

Classe Observer

Si definisce l'azione di cambio stato, l'Update() che concretamente si implementerà con una scrittura in Console. In IFan.cs:

namespace ObserverPattern
{
    // Observer
    public interface IFan
    {
        void Update(ICelebrity celebrity);
    }
}

Classi ConcreteObserver

In Fan.cs:

namespace ObserverPattern
{
    // Concrete Observer
    public class Fan : IFan
    {
        public void Update(ICelebrity celebrity)
        {
            Console.WriteLine($"Fan notified. Tweet of {celebrity.FullName}: " +
                $"{celebrity.Tweet}");
        }
    }
}

Classi ConcreteSubject

Qui ci sarano le concrete implementazioni degli attori ovvero le celebrità di cui si vuol conoscere i nuovi Twit pubblicati.
In GClooney.cs:

namespace ObserverPattern
{
    // Concrete Subject
    public class GClooney : ICelebrity
    {        
        private readonly List<IFan> _fans = new List<IFan>();   // Collection
        private string _tweet;                                  // Backing field        
 
        public GClooney(string tweet){                          // Constructor
            _tweet = tweet;
        }
 
        public string FullName => "George Clooney";
        public string Tweet
        {
            get{ return _tweet;}
 
            set
            {
                Notify(value);
            }
        }
 
        public void AddFollower(IFan fan) {
            _fans.Add(fan);
        }
 
        public void RemoveFollower(IFan fan) {
            _fans.Remove(fan);
        }
 
        public void Notify(string tweet)
        {
            _tweet = tweet;
            foreach (var fan in _fans)
            {
                fan.Update(this);
            }
        }
    }
}

In TSwift.cs si implementa la notifica in modo più complesso ovvero definendo un evento e le sue notifiche mediante degli handler che li intercettino.

namespace ObserverPattern
{
    public class TSwift : ICelebrity
    {
        private string _tweet;
 
        private delegate void TweetUpdate(ICelebrity celebrity);
        private event TweetUpdate OnTweetUpdate;
 
        public TSwift(string tweet)
        {
            _tweet = tweet;
        }
 
        public string FullName => "Taylor Swift";
 
        public string Tweet
        {
            get { return _tweet; }
 
            set
            {
                Notify(value);
            }
        }
 
        public void Notify(string tweet)
        {
            _tweet = tweet;
            if (OnTweetUpdate!=null)
            {
                OnTweetUpdate(this);
            }
        }
 
        public void AddFollower(IFan fan)
        {
            OnTweetUpdate += fan.Update;
        }        
 
        public void RemoveFollower(IFan fan)
        {
            OnTweetUpdate -= fan.Update;
        }
    }
}

Main

In Program.cs:

    class Program
    {
        static void Main(string[] args)
        {
            var gClooney = new GClooney("I love my new wife");
            var tSwift = new TSwift("1981 is now my favorite number.");
 
            var firstFan = new Fan();
            var secondFan = new Fan();
 
            gClooney.AddFollower(firstFan);
            tSwift.AddFollower(secondFan);
 
            gClooney.Tweet = "My wife didn't force me to tweet.";
            tSwift.Tweet = "I love my new music.";
 
            Console.Read();
        }
    }

Mappa e Link


C# | Design Pattern | Teoria


Visual Studio


Parole chiave:

Author Giuseppe AINO