Pokazywanie postów oznaczonych etykietą instance. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą instance. Pokaż wszystkie posty

wtorek, 10 lipca 2012

[Wzorce projektowe] Flyweight

Analogia z życia:

Chodząc do szkoły codziennie rano pakujemy do plecaka zeszyty. Mając pięć godzin lekcyjnych, w tym dwie godziny języka polskiego, zabierzemy cztery zeszyty (prawdopodobnie częściowo już zapisane). Moglibyśmy dla każdej lekcji każdego przedmiotu zakładać nowy zeszyt, ale byłoby to kosztowne zupełnie niepotrzebne. Każda nowa lekcja jest identyfikowana przez stronę, na której się znajduje i przez rodzaj zeszytu w jakim jest zapisana. Oczywiście do różnych przedmiotów mamy do dyspozycji różne zeszyty (w linie, kratkę, gładkie). Zawsze jednak jeden zeszyt może nam posłużyć do zapisywania lekcji przez cały semestr z danego przedmiotu.



Zastosowanie:

Wzorzec Flyweight znajduje zastosowanie wszędzie tam, gdzie mamy do czynienia z dużą ilością obiektów tego samego typu, co wiąże się z dużymi kosztami pamięciowymi przy przechowywaniu. Dzięki niemu możemy używać tego samego obiektu w wielu sytuacjach, przy zachowaniu całej elastyczności związanej z korzystaniem z niego, co powoduje, że zamiast używać np. setek instancji możemy skorzystać zaledwie z kilku. Aby było to możliwe, musimy wyciągnąć z obiektów ich zewnętrzny stan poza obiekt, przy zachowaniu wewnętrznych właściwości. Dzięki temu aplikacja nie zależy od tożsamości konkretnych instancji obiektów, mimo iż podchodząc w 100% obiektowo do modelowania zagadnienia musielibyśmy utworzyć o wiele więcej obiektów.

Zasada działania:

Na początku tworzymy pewien interfejs reprezentujący rodzinę obiektów. Następnie modelujemy różnice w obiektach poprzez konkretne implementacje tego interfejsu. W miejscu, w którym pojawia się zapotrzebowanie na obiekty posługujemy się fabryką, która zwraca już istniejące instancje i wywołuje na nich pewne akcje. Zewnętrzne cechy obiektów przekazywane są przez parametr.

Przykład implementacyjny:

  public interface ISoldier
    {
        int Damage { get; set; }
        void Shoot(int x, int y);
    }

    public class LightInfantry : ISoldier
    {
        public LightInfantry()
        {
            Damage = 20;
        }

        public int Damage { get; set; }

        public void Shoot(int x, int y)
        {
            Console.WriteLine(String.Format
                ("Shooting from ({0},{1}), done {2} damage",x,y,Damage));
        }
    }

    public class Artillery : ISoldier
    {
        public Artillery()
        {
            Damage = 200;
        }

        public int Damage { get; set; }

        public void Shoot(int x, int y)
        {
            Console.WriteLine(String.Format
                ("Shooting from ({0},{1}), done {2} damage", x, y, Damage));
        }
    }
public class SoldierFactory
    {
        private Dictionary<string, ISoldier> _instances =
            new Dictionary<string, ISoldier>();

        
        public ISoldier GetSoldier(string typeName)
        {
            switch (typeName)
            {
                case "LightInfantry":
                    if (!_instances.ContainsKey(typeName))
                        _instances.Add(typeName, new LightInfantry());
                    return _instances[typeName];
                case "Artillery" :
                    if (!_instances.ContainsKey(typeName))
                        _instances.Add(typeName, new Artillery());
                    return _instances[typeName];
                default:
                    throw new NotImplementedException();
            }
        }
    }
class Program
    {
        static void Main(string[] args)
        {
            CreateArmyAndAttack();
            Console.Read();
        }

        public static void CreateArmyAndAttack()
        {
            Random r = new Random();
            var factory = new SoldierFactory();
            for (int i = 0; i < 1000; i++)
            {
                factory.GetSoldier("LightInfantry")
                    .Shoot(r.Next() % 100, r.Next() % 100);
            }
            for (int i = 0; i < 100; i++)
            {
                factory.GetSoldier("Artillery")
                    .Shoot(r.Next() % 20, r.Next() % 100);
            }
        }
    }

czwartek, 5 lipca 2012

[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