piątek, 6 lipca 2012

[Wzorce projektowe] Observer

Analogia z życia:

Grupy google umożliwiają prowadzenie rozległych dyskusji na konkretne tematy. Zakładając grupę dyskusyjną mamy możliwość wysyłania do niej wiadomości. Każdy zainteresowany tematem może sobie zasubskrybować grupę i otrzymywać wszystkie wysyłane na nią wiadomości. Jeśli temat przestanie go interesować, może anulować subskrypcję. Osoba wysyłająca wiadomości nie musi przejmować się tym, kto obecnie subskrybuje jej wiadomości - są one wysyłane na adres grupy.





Zastosowanie:

Wzorzec obserwatora stosujemy, gdy jeden obiekt zależny jest od innego i zmiana stanu jednego obiektu powinna powodować zmianę innych obiektów. Często używając wzorca obserwatora nie mamy wiedzy na temat tego, jakie obiekty powiadamiamy.

Zasada działania:

Tradycyjne podejście do wzorca obserwatora zakłada stworzenie dwóch klas abstrakcyjnych : podmiotu i obserwatora. Obserwator zawiera referencję do podmiotu i może go aktualizować. Podmiot może rejestrować nowych obserwatorów (zawiera kolekcję obserwatorów), a także usuwać już zarejestrowanych. Może także notyfikować wszystkich o zmianie stanu. Ponadto może się też zdarzyć, że obserwatorzy będą chcieli modyfikować źródło notyfikacji, co należy odpowiednio zaimplementować.

Przykład implementacyjny:


public interface IAbstractObserver
    {
        void Update(int number);
    }

    public class Counter : IAbstractObserver
    {
        public Counter(IAbstractSubject sub)
        {
            sub.Subscribe(this);
        }

        public static int Count { get; set; }

        public void Update(int n)
        {
            Count++;
        }
    }

    public class Printer : IAbstractObserver
    {
        public Printer(IAbstractSubject sub)
        {
            sub.Subscribe(this);
        }

        public void Update(int n)
        {
            Console.WriteLine(n);
        }
    }
public interface IAbstractSubject
    {
        void Subscribe(IAbstractObserver observer);
        void Unsubscribe(IAbstractObserver observer);
        void Notify();
    }

    public class RandomNumbers : IAbstractSubject
    {
        private List<IAbstractObserver> _observers = new List<IAbstractObserver>();


        private int _randomNumber;
        public int RandomNumber
        {
            get { return _randomNumber; }
            set
            {
                _randomNumber = value;
                Notify();
            }
        }

        public void Subscribe(IAbstractObserver observer)
        {
            _observers.Add(observer);
        }

        public void Unsubscribe(IAbstractObserver observer)
        {
            _observers.Remove(observer);
        }

        public void Notify()
        {
            foreach (IAbstractObserver abstractObserver in _observers)
            {
                abstractObserver.Update(_randomNumber);
            }
        }
    }
 class Program
    {
        static void Main(string[] args)
        {
            RandomNumbers rn = new RandomNumbers();
            Printer printer = new Printer(rn);
            Counter counter = new Counter(rn);

            Random r = new Random();

            for (int i = 0; i < 10; i++)
            {
                int number = r.Next(0, 10);
                rn.RandomNumber = number;
                Thread.Sleep(number*100);
            }

            Console.WriteLine(Counter.Count);
            Console.Read();
        }
    }

Powyższa implementacja jest podejściem klasycznym. Platforma .NET Framework udostępnia zdarzenia i delegatów, za pomocą których można uzyskać podobny efekt.

Brak komentarzy:

Prześlij komentarz