sobota, 7 lipca 2012

[Wzorce projektowe] Template Method

Analogia z życia:

Piłkarze czołowych klubów europejskich trenują według ściśle zaplanowanego grafiku. Jednego dnia trenują wszyscy stałe fragmenty gry, innego grę piłką. W ciągu miesiąca oprócz meczów swojego zespołu, zdarzają się też takie dni, w których wszyscy wyjeżdżają na zgrupowania swoich reprezentacji narodowych. W tym czasie każdy piłkarz zajmuje się czym innym, zgodnie z zaleceniami swojego trenera - selekcjonera. Po powrocie z reprezentacji, piłkarze wracają do wspólnych treningów w klubie.



Zastosowanie:

Wzorca metody szablonu używa się podczas modelowania pewnego algorytmu składającego się z etapów. Każdy etap może być inny, natomiast całość powinna wykonywać się w odpowiedniej kolejności. Zatem zgodnie z założeniem narzucamy jedynie strukturę pewnego algorytmów, natomiast wykonanie kolejnych kroków zależy od konkretnych implementacji. Część kroków, wspólna dla całego algorytmu, może być także zdefiniowana w klasie bazowej.

Zasada działania:

Szablon metody definiowany jest w abstrakcyjnej klasie bazowej, gdzie w odpowiednich miejscach wywoływane są metody abstrakcyjne. Klasy dziedziczące z klasy bazowej redefiniują odpowiednie metody, wprowadzając w nich swoje zachowania. Przydatne mogą się też okazać metody wirtualne.

Przykład implementacyjny:

public abstract class AbstractLetter
    {
        private readonly string _letterContent;

        public AbstractLetter(string letter)
        {
            _letterContent = letter;
        }

        public void PrintLetter()
        {
            Console.Write("\t\t");
            Console.Write(Recipent() + "\n");
            Console.WriteLine();
            Console.WriteLine(_letterContent);
            Console.WriteLine("\t\t" + Signature());
        }

        public abstract string Recipent();
        public abstract string Signature();

    }
    class FormalLetter : AbstractLetter
    {
        public FormalLetter(string s) : base(s)
        {
            
        }

        public override string Recipent()
        {
            return "Dear Sir/Madam";
        }

        public override string Signature()
        {
            return "Yours faithfully";
        }
    }
    public class InformalLetter : AbstractLetter
    {
        public InformalLetter(string s) : base(s)
        {
            
        }
        
        public override string Recipent()
        {
            return "Hi Jack";
        }

        public override string Signature()
        {
            return "See Ya";
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            string s = "This is sample letter";
            FormalLetter formalLetter = new FormalLetter(s);
            InformalLetter informalLetter = new InformalLetter(s);

            formalLetter.PrintLetter();
            Console.WriteLine();
            informalLetter.PrintLetter();
            Console.WriteLine();
        }
    }

[Wzorce projektowe] Strategy

Analogia z życia:

Podróżując na wakacje można zdecydować się na różne środki transportu. Ktoś woli jechać samochodem, ktoś pociągiem a ktoś inny lecieć samolotem. W każdym z tych przypadków zapłacimy za bilet inną kwotę. W przypadku samolotu płacimy za konkretne połączenie, natomiast w przypadku samochodu zapłacimy za benzynę, czyli koszt będzie zależny od ilości przejechanych kilometrów. Tak więc pomimo tego samego efektu (dotarcie do pewnego miejsca), koszty podróży wyliczone zostaną innym algorytmem.




Zastosowanie: 

Strategii używamy wszędzie tam, gdzie mamy do czynienia z pewnymi rodzinami podobnych algorytmów. Algorytmy te, dzięki wzorcowi mogą rozwijać się niezależnie od klas, które je wykorzystują. Dzięki temu uzyskujemy separację pomiędzy samym działaniem algorytmu, a przetwarzaniem wyników, jakie on zwróci. W każdym miejscu, gdzie pojawiają się wyrażenie switch, if - else i sterują one wywołaniem jakiejś metody, można zastanowić się nad zastosowaniem wzorca strategii. Dzięki temu dodanie nowego algorytmu nie spowoduje modyfikacji klasy, która z niego korzysta.

Zasada działania:

Najprościej użyć wspólnego interfejsu dla rodziny strategii, oraz konkretnych implementacji jako konkretnych algorytmów. 


Przykład implementacyjny:

    public interface ICentralElement
    {
        List<int> Collection { get; set; }
        int CalculateCentralElement();
    }
    public class AverageAlgorithm : ICentralElement
    {
        public List<int> Collection { get; set; }

        public int CalculateCentralElement()
        {
            return (int)Collection.Average();
        }
    }
    public class MedianAlgorithm : ICentralElement
    {
        public List<int> Collection { get; set; }

        public int CalculateCentralElement()
        {
            return Collection[Collection.OrderBy(s => s).ToArray().Count()/2];
        }
    }
 public class CentralElementStrategy
    {
        private readonly ICentralElement _strategy;

        public CentralElementStrategy(ICentralElement strategy)
        {
            _strategy = strategy;

        }

        public int Calculate()
        {
            return _strategy.CalculateCentralElement();
        }
    }
class Program
    {
        public static CentralElementStrategy Strategy;

        static void Main(string[] args)
        {
            RandomStrategy();

            Console.WriteLine(Strategy.Calculate());
        }

        public static void RandomStrategy()
        {
            ICentralElement centralElement;
            Random r = new Random();
            var num = r.Next();
            if (num % 2 == 0)
                centralElement = new MedianAlgorithm();
            else
                centralElement = new AverageAlgorithm();

            List<int> list = new List<int>();
            for (int i = 0; i < 20; i++)
            {
                list.Add(r.Next());
            }
            centralElement.Collection = list;

            Strategy = new CentralElementStrategy(centralElement);
        }
    }

Platforma .NET Framework udostępnia pewne mechanizmy ułatwiające implementację wzorca strategii, takie jak delegaty np. Func<>, Action<>, Predicate<> itd.

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.

[Wzorce projektowe] Simple Factory, Abstract Factory

Analogia z życia:

Automat do napojów wyrzuca puszki takich samych rozmiarów z napojami różnych producentów. Decyzja o tym, którą puszkę zwrócić podejmowana jest w momencie, gdy użytkownik naciśnie odpowiednie przyciski i wrzuci odpowiednie monety. Całym procesem przeliczania oraz wybrania żądanego napoju zajmuje się automat.




Zastosowanie:

Fabryk używamy, kiedy chcemy oddzielić proces tworzenia obiektu od podejmowania decyzji o tym, który obiekt chcemy utworzyć. Znacznie ułatwia to rozszerzanie możliwości podczas dalszego rozwoju oprogramowania. Fabryki bywają także użyteczne, gdy informacje o tym, który obiekt mamy utworzyć przechowywane są poza programem, na przykład w bazie danych, pliku .xml etc. Zatem warto stosować fabryki, gdy nie jesteśmy pewni, której konkretnej implementacji danego interfejsu będziemy w danym miejscu potrzebowali. Często w kodzie może się pojawić sporo wyrażeń typu if-else lub switch podczas tworzenia obiektów. Można je również zastąpić fabryką.

Simple Factory:

Pierwsza wersja fabryki ma za zadanie enkapsulację tworzenia obiektów. Decyzja o tym, który obiekt ma zostać zbudowany może zostać podjęta w czasie trwania programu np. na podstawie pliku konfiguracyjnego. Pobranie obiektu odbywa się za pomocą metody GetInstance z parametrem typu string, skojarzonym z typem przez dot Netowe Reflection.


public interface IDrink
    {
        string GetName();
    }

    public class CocaCola : IDrink
    {
        public string GetName()
        {
            return "Coca - Cola";
        }
    }

    public class Pepsi : IDrink
    {
        public string GetName()
        {
            return "Pepsi";
        }
    }

    public class Sprite : IDrink
    {
        public string GetName()
        {
            return "Sprite";
        }
    }
public class DrinkFactory
    {
        private Dictionary<string, Type> _drinks;

        public DrinkFactory()
        {
            LoadDrinks();
        }

        public void LoadDrinks()
        {
            _drinks = new Dictionary<string, Type>();
            var types = Assembly.GetExecutingAssembly().GetTypes();
            foreach (var type in types)
            {
                if(type.GetInterface(typeof(IDrink).ToString())!= null)
                {
                    _drinks.Add(type.Name,type);
                }
            }
        }

        public IDrink GetInstance(string typeName)
        {
            foreach (KeyValuePair<string, Type> pair in _drinks)
            {
                if (pair.Key.ToLower().Contains(typeName.ToLower()))
                    return Activator.CreateInstance(pair.Value) as IDrink;
            }
            throw new NotImplementedException();
        }
    }
 class Program
    {
        static void Main(string[] args)
        {
            var factory = new DrinkFactory();

            IDrink drink = factory.GetInstance("Cola");
            Console.WriteLine(drink.GetName());
            drink = factory.GetInstance("pepsi");
            Console.WriteLine(drink.GetName());
        }
    }

Abstract Factory :

Fabryka abstrakcyjna zapewnia jedynie interfejs do tworzenia rodzin powiązanych ze sobą pewien sposób obiektów. Konkretne fabryki, będące implementacjami fabryki abstrakcyjnej umożliwiają tworzenie całych rodzin obiektów w zależności od parametru. Jedna fabryka może tym razem tworzyć wiele obiektów za pomocą różnych metod. Sposób tworzenia obiektów wewnątrz konkretnej fabryki może być całkowicie dowolny.




    public interface ICar
    {
        Engine Engine { get; set; }
        string Name { get; set; }
    }

    public class VWGolf : ICar
    {
        public Engine Engine { get; set; }

        public string Name
        {
            get { return "Volkswagen Golf"; }
            set { throw new NotImplementedException(); }
        }
    }

    public class FordFocus : ICar
    {
        public Engine Engine { get; set; }

        public string Name
        {
            get { return "Ford Focus"; }
            set { throw new NotImplementedException(); }
        }
    }
 abstract public class Engine
    {
        public int Capacity { get; set; }
    }

    public class GasEngine : Engine
    {
        public GasEngine()
        {
            Capacity = 30;
        }
    }

    public class PetrolEngine : Engine
    {
        public PetrolEngine()
        {
            Capacity = 50;
        }

    }
    public abstract class CarAbstractFactory
    {
        public abstract ICar CreateAutoWithPetrolEngine();
        public abstract ICar CreateAutoWithGasEngine();
    }

    public class VWGolfFactory : CarAbstractFactory
    {
        private ICar BaseCreate()
        {
            return new VWGolf();
        }

        public override ICar CreateAutoWithPetrolEngine()
        {
            var car = BaseCreate();
            car.Engine = new PetrolEngine();
            return car;
        }

        public override ICar CreateAutoWithGasEngine()
        {
            var car = BaseCreate();
            car.Engine = new GasEngine();
            return car;
        }
    }

    public class FordFocusFactory : CarAbstractFactory
    {

        public override ICar CreateAutoWithPetrolEngine()
        {
            var car = new FordFocus();
            car.Engine = new PetrolEngine();
            return car;
        }

        public override ICar CreateAutoWithGasEngine()
        {
            var car = new FordFocus();
            car.Engine = new GasEngine();
            return car;
        }
    }
static void Main(string[] args)
        {
            CarAbstractFactory factory = LoadFactory();
            var car = factory.CreateAutoWithGasEngine();
            Console.WriteLine(car.Name);
            Console.WriteLine(car.Engine.Capacity);
        }

        public static CarAbstractFactory LoadFactory()
        {
            Random rnd = new Random();
            if (rnd.Next() % 2 == 0)
                return new VWGolfFactory();
            return new FordFocusFactory();
        }

czwartek, 5 lipca 2012

[Wzorce projektowe] Bridge

Analogia z życia:

Niektóre restauracje oferują jedynie zestawy obiadowe. Zestaw obiadowy zawsze musi się składać z zupy i drugiego dania. Każdego dnia mamy do dyspozycji inne zupy i inne drugie dania, gdzie zupa i drugie danie to pojęcia abstrakcyjne. Zestaw obiadowy jako całość zawsze tworzą konkretna zupa i konkretne drugie danie. Jeśli chcemy rozbudować taki zestaw o sałatkę, wystarczy powiedzieć, że zestaw obiadowy składa się z zupy, drugiego dania i sałatki.


Zastosowanie:

Definicja mówi, że mostu należy używać wszędzie tam, gdzie chcemy oddzielić abstrakcję od implementacji tak, aby istniały niezależnie. Przykładem mogą być interfejsy użytkownika.Chcemy na przykład rysować na ekranie różne figury różnymi kolorami. Pojęciem abstrakcyjnym jest kolorowa figura, przykładem konkretnej implementacji czerwony trapez.

Zasada działania:

Często spotykanym sposobem implementacji jest klasa abstrakcyjna reprezentująca abstrakcyjne pojęcie. Z klasy tej dziedziczą konkretne implementacje. Klasa abstrakcyjna zawiera pole lub propercję typu pewnego interfejsu który reprezentuje kolejną abstrakcję. Jedyny konstruktor dla klas implementujących bazową klasę abstrakcyjną to taki, w którym jednym z argumentów jest obiekt implementujący potrzebny interfejs.


Przykład implementacyjny:


 public abstract class Vector
    {
        protected Norm Norm { get; set; }
        protected float[] Coefficients { get; set; }

        protected Vector(Norm n)
        {
            this.Norm = n;
        }

        public void NormName()
        {
            Norm.PrintName();
        }

        public double Calculate()
        {
            return Norm.Calculate(Coefficients);
        }

    }

    public class Vector3D : Vector
    {
        public Vector3D(Norm norm, float x, float y, float z) : base(norm)
        {
            Coefficients = new float[] { x, y, z};
        }
    }

    public class Vector2D : Vector
    {
        public Vector2D(Norm norm, float x, float y) : base(norm)
        {
            Coefficients = new float[] { x, y};
        }
    }

 public abstract class Norm
    {
        public abstract void PrintName();
        public abstract double Calculate(float[] coeffs);
    }

    public class EuclideanNorm : Norm
    {

        public override void PrintName()
        {
            Console.WriteLine("Calculating Euclidean Norm");
        }

        public override double Calculate(float[] coeffs)
        {
            float norm = 0.0f;
            foreach (var coeff in coeffs)
            {
                norm += coeff*coeff;
            }
            return Math.Sqrt((double) norm);
        }
    }

    public class ManhattanNorm : Norm
    {

        public override void PrintName()
        {
            Console.WriteLine("Calculating Manhattan Norm");
        }

        public override double Calculate(float[] coeffs)
        {
            return coeffs.Sum(s => Math.Abs(s));
        }
    }
 class Program
    {
        static void Main(string[] args)
        {
            Vector v1 = new Vector2D(new ManhattanNorm(), 2.0f, 3.5f);
            Vector v2 = new Vector3D(new EuclideanNorm(), 4.0f, 4.6f, 3.4f);

            v1.NormName();
            Console.WriteLine(v1.Calculate().ToString());

            v2.NormName();
            Console.WriteLine(v2.Calculate().ToString());

            Console.Read();
        }
    }

Korzystając z pewnego schematu dla wzorca mostu można opisać powyższy przykład


Abstrakcję reprezentuje klasa Vector, która zawiera realizatora w postaci normy. Konkretni realizatorzy to dwie konkretne normy natomiast konkretne abstrakcje to wektory 2D i 3D

[Wzorce projektowe] Singleton

Analogia z życia:

Każde dziecko wie, że nieodłączną częścią każdego samochodu jest kierownica. Samochód zawiera dokładnie jedną kierownicę. Kierownica może być używana tylko przez jedną osobę, lub w ogóle nie używana. Gdyby kilka osób zaczęło kręcić kierownicą podczas jazdy to nietrudno sobie wyobrazić do czego by to doprowadziło. Gdyby samochód miał więcej kierownic. W najlepszym wypadku doszło by do czegoś takiego :-):



Zastosowanie:

Singletony tworzy się, gdy zachodzi potrzeba stworzenia dokładnie jednej instancji danej klasy. Dodatkowo wprowadza się blokowanie dwufazowe - zabezpieczenie instancji przed dostępem z wielu wątków. Obiekty - singletony podczas tworzenia nie potrzebują by przekazywać parametry za pomocą konstruktora. Czasami warto także zastanowić się nad stworzeniem singletonu gdy samo tworzenie obiektu jest kosztowne czasowo, a nie jest potrzebne wiele instancji.

Zasada działania:


Zazwyczaj tworzy się klasę zawierającą propercję lub polę pewnego typu. Klasa zewnętrzna posiada prywatny konstruktor i metodę zwracającą instancję obiektu z pola / propercji. Jeśli obiekt nie został wcześniej zainicjowany, to tworzy się instancję, która później jest zwracana przy każdym wywołaniu tej metody. Można użyć także klas statycznych, ale jest to odradzane ze względu na problemy z testowaniem i brak możliwości dziedziczenia.

Przykład:

Interfejs do zapisywania zmian do jakiegoś rejestru np logowanie wyjątków.

Przykład implementacyjny:

Poniższy singletonu wydaje się być optymalny ze względu na wydajność dla platformy .NET.




    public class ThreadSafeSingleton
    {
        private ThreadSafeSingleton()
        {
            
        }

        public static ThreadSafeSingleton Instance 
        {
            get { return NestedClass.instance; }
        }

        public void SayHello()
        {
            Console.WriteLine("How are you ?");
        }

        public class NestedClass
        {
            static NestedClass()
            {
            }

            internal static readonly ThreadSafeSingleton instance =
                new ThreadSafeSingleton();
        }
    }

class Program
    {
        static void Main(string[] args)
        {
            var inst = ThreadSafeSingleton.Instance;
            inst.SayHello();

        }
    }


Prywatny konstruktor zabrania tworzenia obiektów klasy. Statyczna propercja gwarantuje, że przekazywana będzie jedna referencja przy każdym wywołaniu, natomiast dzięki statycznemu konstruktorowi klasy zagdnieżdżonej obiekt zostanie zainicjowany dopiero przy pierwszym jego wywołaniu (Lazy Loading). Pole readonly zapewnia bezpieczeństwo przy pracy wielowątkowej i jest to podejście szybsze od stosowania instrukcji lock.

Linki:

1.Readonly a thread safe 

niedziela, 1 lipca 2012

[Wzorce projektowe] Facade

Analogia z życia: 

Telewizja satelitarna, taka jak na przykład Cyfra+ w swej ofercie proponuje klientowi kilkaset kanałów. Często jednak zdarza się, że nie wszystkie z nich interesują klienta i co więcej nie potrzebuje do nich dostępu. W związku z tym może sobie wykupić pakiet, zawierający podzbiór wszystkich oferowanych kanałów.



Zastosowanie:

Kiedy mamy dostęp do pewnego API, z którego interesuje nas jedynie podzbiór usług, jakie ono oferuje. Fasadę stosujemy głównie, aby uprościć pewne API i wybrać z niego tą część, która nas interesuje. Może się też zdarzyć, że pewne API, które otrzymaliśmy zostało źle zaprojektowane - tu także do akcji wkracza wzorzec Fasady. Oczywiście nie jest powiedziane że Fasada dotyczy tylko jednego API, można także opakowywać wiele API w jeden interfejs dostosowany do bieżących potrzeb. Wraz z rozwojem programu fasada często będzie wymagała rozbudowywania.

Zasada działania:

Podobnie jak w przypadku Adaptera, przeważnie tworzy się klasę opakowującą pewne API, gdzie metody tej klasy wywołują metody z opakowywanego API.

Przykład:

Często tworzy się Fasady dla operacji I/O tak, aby zapis lub odczyt realizował się wywołaniem jednej metody.

Przykład implementacyjny:

Fasadę można utworzyć nawet dla dot Netowego API do pracy z ciągami znakowymi.

public class StringFacade
    {
        private string _str;

        public StringFacade(string  s)
        {
            _str = s;
        }

        public string Substring(int from, int to)
        {
            return _str.Substring(from, to - from);
        }

        public string Append(string s)
        {
            StringBuilder sb = new StringBuilder(_str);
            sb.Append(s);
            return sb.ToString();
        }
    }