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

środa, 11 listopada 2015

[Wzorce projektowe] Visitor

Analogia z życia:

Ludzie mieszkają w mieszkaniach, które zazwyczaj są zamykane przed innymi ludźmi. Czasami zdarzają się jednak sytuacje, gdy ktoś obcy musi wejść do naszego mieszkania, np. żeby naprawić kran. Wtedy otwieramy mieszkanie dla takiej osoby, udostępniamy mu wybraną jego część (kuchnię) i pozwolimy, by wykonał odpowiednie czynności.




Zastosowanie:

Wszędzie tam, gdzie chcemy wydzielić jakąś operację, wykonywaną na obiekcie danego typu poza definicję tego typu. Jeżeli obiekt dobrze implementuje enkapsulację, to nikt z zewnątrz nie może dostać się do jego stanu, a jednocześnie operacje na tym stanie może wykonywać specjalny typ - visitor.

Zasada działania:

Definiuje się dwie rodziny obiektów (rodziny rozumiane jako klasa bazowa plus jej implementacje), elementy oraz wizytorzy. Element to obiekt zawierający strukturę danych i udostępniający operację Visit przyjmującą jako parametr obiekt z rodziny wizytorów. Visitor i elementy po nim dziedziczące reprezentują akcje wykonywane na obiektach typu Element. Visitor wystawia metodę Accept przyjmującą elementy dziedziczące po klasie Element i wykonujące na niej dane operacje.

Definicja grupy elementów:

public abstract class Element
{
    public abstract void Accept(Visitor visitor);
}

public class Tap : Element
{
    public override void Accept(Visitor visitor)
    {
        visitor.VisitKitchen(this);
    }
}

public class Shower : Element
{
    public override void Accept(Visitor visitor)
    {
        visitor.VisitBathroom(this);
    }
}

Definicja wizytatora:

public abstract class Visitor
{
    public abstract void VisitKitchen(Tap tap);
    public abstract void VisitBathroom(Shower shower);
}

public class Plumber : Visitor
{
    public override void VisitBathroom(Shower shower)
    {
        Console.WriteLine("Repairing " + nameof(shower));
    }

    public override void VisitKitchen(Tap tap)
    {
        Console.WriteLine("Repairing " + nameof(tap));
    }
}

Definicja obiektu mieszkania, akceptującego "wizytę":

public class Flat
{
    private List<Element> _stuff = new List<Element>();

    public void Attach(Element element)
    {
        _stuff.Add(element);
    }

    public void Detach(Element element)
    {
        _stuff.Remove(element);
    }

    public void Accept(Visitor visitor)
    {
        foreach (Element element in _stuff)
        {
            element.Accept(visitor);
        }
    }
}

static void Main(string[] args)
{
    var flat = new Flat();
    flat.Attach(new Tap());
    flat.Attach(new Shower());

    flat.Accept(new Plumber());

    Console.ReadKey();
}

poniedziałek, 9 listopada 2015

[Wzorce projektowe] Composite

Analogia z życia:

Oddając samochód do mechanika powierzamy mu kluczyki i oczekujemy naprawy zepsutych części. Mechanik po odebraniu kluczyków wjeżdża samochodem na warsztat i zleca swoim pracownikom konkretne zadania: wymianę zderzaka, prostowanie blachy, lakierowanie. Z naszego punktu widzenia załatwiamy wszystkie te sprawy z jedną osobą (kierownikiem warsztatu), nie musimy martwić się o to, kto będzie wykonywał poszczególne zadania.



Zastosowanie:

Wszędzie tam, gdzie chcemy, aby kod klienta wiedział jak najmniej o procesie, który woła. Klient powinien być niezależny od tego procesu i przechowywać jedynie referencję do obiektu kompozytu, który dba o to, by wywołać odpowiednie konkretne składowe procesu.

Zasada działania:

Klient zawiera referencję na pewien interfejs. Interfejs ten jest implementowany przez konkretne typy, jak również przez specjalny typ - kompozyt, który zawiera także kolekcję elementów implementujących interfejs.


public interface ICarMechanic
{
    void Repair(Car instance);
}

public class Tinsmith : ICarMechanic
{
    public void Repair(Car instance)
    {
        instance.CrackedBody = false;
    }
}

public class WheelChanger : ICarMechanic
{
    public void Repair(Car instance)
    {
        instance.WorkingWheels = 4;
    }
}

public class BumperChanger : ICarMechanic
{
    public void Repair(Car instance)
    {
        instance.HasNewBumper = true;
    }
}

//composite
public class CarService : ICarMechanic
{
    private List<ICarMechanic> _mechanics;

    public CarService(IEnumerable<ICarMechanic> mechanics)
    {
        _mechanics = new List<ICarMechanic>(mechanics);
    }

    public void Repair(Car instance)
    {
        if (instance.BrokenWheel)
        {
            RepairIfAny<WheelChanger>(instance);
        }

        if (instance.BrokenBody)
        {
            RepairIfAny<WheelChanger>(instance);                
        }

        if (instance.BrokenWheel)
        {
            RepairIfAny<BumperChanger>(instance);
        }
    }

    private void RepairIfAny<T>(Car instance) where T : ICarMechanic
    {
        var mechanic = _mechanics.OfType<T>().FirstOrDefault();
        if (mechanic != null)
            mechanic.Repair(instance);
    }
}

Wyekstraktowanie logiki napraw do klasy - kompozytu znacznie ułatwia kod po stronie klienta wołającego usługę naprawy.

public class CarOwner
{
    private Car _car;
    private ICarMechanic _mechanicContact;

    public CarOwner(Car car, ICarMechanic contact)
    {
        _car = car;
        _mechanicContact = contact;
    }

    public void RenewCar()
    {
        _mechanicContact.Repair(_car);
        Console.WriteLine("Car has been repaired");
    }
}

Cały proces startuje przez stworzenie odpowiednich zależności dla kompozytu:

static void Main(string[] args)
{
    var mechanicsSvc = new CarService(
        new ICarMechanic[] { new BumperChanger(), new WheelChanger(), new Tinsmith() });

    var car = new Car() { BrokenBody = true, BrokenWheel = true, HasNewBumper = false };

    var owner = new CarOwner(car, mechanicsSvc);
    owner.RenewCar();

    Console.ReadKey();
}

sobota, 20 października 2012

[HTML|JS|CSS] JavaScript Patterns: Wzorce projektowe

Wzorce projektowe znane z języków obiektowych nie zawsze są łatwe do zaimplementowania w języku JavaScript. Aby uzyskać podobny efekt, często należy znać pewne specyficzne konstrukcje JS. Poniżej przedstawiono trzy wzorce przez tzw Gang of Four
. Ich implementacja w JS jest nieco inna od tej, którą wszyscy znają z języków obiektowych.

  • Singleton

Jeżeli chcemy zapewnić, aby pewien obiekt był zinstancjonowany tylko raz, można skorzystać z faktu, że funkcja jest obiektem, który może posiadać własne properties.

function MyObject(){

 if(typeof MyObject.instance !== "undefined"){
  return MyObject.instance;
 }

 this.name = "MyObject";
 this.counter = 0;

 MyObject.instance = this;
}

var o1 = new MyObject();
o1.counter++;
var o2 = new MyObject();
console.log(o2.counter);

Program wypisze 1. Problemem jest, że statyczna instancja jest publiczna, aby tego uniknąć można skorzystać z konstrukcji closure.

  • Factory

Wzorzec przydatny przy tworzeniu całych rodzin obiektów. Ma on na celu umożliwienie podjęcia decyzji o tym, który dokładnie obiekt ma zostać stworzony, podczas pracy programu. JavaScript ułatwia implementację wzorca, ponieważ warto pamiętać, że obiekty są parami typu klucz - wartość.

function VehicleMaker(){}

VehicleMaker.prototype.printType = function(){
 console.log(this.type);
};

VehicleMaker.factory = function(type){
 var constr = type,
  newvehicle;

 if(typeof VehicleMaker[constr] !== "function"){
  throw {
   name: "Error",
   message: constr + " not found"
  };
 }

 if(typeof VehicleMaker[constr].prototype.printType !== 
  "function"){
  VehicleMaker[constr].prototype = new VehicleMaker();  
 } 
 newvehicle = new VehicleMaker[constr]();
 return newvehicle;
};

VehicleMaker.Car = function(){
 this.type = 'car';
};

VehicleMaker.Bike = function(){
 this.type = 'bike';
};

VehicleMaker.Bus = function(){
 this.type = 'bus';
};

var bus = VehicleMaker.factory('Bus');
var car = VehicleMaker.factory('Car');
var bike = VehicleMaker.factory('Bike');

bus.printType();
car.printType();
bike.printType();

try{
 var whatever = VehicleMaker.factory('whatever');
}
catch(err){
 console.log(err);
}

  • Decorator

Wzorzec pozwala rozszerzać w pewien sposób jakiś obiekt w czasie pracy programu, bez z góry narzuconych reguł. Każdy obiekt po dekoracji zachowuje swoje właściwości plus dodatkowo dostaje coś ekstra. W przypadku języka JS do implementacji można wykorzystać listę dekoratorów i odpowiednią metodę, która wykona ich zadania gdy przyjdzie na to czas.

function Degree(name){
 this.name = name;
 this.decorators_list = [];
}

Degree.decorators = {};

Degree.decorators.Engineer = {
 getDegree: function(name){
  return "inz. " + name;
 }
};

Degree.decorators.Doctor = {
 getDegree: function(name){
  return "dr " + name;
 }
};

Degree.decorators.Professor = {
 getDegree: function(name){
  return "Prof. " + name;
 }
};

Degree.decorators.Print = {
 getDegree: function(name){
  console.log(name);
 }
};

Degree.prototype.decorate = function(decorator){
 this.decorators_list.push(decorator);
};

Degree.prototype.getDegree = function(){
 var fname = this.name,
  i,
  max = this.decorators_list.length,
  name;
 for(i = 0; i < max; i+=1){
  name = this.decorators_list[i];
  fname = Degree.decorators[name].getDegree(fname); 
 }
 return fname;
};

var prof = new Degree("Jan Kowalski");
prof.decorate('Engineer');
prof.decorate('Doctor');
prof.decorate('Professor');
console.log(prof.getDegree());

[HTML|JS|CSS] JavaScript Patterns: Code Reuse

JavaScript jest językiem, w którym nie występują klasy, a zatem klasyczne dziedziczenie znane chociażby z C++ musimy zastępować pewnymi wzorcami. Dzięki ich zastosowaniu będziemy mogli wiele razy używać niektórych fragmentów kodu.

  • Classical pattern

Wzorzec ten ma imitować dziedziczenie klas z języków obiektowych, stąd nazwa. Istnieje kilka sposobów jego implementacji. Poniżej przykład, w którym wykorzystuje się obiekt prototype. Zasada działania jest prosta. Jeżeli przy wywołaniu funkcji nie ma jej w konstruktorze danego obiektu, jej sygnatura poszukiwana jest w referencji __proto__ wskazującej na obiekt prototype. Podobnie rzecz ma się z properties.


function Parent1(n){
 this.name = n || 'Parent2';
}

Parent1.prototype.say = function(){
 console.log('My name is: ' + this.name);
};

function Child1(n){
 Parent1.apply(this,arguments);
}
Child1.prototype = new Parent1();

var c1 = new Child1('Child1');
console.log(c1.name);
c1.say();
delete c1.name;
c1.say();

Konsola:

Child1
My name is: Child1
My name is: Parent1

  • Klass

Ideą tego wzorca jest emulacja klas z języków obiektowych, a więc doprowadzenie do takiego API jak w poniższym przykładzie.


var car1 = new car('simple car');
car1.printName();
var car2 = new bmw('bmw car');
car2.printName();

Definicję funkcji konstruktorów wprowadza się za pomocą specjalnej funkcji klass. Przyjmuje ona funkcję konstruktora dla parenta oraz listę properties (jako obiekt), charakterystycznych dla nowego obiektu.


var car = klass(null, {
	__construct : function(name){
		console.log('constructing car...');
		this.name = name;
	},
	printName : function(){
		console.log(this.name);
	}
});

var bmw = klass(car, {
	__construct : function(){
		console.log('constructing bmw');
	},
	printName : function(){
		console.log('bmw printer');
		bmw.uber.printName.call(this);
	}
});

Cała "magia" zawarta jest w funkcji klass:

var klass = function(Parent, props){
	var Child, F, i;
	Child = function(){
		if(Child.uber && Child.uber.hasOwnProperty("__construct")){
			Child.uber.__construct.apply(this, arguments);
		}
		if(Child.prototype.hasOwnProperty("__construct")){
			Child.prototype.__construct.apply(this, arguments);
		}
	};
	Parent = Parent || Object;
	F = function(){};
	F.prototype = Parent.prototype;
	Child.prototype = new F();
	Child.uber = Parent.prototype;
	Child.prototype.constructor = Child;

	for(i in props){
		if (props.hasOwnProperty(i)){
			Child.prototype[i] = props[i];
		}
	}
	return Child;
};

Przy użyciu pomocniczego obiektu funkcji F(), obiekt Child przejmuje prototype swojego parenta podanego jako argument funkcji. Obiekty uber i prototype służą do wywoływania konstruktorów będących wyżej w hierarchii dziedziczenia w momencie konstrukcji obiektu. Ostatecznie kopiowane są również properties obiektu parent.

  • Prototypical inheritance

Jest to prostszy od powyższych i często wystarczający wzorzec, w którym mechanizm dziedziczenia polega na przekazaniu prototypu rodzica do prototypu dziecka. Warto zwrócić uwagę na to jak w obiekcie dziedziczącym zachowuje się funkcja definiowana w prototype, a jak ta z obiektu parenta.

function object(parent){
	var F = function(){};
	F.prototype = parent;
	return new F();
}

function Parent3(){
	this.name = 'Parent3';
	var that = this;
	this.print = function(){
		console.log(that.name);
	};
}

Parent3.prototype.printName = function(){
	console.log(this.name);
};

var newparent = {
	name: "ParentName"
};

var child = object(newparent);
console.log(child.name);

//errors
//child.printName();
//child.print();

var parent3 = new Parent3('Parent3');
var child3 = object(parent3);

child3.name = "Child3";
child3.printName();
child3.print();

Konsola wypisze dwa różne wyniki, ponieważ jedynie funkcja z prototypu będzie wywołana z property z obiektu dziedziczącego. Ponadto obiekt z którego chcemy dziedziczyć musi być tworzony przez operator new.

ParentName
Child3
Parent3

  • Mixin


Jest to specjalny wzorzec umożliwiający dziedziczenie z kilku prostych obiektów w jeden. Tworzy się specjalną funkcję mix, do której przekazywane są jako argumenty obiekty, które chcemy złączyć w całość.


function mix(){
	var arg, prop, child = {};
	for (arg = 0; arg < arguments.length; arg++) {
		for(prop in arguments[arg]){
			if(arguments[arg].hasOwnProperty(prop)){
				child[prop] = arguments[arg][prop];
			}
		}
	}
	return child;
}

var obj = mix({i : 1, one : "one"},
			{j : 1, two : "two"},
			{j : 2, three : "three"});

console.dir(obj);

W przypadku dwóch identycznie nazywających się properties w obiektach, wybrany zostanie ostatni.

{ i: 1, one: 'one', j: 1, two: 'two', three: 'three' }

czwartek, 18 października 2012

[HTML|JS|CSS] JavaScript Patterns: Tworzenie obiektów

Tym razem kilka wzorców projektowych charakterystycznych dla języka JavaScript, które przydają się przy tworzeniu obiektów. Z jakimi podstawowymi problemami spotyka się programista w tym języku ? Przede wszystkim jest to brak klas i przestrzeni nazw. Poniżej użyteczne wzorce.

  • Przestrzeń nazw

Spore zapotrzebowanie na zmienne globalne powoduje zaśmiecanie obiektu okna. Aby temu zapobiec tworzy się jeden globalny obiekt będący odpowiednikiem modułu reprezentującego pewne właściwości.

var mymodule = mymodule || {};
mymodule.length = 3;
mymodule.maxusers = 15;
mymodule.config = {};
mymodule.config.initialtext = "hello world";

  • Prywatne składowe

Aby uzyskać efekt prywatnych składowych obiektu można skorzystać z konstrukcji closure.

function Person(name){
 var myname = name;
 this.getName = function(){
  return myname;
 };
 this.setName = function(str){
  myname = str;
 };
}

var sb = new Person("John");
console.log(sb.myname);
console.log(sb.getName());
sb.setName("Joe");
console.log(sb.getName());

  • Wzorzec modułu

Połączenie kilku mniejszych wzorców, mające na celu utworzenie obiektu będącego modułem z prywatnymi zmiennymi, metodami itd.

var mymodule = {};
mymodule.array = {};
mymodule.array.utils = function(){
 //private members
 var logged = 0;
 var logger = function(i,e){
  logged++;
  console.log("Index:" + i + ", element: " + e);
 };
 //public members
 var totalLogged = function(){
  return logged;
 };
 var printArray = function(array){
  for (var i = array.length - 1; i >= 0; i--) {
   logger(i,array[i]);
  }
 };
 //public API
 return{
  print : printArray,
  total : totalLogged
 };
}();

var myarray = ['one','two','three'];

mymodule.array.utils.print(myarray);
console.log(mymodule.array.utils.total());

  • Sandbox

Ulepszona wersja wzorca przestrzeni nazw. Ponieważ każda funkcja jest obiektem, możemy w niej enkapsulować serwisy i przez samą funkcję się do nich odwoływać oraz zwracać je za pomocą callbacku.

function Sandbox(){
 var args = Array.prototype.slice.call(arguments),
 callback = args.pop(),
 modules = (args[0] && typeof args[0] === "string") ? args : args[0],
 i;

 if(! (this instanceof Sandbox)){
  return new Sandbox(modules, callback);
 }

 for(i = 0; i < modules.length; i += 1){
  Sandbox.modules[modules[i]](this);
 }

 return callback(this);
}

Sandbox.modules = {};
Sandbox.modules.vector = function(box) {
 box.sum = function(v){
  var sum = 0;
  for (var i = v.length - 1; i >= 0; i--) {
   sum += v[i];
  }
  return sum;
 };
 box.normalize = function(v,sumfun){
  var sum = sumfun(v);
  var arr = [];
  for (var i = v.length - 1; i >= 0; i--) {
   arr.push(v[i]/sum);
  }
  console.log(arr);
 };
};

Sandbox('vector', function(box){
 var a = [1,2,3,4];
 console.dir(box.sum(a));
 box.normalize(a,box.sum);
});

  • Statyczne pola

Aby uzyskać konstrukcję składniową statycznego pola, znaną z języków obiektowych typu C++, należy pamiętać o tym, że każda funkcja jest obiektem, do którego można dynamicznie przypisywać właściwości.

function Computer(){
 Computer.instances++;
 return {
  ram : 4096,
  hdd : 1024
 };
}

Computer.instances = 0;

var computers = [new Computer(), new Computer()];
console.log(Computer.instances);

  • Chaining pattern


Jest to wzorzec umożliwiający wywoływanie wielu funkcji w łańcuchu, co poprawia czytelność kodu i zmniejsza jego ilość, kosztem utrudnień w testowaniu.
var number = {
 value : 0,
 add : function(x){
  this.value += x;
  return this;
 },
 multiply : function(x){
  this.value *= x;
  return this;
 },
 print : function(){
  return this.value;
 }
};

console.log(number.add(10).multiply(12).print());

poniedziałek, 15 października 2012

[HTML|JS|CSS] JavaScript Patterns: Funkcje

Funkcje są powszechnie stosowane w języku JavaScript. W porównaniu z językami obiektowymi mamy dwie bardzo ważne cechy, o których nie należy zapominać: funkcje są obiektami i zapewniają osobny scope. Można je tworzyć dynamicznie, przypisywać do zmiennych, przekazywać jako argumenty funkcji i jako wartość zwracaną przez funkcję. Każda funkcja ma właściwość name, którą można wykorzystywać np. podczas debugowania.

var f1 = function(){};
function f2(){}
var f3 = function f3(){};

console.log(f1.name);
console.log(f2.name);
console.log(f3.name);

Konsola:

undefined
f2
f3

Poniżej kilka wzorców projektowych przydatnych przy pracy z funkcjami.

  • Callback

Przekazanie funkcji jako argumentu by wykonać pewne specyficzne operacje np. zapewniając dodatkowy scope.

var mycallback = function(msg){
 if(msg % 2 == 0){
  mycallback.counter++;
 }
};
mycallback.counter = 0;

var fun = function(fun){
 var j = 2;
 for (var i = 1; i < 100000; i++) {
  j += i;
  fun(j);
 }
};

fun(mycallback);
console.log('Operations finished');
console.log('Even numbers: ' + mycallback.counter);

  • Funkcja zwracająca funkcję

Funkcja może być zwracana przez inną funkcję. Zewnętrzna funkcja może się zająć pewną inicjalizacją, natomiast funkcja zwracana dokonywać modyfikacji danych.

function myfun(){
 var number = 0;
 return function(){
  number++;
  console.log(number);
 };
}

var myfunvar = myfun();
myfunvar();
myfunvar();
myfun()();

Konsola

1
2
1

  • Samo definiująca się funkcja

Bardzo użyteczny wzorzec dla opóźnionych inicjalizacji.

var sdfunc = function(){
 var name = "Mike";
 console.log('Initializing');
 sdfunc = function(){
  console.log('My name is ' + name);
 };
};

console.log('Before');
sdfunc();
sdfunc();

Konsola:

Before
Initializing
My name is Mike

  • Immediate functions

Gdy chcemy wykonać pewne operacje tylko raz i nie chcemy zaśmiecać globalnego scope nowymi zmiennymi.


(function(){
 var day = (new Date()).getDay();
 var days = ['pn','wt','sr','cz','pt','sb','nd'];
 console.log('Dzisiaj jest: ' + days[day - 1]);
})();

  • Memoization

Wzorzec używany do cache'owania wyników długich operacji, tak aby przeprowadzane były tylko raz, a przy kolejnych wywołaniach można było korzystać z uzyskanych już wyników.

var cfun = function(param){
 if(!cfun.cache[param]){
  var result = 2;
  console.log('Computing...');
  for (var i = 1; i < param; i++) {
   result += i;
  }
  cfun.cache[param] = result;
 }
 return cfun.cache[param];
};

cfun.cache = {};

console.log(cfun(55));
console.log(cfun(33));
console.log(cfun(55));

Konsola:


Computing...
1487
Computing...
530
1487

  • Currying

Nazwa wzorca pochodzi od nazwiska znanego matematyka Haskella Curry. Jego istotą jest transformacja funkcji, która przyjmuje wiele parametrów. Po transformacji będzie możliwe wywołanie funkcji jako łańcucha wywołań z pojedynczym argumentem.Szczegółowy opis tutaj.

var multiply = function(x,y){
 var oldy = y, oldx = x;
 if(typeof oldy === "undefined"){
  return function(newy){
   return oldx*newy;
  };
 }
 return x*y;
};

console.log(multiply(2,4));
var multiply5 = multiply(5);
console.log(multiply5(2));
console.log(multiply(3)(4));

niedziela, 29 lipca 2012

Wzorce projektowe

Spis postów na temat wzorców projektowych:

SOLID:



Wzorce:


[Wzorce projektowe] State

Analogia z życia:

Chcąc pobrać pieniądze idziemy do bankomatu. Po włożeniu karty i podaniu PINu możemy zgłosić żądanie pobrania pieniędzy. Jeśli spełnione jest kilka warunków, to maszyna wyda nam kwotę jakiej żądamy. Może się jednak zdarzyć tak, że nie mamy środków na koncie, albo nasze konto zostało zablokowane. Inną możliwością jest brak funduszy w maszynie. Każda z tych sytuacji jest osobnym stanem, który musi być rozpoznany i obsługiwany przez bankomat.





Zastosowanie:

W każdej sytuacji, gdy mamy do czynienia z obiektem, którego akcje zależą od stanu w jakim się znajduje. Stan obiektu jest zmieniany w czasie gdy aplikacja jest uruchomiona. Przejścia pomiędzy stanami niekonieczną muszą być dozwolone w pewnym kontekście, co też należy obsłużyć.

Zasada działania:

Głównym rdzeniem programu jest obiekt zmieniający stany dynamicznie. Stan reprezentuje się np. za pomocą enuma. Obiekt udostępnia metodę do zmiany stanu i sprawdzenia, czy dany stan można w danej chwili osiągnąć. Każdy stan implementuje jeden wspólny interfejs i udostępnia metody reprezentujące akcje.

Przykład implementacyjny:

    public interface ITvActions
    {
        void TurnOn();
        void TurnOff();
        void ChangeChannel(int n);
    }
public abstract class TVState
    {
        public Tv Owner { get; set; }

        protected TVState(Tv @object)
        {
            Owner = @object;
        }
    }
 public class RunningState : TVState, ITvActions
    {
        public RunningState(Tv @object) : base(@object)
        {
        }

        public void TurnOn()
        {
            Console.WriteLine("TV already running");
        }

        public void TurnOff()
        {
            Owner.IsRunning = false;
            Owner.SetState("Off");
            Console.WriteLine("Turned Off");
        }

        public void ChangeChannel(int n)
        {
            Owner.CurrentChannel = n;
            Console.WriteLine("Switched to " + n);
        }
    }
class TurnedOffState : TVState, ITvActions
    {
        public TurnedOffState(Tv @object) : base(@object)
        {
        }

        public void TurnOn()
        {
            Owner.SetState("Running");
            Owner.IsRunning = true;
            Console.WriteLine("Succesfully turned on");
        }

        public void TurnOff()
        {
            Console.WriteLine("Already turned off");
        }

        public void ChangeChannel(int n)
        {
            Console.WriteLine("Unable to change");
        }
    }
public class Tv : ITvActions
    {
        public ITvActions State { get; set; }
        public int CurrentChannel { get; set; }
        public bool IsRunning { get; set; }

        public Tv()
        {
            IsRunning = true;
            CurrentChannel = 1;
            State = new RunningState(this);
        }

        public void TurnOn()
        {
            State.TurnOn();
        }

        public void TurnOff()
        {
            State.TurnOff();
        }

        public void ChangeChannel(int n)
        {
            State.ChangeChannel(n);
        }

        public void SetState(string state)
        {
            switch (state)
            {
                case "Running":
                    State = new RunningState(this);
                    break;
                case "Off":
                    State = new TurnedOffState(this);
                    break;
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Tv tv = new Tv();
            tv.TurnOff();
            tv.ChangeChannel(4);
            tv.TurnOn();
            tv.ChangeChannel(1);
            Console.ReadKey();
        }
    }

sobota, 28 lipca 2012

[Wzorce projektowe] Service Locator

Analogia z życia:

Dzisiejsze telefony komórkowe oferuję wiele funkcjonalności. Możemy na przykład napisać wiadomość tekstową lub do kogoś zadzwonić. Z punktu widzenia użytkownika, wysłanie takiej wiadomości jest wykorzystaniem pewnej usługi. Piszemy wiadomość, podajemy numer i klikamy wyślij. To w jaki sposób wiadomość zostanie przekazana i ile za nią zapłacimy zależy między innymi od operatora sieci. Wkładając kartę SIM, możemy na starcie określić wspomniane warunki, aby później po uruchomieniu telefonu w każdej chwili korzystać z usługi w wybranej wersji.


Zastosowanie:

Wzorzec lokatora usług znajduje zastosowanie w sytuacji, gdy chcemy odseparować obiekty, od usług z których korzystają. Zwiększy to niezależność komponentów i ułatwi przyszłe wprowadzanie modyfikacji. Dodatkowo mając kilka podobnych serwisów, można wybierać któryś z nich przed uruchomieniem aplikacji przez odpowiednie ustawienia konfiguracyjne. Rozwiązanie takie wspiera testowalność.

Zasada działania:

Wprowadza się klasę lokatora usług, zawierającą statyczne pole typu HashTable z usługami. Kluczem do usługi w tablicy może być typ usługi lub podana nazwa. Dodatkowo klasa lokatora usług zawiera statyczne metody do pobrania i dodania nowego serwisu. Serwisy można dodawać na starcie aplikacji, np. w WPF czy Silverlight w metodzie OnStartup.

Przykład implementacyjny:

 public interface ILogger
    {
        void Write(string msg);
    }

    public class ConsoleLogger : ILogger
    {
        public void Write(string msg)
        {
            Console.WriteLine(msg);
        }
    }
internal class ServiceLocator
    {
        private static readonly Hashtable Services 
            = new Hashtable();

        public static void AddService<T>(T service)
        {
            Services.Add(typeof(T),service);
        }

        public static void AddService<T>(string name, T service)
        {
            Services.Add(name, service);
        }

        public static T GetService<T>()
        {
            return (T)Services[typeof(T)];
        }

        public static T GetService<T>(string name)
        {
            return (T)Services[name];
        }

    }
public class Fibonnaci
    {
        public int Number { get; set; }
        public int LastNum { get; set; }

        public Fibonnaci()
        {
            LastNum = 0;
            Number = 1;
        }

        public int Next()
        {
            var n = LastNum + Number;
            LastNum = Number;
            Number = n;
            IsDivisibleBy4();
            return n;
        }

        public void IsDivisibleBy4()
        {
            if (Number%4 != 0) return;
            var logger = ServiceLocator.GetService<ILogger>();
            logger.Write(String.Format
                             ("Number {0}, last : {1}",Number, LastNum));
        }
    }
class Program
    {
        static Program()
        {
            ServiceLocator.AddService<ILogger>(new ConsoleLogger());
        }

        static void Main(string[] args)
        {
            Fibonnaci fibonnaci = new Fibonnaci();
            for (int i = 0; i < 100; i++)
            {
                fibonnaci.Next();
            }
            Console.ReadKey();
        }
    }

[Wzorce projektowe] Repository

Analogia z życia:

Chcąc wypożyczyć książki udajemy się do biblioteki. Osoby pracujące tam pomagają nam znaleźć interesujące nas pozycje, podają z magazynu egzemplarze, które nas interesują oraz wstawiają te, które chcemy oddać. W przypadku bibliotek samoobsługowych musielibyśmy wiedzieć o tym, w jaki sposób wyszukiwać i gdzie fizycznie szukać konkretnych egzemplarzy.



Zastosowanie:

Wszędzie tam, gdzie mamy do czynienia z dostępem do danych : połączenia SQL, pobieranie danych z plików, czy webserwisów. Wzorzec repozytorium zapewnia enkapsulację kodu odpowiadającego za połączenie ze źródłem danych w taki sposób. Jest to zatem warstwa pośrednia pomiędzy buissness logic, a źródłem danych, co ułatwia np. testowanie. Wzorzec często występuję w parze z wzorcem Unit of Work odpowiadającym za zatwierdzanie lub wycofywanie zmian.

Zasada działania:

Wprowadza się obiekt repozytorium udostępniający pewne API. W skład API wchodzą takie metody, jak dodawanie encji, usuwanie czy przeszukiwanie kolekcji encji. Bazowe API może być generycznym interfejsem, z którego dziedziczą konkretne repozytoria.

Przykład implementacyjny:

public interface IEntity
    {
        int Id { get; set; }
    }

    public class Customer : IEntity 
    {
        public string Name { get; set; }
        public string Mail { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
        public int Id { get; set; }
    }
public interface IRepository<T>
        where T : IEntity
    {
        void Add(T instance);
        void Remove(T instance);
        IEnumerable<T> Find(Predicate<T> predicate);
    }

    public class CustomerRepository : IRepository<Customer>
    {
        private List<Customer> _entities;

        private int _count;

        public CustomerRepository()
        {
            try
            {
               LoadDataFromXml();
            }
            catch (Exception)
            {
               Console.WriteLine("Unable to load data");
            }
        }

        public void LoadDataFromXml()
        {
            _entities = new List<Customer>();
            XDocument doc = XDocument.Load("Records.xml");
            var list = doc.Elements("records").Elements("record");
            foreach (var element in list)
            {
                IEnumerable<XElement> properties = element.Elements();
                var customer = new Customer();
                customer.Id = _count++;
                customer.Name = properties.ElementAt(0).Value;
                customer.Mail = properties.ElementAt(1).Value;
                customer.Address = properties.ElementAt(2).Value;
                customer.City = properties.ElementAt(3).Value;
                _entities.Add(customer);
                }
        }

        public void Add(Customer instance)
        {
            instance.Id = _count++;
            _entities.Add(instance);
        }

        public void Remove(Customer instance)
        {
            _entities.Remove(instance);
        }

        public IEnumerable<Customer> Find(Predicate<Customer> predicate)
        {
            return _entities.Where(customer => predicate(customer));
        }
    }
class Program
    {
        static void Main(string[] args)
        {
            IRepository<Customer> repository = new CustomerRepository();

            var results = repository.Find(c => c.City.StartsWith("C"));
            foreach (Customer customer in results)
            {
                Console.WriteLine(customer.City);
            }

            repository.Add(new Customer()
                               {
                                   Name = "Jonnie Walker",
                                   Address = "Elm Street 12/9",
                                   Mail = "jwalker@mit.edu",
                                   City = "NY"
                               });

            Console.ReadKey();
        }
    }

wtorek, 24 lipca 2012

[Wzorce projektowe] Prototype

Analogia z życia:

Podróżując lubimy robić zdjęcia. Są one pamiątką z miejsc, które odwiedziliśmy i chwil, które przeżyliśmy. Może się tak zdarzyć, że wywołamy pewne zdjęcie, na którym jest grupa osób. Gdy każda z tych osób obejrzy zdjęcie, dojdzie do wniosku, że chce takie samo. Możemy oczywiście zebrać całą grupę, pojechać w to samo miejsce i zrobić jeszcze zdjęcie kilka razy. Ale na 100% będzie ono inne, a jego zrobienie będzie nas kosztowało sporo wysiłku, czasu a może i pieniędzy. Można także zrobić kopię u fotografa, przez co zaoszczędzimy sobie wymienionych kłopotów.

Zastosowanie:

Prototyp jest jednym z najprostszych, o ile nie najprostszym wzorcem projektowym. Stosuje się go wszędzie tam, gdzie jest potrzebna kopia pewnego obiektu, a jej utworzenie w sposób tradycyjny wydaje się bezsensowne. Proces tworzenia może być np. bardzo długi (pobieranie z bazy danych, pobieranie pliku), W innym przypadku odtworzenie obiektu może wymagać wywołania szeregu metod lub też stan obiektu może być w pewnym sensie niestabilny i trudny do odtworzenie w ogóle.


Zasada działania:


Używając języka C# wystarczy zaimplementować interfejs ICloneable dla obiektu, który chcemy klonować, pamiętając o zasadach tworzenia kopii : Deep vs Shallow Copy. Dla obiektów z propercjami prostych typów wystarczy metoda MemberwiseClone().

Przykład implementacyjny:


public class RandomMelody : ICloneable
    {
        public List<int> Frequencies { get; set; }
        public List<int> Durations { get; set; }

        internal RandomMelody(){}

        public RandomMelody(int n)
        {
            Frequencies = new List<int>();
            Durations = new List<int>();

            Random r = new Random();

            for (int i = 0; i < n; i++)
            {
                Frequencies.Add((r.Next()%300) + 400);
                Durations.Add((r.Next() % 25) + 100);
            }
        }

        public void Play()
        {
            for (int i = 0; i < Frequencies.Count; i++)
            {
                Console.Beep(Frequencies[i], Durations[i]);
            }
        }

        public object Clone()
        {
            var rm = new RandomMelody();
            rm.Durations = new List<int>();
            rm.Frequencies = new List<int>();
            for (int i = 0; i < Durations.Count; i++)
            {
                rm.Durations.Add(Durations[i]);
                rm.Frequencies.Add(Frequencies[i]);
            }
            return rm;
        }
 class Program
    {
        static void Main(string[] args)
        {
            var s1 = new RandomMelody(10);
            var s2 = new RandomMelody(10);
            var s3 = s1.Clone() as RandomMelody;

            Console.WriteLine("Original song");
            s1.Play();
            Console.WriteLine("New song");
            s2.Play();
            Console.WriteLine("Original song");
            s1.Play();
            Console.WriteLine("Copy");
            s3.Play();
        }
    }

poniedziałek, 23 lipca 2012

[Wzorce projektowe] Null Object

Analogia z życia:

Każdy użytkownik komunikatora gadu - gadu ma do dyspozycji kilka statusów. Może być np. dostępny lub niewidoczny. Mając na liście znajomych, nie możemy w prosty sposób rozróżnić kto z nich jest niewidoczny, a kogo nie ma w danej chwili przy komputerze. Komunikator zapewnia jednak, że możemy wysłać wiadomość do każdego z nich : jeśli użytkownik jest przy komputerze, to może nam błyskawicznie odpisać, natomiast gdy go nie ma, wiadomość nie zaginie, lecz zostanie mu dostarczona po zalogowaniu do komunikatora.



Zastosowanie:

Wzorca Null Object można użyć, gdy mamy do czynienia z dużą ilością obiektów, które w pewnym etapie programu mają wartość null. Aby uniknąć wyjątku NullReference oraz sprawdzeń, czy dany obiekt jest nullem, można wprowadzić dodatkowy obiekt bez jakiejkolwiek funkcjonalności, np. zawierający puste metody tego samego interfejsu co zwykły obiekt. Kolejnym powodem, dla którego warto zastosować ten wzorzec jest sytuacja, w której klient pobiera obiekty i nie chcemy, aby zajmował się sprawdzaniem, czy ich wartość jest równa null.

Zasada działania:

Obiekt klienta przechowuje referencję do obiektu typu pewnej klasy abstrakcyjnej. Z tej klasy dziedziczą dwa typy : rzeczywisty obiekt i obiekt - null. Można także wprowadzić kilka obiektów null jako reprezentację różnych stanów "nieaktywności". W niektórych przypadkach warto rozważyć zastosowanie singletonu, jako obiektu - null.

Przykład implementacyjny:

public class Fire
    {
        public int Power { get; set; }
    }

    public abstract class FirefighterBase
    {
        public abstract void Distinguish(Fire fire);

        static InactiveFirefighter _inactive = new InactiveFirefighter();

        public static FirefighterBase NULL
        {
            get { return _inactive; }
        }

        public class InactiveFirefighter : FirefighterBase
        {
            public override void Distinguish(Fire fire)
            {
                Console.WriteLine("Cannot help you");
            }
        }
    }

    public class ActiveFirefighter : FirefighterBase
    {
        private int _strength;

        public ActiveFirefighter()
        {
            Random r = new Random();
            _strength = r.Next() % 25;
        }

        public override void Distinguish(Fire fire)
        {
            fire.Power -= _strength;
            Console.WriteLine("Distinguishing...");
        }
    }
 public static class FireBrigade
    {
        public static FirefighterBase GetFireFighter(int id)
        {
            if(id % 2 ==0)
            {
                return FirefighterBase.NULL;
            }
            else
            {
                return new ActiveFirefighter();
            }
        }
    }
class Program
    {
        static void Main(string[] args)
        {
            Fire f = new Fire() {Power = 300};
            List<FirefighterBase> fighters = new List<FirefighterBase>();
            for (int i = 0; i < 15; i++)
            {
                fighters.Add(FireBrigade.GetFireFighter(i));
            }
            foreach (FirefighterBase firefighterBase in fighters)
            {
                firefighterBase.Distinguish(f);
            }

            Console.WriteLine(String.Format("Fire power is now... {0}",f.Power));

            Console.ReadKey();
        }
    }

wtorek, 17 lipca 2012

[Wzorce projektowe] Memento

Analogia z życia:

Popularny odtwarzacz plików mp3, Winamp oferuje szereg funkcjonalności. Jedną z nich jest losowe wybieranie kolejnych odtwarzanych utworów. Po odsłuchaniu kilku wybranych w ten sposób piosenek, możemy wrócić do pierwszego utwory przechodząc po drodze wszystkie, które do tej pory słuchaliśmy. Następnie można również przejść przez kolejne słuchane utwory aż do ostatniego tak, by potem wylosować kolejny.



Zastosowanie:

Wzorzec Memento jest przydatny w momencie, gdy chcemy w naszej aplikacji zaimplementować funkcjonalność przechodzenia pomiędzy stanami pewnego obiektu, np. cofanie ostatnich zmian. Zatem należy go zastosować w przypadku, gdy potrzebujemy w jakiś sposób śledzić stan naszego obiektu i dokonywać zmian tego stanu.

Zasada działania:

Wprowadza się dwa obiekty : autora (originator), który zmienia swój stan podczas działania programu, oraz dozorcę (caretaker), czyli obiekt który wykonuje operacje na autorze, mające na celu zmianę stanu oraz pobieranie obecnego stanu. Dodatkowo korzysta się z obiektu Memento, który przechowuje stan autora Autor tworzy  mementa, natomiast dozorca utrzymuje je, nie ingerując w ich zawartość. To dozorca żąda obiektów memento od autora i gdy zachodzi taka potrzeba oddaje je autorowi. Same mementa powinny być obiektami przechowującymi jedynie stan - bez zachowania, natomiast stan powinien być interpretowany jedynie przez autora. Przeważnie mementa przechowuje się na stosie, pobierając kolejne stany przy kolejnych żądaniach cofania stanu. Można także wprowadzać operację przywracania stanu, co wiąże się z wprowadzeniem drugiego stosu. W przypadku niektórych obiektów można zamiast stanu przechowywać jedynie wykonane operacje i przy próbie przywracania stanu stosować operacje odwrotne, ale nie w każdym przypadku jest to możliwe.

Przykład implementacyjny:

public interface IMemento
    {
        object State { get; set; }
    }

    public class CalculatorState : IMemento
    {
        private object _state;

        public object State
        {
            get { return _state; }
            set { _state = value; }
        }
    }

    public class SimpleCalculator
    {
        public int Value { get; set; }

        public IMemento GetState()
        {
            return new CalculatorState() {State = Value};
        }

        public void RestoreState(IMemento memento)
        {
            Value = int.Parse(memento.State.ToString());
        }
    }

    public class CalculatorCaretaker
    {
        private readonly Stack<IMemento> _states = new Stack<IMemento>();

        public void StoreState(IMemento item)
        {
            _states.Push(item);
        }

        public void UndoFunctionality(SimpleCalculator calc)
        {
            if(_states.Count > 1)
            {
                _states.Pop();
                calc.RestoreState(_states.Peek());
            }
        }
    }
 class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Press number to add, press u to undo");
            CalculatorCaretaker caretaker = new CalculatorCaretaker();
            SimpleCalculator calculator = new SimpleCalculator();

            while(true)
            {
                var n = Console.ReadLine();
                if(n.Contains('u'))
                {
                    caretaker.UndoFunctionality(calculator);
                }
                else
                {
                    int nb;
                    bool b = int.TryParse(n, out nb);
                    if(b)
                    {
                        calculator.Value += nb;
                        caretaker.StoreState(calculator.GetState());
                    }
                    else
                    {
                        break;
                    }
                }
                Console.WriteLine(String.Format("The value is : {0} ", calculator.Value));
            }
        }
    }

sobota, 14 lipca 2012

[Wzorce projektowe] Mediator

Analogia z życia:

Chyba każdy internauta korzystał kiedyś z forów internetowych. Służą one do wymiany informacji na różne tematy między uczestnikami. Każdy użytkownik (nie licząc administracji) ma takie same prawa i także dotyczą go te same obowiązki. Każdy z każdym może się wymieniać informacjami na dowolny temat, a miejscem, gdzie ta wymiana danych się dokonuje jest temat w odpowiednim dziale na forum.




Zastosowanie:

Wzorzec Mediatora okazuje się przydatny, gdy mamy do czynienia z grupą obiektów o podobnych cechach, które mają się ze sobą w pewien sposób komunikować. Wprowadza się zatem dodatkowy, scentralizowany obiekt mediatora, który zbiera informacje od zwykłych obiektów, zwanych kolegami i rozprowadza je zgodnie z pewną logiką, na przykład tylko do uprawnionych obiektów.

Zasada działania:

Wprowadza się pewną klasę bazową z której dziedziczą obiekty zwane kolegami. Każdy obiekt będzie miał w sobie zaszytą logikę przekazywania wiadomości do instancji klasy mediatora. Mediator natomiast zajmuje się rozprowadzeniem wiadomości po wszystkich obiektach - kolegach, którzy są taką wiadomością w jakiś sposób zainteresowani.

Przykład implementacyjny:

public abstract class Warship
    {
        private readonly IWarshipsController _controller;

        protected Warship(bool isActive, IWarshipsController controller)
        {
            _controller = controller;
            IsActive = isActive;
            _controller.RegisterWarship(this);
        }

        public virtual int Range { get; set; }
        public bool IsActive { get; set; }

        private int _lattitude;
        public int Lattitude
        {
            get { return _lattitude; }
            set
            {
                _lattitude = value;
                _controller.ReceiveCoordinatesNotification(this);
            }
        }

        private int _longitude;
        public int Longitude
        {
            get { return _longitude; }
            set
            {
                _longitude = value;
                _controller.ReceiveCoordinatesNotification(this);
            }
        }

        public void InformAboutFellows()
        {
            Console.WriteLine(this.GetType().Name.ToString() + " Acknowledged");
        }
    }

    public class Cruiser : Warship
    {
        public Cruiser(bool i, IWarshipsController c) : base(i,c)
        {
            
        }

        public override int Range
        {
            get
            {
                return 100;
            }
        }
    }

    public class Destroyer : Warship
    {
        public Destroyer(bool i, IWarshipsController c) : base(i,c)
        {
            
        }

        public override int Range
        {
            get
            {
                return 50;
            }
        }
    }
 public interface IWarshipsController
    {
        void ReceiveCoordinatesNotification(Warship sender);
        void RegisterWarship(Warship warship);
    }

    public class MarinaryGovernment : IWarshipsController
    {
        private List<Warship> _ships = new List<Warship>();

        public void ReceiveCoordinatesNotification(Warship sender)
        {
            foreach (Warship warship in _ships.Where(x => x!=sender))
            {
                if(warship.Lattitude - sender.Lattitude < warship.Range
                    || warship.Longitude - sender.Longitude < warship.Range)
                {
                    warship.InformAboutFellows();
                }

            }
        }

        public void RegisterWarship(Warship warship)
        {
            if(!_ships.Contains(warship))
            {
                _ships.Add(warship);
            }
        }
    }
class Program
    {
        static void Main(string[] args)
        {
            MarinaryGovernment gov = new MarinaryGovernment();
            Warship c1 = new Cruiser(true, gov);
            Warship c2 = new Cruiser(true, gov);
            Warship c3 = new Cruiser(true, gov);

            Warship d1 = new Destroyer(true, gov);
            Warship d2 = new Destroyer(true, gov);
            Warship d3 = new Destroyer(true, gov);

            c1.Longitude = 150;
            c2.Longitude = 125;
            c3.Longitude = 50;

            d1.Lattitude = 75;
            d2.Lattitude = 100;
            d3.Lattitude = 130;

            Console.ReadKey(true);

        }
    }