ś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();
}

wtorek, 3 listopada 2015

[HTML|JS|CSS] ES6: Classes

Nowa specyfikacja EcmaScript wproawdza słowo kluczowe class, by znacznie uprościć budowanie obiektowych konstrukcji w JS. Do tej pory obiekty tworzyliśmy między innymi za pomocą konstruktorów (funkcja wołana ze słowem kluczowym new), natomiast za dziedziczenie odpowiadało property prototype. ES6 wprowdza klasy, aby uporządkować te mechanizmy. Jedynym celem, dla którego powinniśmy używać klas jest tworzenie obiektów.

Klasy możemy definiować na dwa sposoby:

// class declaration
class Car
{
  constructor(make)
  {
    this.make = make;
  }
}

var ford = new Car("Ford");
console.log(ford.make);

console.log(typeof Car); //function

// class expression syntax
var Plane = class {
  constructor(make)
  {
    this.make = make;
  }
}

var plane = new Plane("Airbus");
console.log(plane.make);

Poza konstruktorami, klasy mogą zawierać regularne funkcja (które zostaną dodane do prototypu obiektu), oraz settery i gettery pozwalające na zaimplementowanie reguł enkapsulacji (jak na przykład w C#). Specjalnym rodzajem metod są metody statyczne - nie zostaną one dodane do prototype, tylko do samej klasy.

class Tank
{
  constructor()
  {
    this._name = "";
  }

  get name(){
  	console.log("getting name");
  	return this._name;
  }

  set name(value){
  	console.log("setting name");
  	this._name = value;
  }

  fire(){
  	console.log("fire !");
  }

  static report(){
  	console.log("reporting !");
  }
}


var abrams = new Tank();
abrams.name = "Abrams";
console.log(abrams.name);
abrams.fire();
Tank.report();

console.log(typeof abrams.__proto__.fire); //function
console.log(typeof abrams.__proto__.report); //undefined

Klasy niosą za sobą także możliwość dziedziczenia, reprezentowaną przez słowo kluczowe extends. Dziedziczyć można po klasie lub bezpośrednio po konstruktorze. Drugie, nowe słowo kluczowe to super. Służy ono do wywołania konstruktora bazowego oraz do odwołania się do metod z bazowego konstruktora.

function A(a)
{
  this.a = a;
}

A.prototype.printA = function(){
  console.log(this.a);
}

class B extends A
{
  constructor(a, b)
  {
    super(a);
    this.b = b;
  }

  printB()
  {
    console.log(this.b);
  }

  static sayHello()
  {
    console.log("Hello");
  }
}

class C extends B
{
  constructor(a, b, c)
  {
    super(a, b);
    this.c = c;
  }

  printC()
  {
    console.log(this.c);
  }

  printAll()
  {
    this.printC();
    super.printB();
    super.printA();
  }
}

var obj = new C(1, 2, 3);
obj.printAll();

C.sayHello();
asdasd

niedziela, 1 listopada 2015

[HTML|JS|CSS] ES6: Promise

Pisanie asynchronicznego kodu nigdy nie było zbyt przyjemne w JS. Między innymi dlatego zdecydowano się na zaimplementowanie dobrze znanego wzorca Promise, jako natywnego mechanizmu JS. Od wersji ES6, obiekty Promise reprezentujące asynchroniczną operację, tworzone są konstruktorem. Funkcja, którą przyjmuje konstruktur powinna reprezentować asynchroniczną operację (np. AJAX-ową), zakonczoną wywołaniem callbacka resolve lub reject.

var timeoutPromise = new Promise(function(resolve, reject){

  setTimeout(function(){
   var d = new Date();
 var milis = d.getMilliseconds();
 if(milis % 10 === 0){
  reject("an error occured");
 }
 else{
  resolve(d);
 }
  }, 1000);

});

timeoutPromise.then(function(data){
 //onFulfilled
 console.log(data);
}, function(err){
 //onError
 console.log(err);
})


timeoutPromise.then(function(data){
 console.log(data);
 return "processed"; //returns next promise
}, function(err){
 console.log(err);
}).then(function(data){
 console.log(data);
});

Promise'y można tworzyć z synchronicznych obiektów metodami Promise.resolve, np. Promise.resolve(13). Dodatkowo przydatne są metody all i race, które pozwalają obsługiwać wiele asynchronicznych operacji. Pierwsza z nich zwróci callback, kiedy wszystkie promise'y zostaną wykonane, druga w przypadku, gdy wykona się którykolwiek z nich.

var p1 = new Promise(function(resolve, reject){
  setTimeout(function(){
    resolve();
    console.log("p1");
  }, new Date().getMilliseconds());
});

var p2 = new Promise(function(resolve, reject){
  setTimeout(function(){
    resolve();
    console.log("p2");
  }, new Date().getMilliseconds());
});


Promise.all([p1, p2]).then(function(){
  console.log("All done");
});

Promise.race([p1, p2]).then(function(){
  console.log("First done");
});

[HTML|JS|CSS] ES6: Iterators, generators

Obiekty, które zaimplementują iteration protocol nazywane są iteratorami. Obiekt taki musi posiadać metodę next, zwracającą obiekt z dwoma propercjami: value oraz done.
let fibonacci100Iterator = {
  last: 0,
  previous : 1,
  index: 0,
  next: function(){
   let value = this.last + this.previous;
   this.previous = this.last;
   this.last = value;

    return {done: this.index++ > 100, value: value};
  }
};

while(true){
 let {done, value} = fibonacci100Iterator.next();
 console.log(value);
 if(done)
  break;
}

ES6 wprowadza nowy typ danych: Symbol. Od teraz obiekty mogą mieć klucze typu string lub właśnie Symbol. Zadaniem funkcji Symbol() jest wygenerowanie unikalnego identyfikatora tak, by uniknąć kolizji wartości. Obiekt Symbol zawiera kilka predefiniowanych propercji będących symbolami dla różnych konstrukcji ES6. Jedną z nich jest Symbol.iterator.

Obiekty, które pod kluczem Symbol.iterator będą miały zdefiniowaną funkcję zwracającą iterator nazywane będą iterable. Każdy iterable może być odpytywany za pomocą nowej pętli for ..of.

let iterableFib = {
 [Symbol.iterator] : function(){
  return fibonacci100Iterator;
 }
};

for(let val of iterableFib){
 console.log(val);
}

 Kolejną konstrukcją są generatory, czyli funkcje zwracające wiele wartości, jedna po drugiej. Obiekt generatora nie wykonuje się od razu, lecz zwraca obiekt implementujący iterable. Z każdym wywołaniem metody next wykonywana jest ona do napotkania pierwszej instrukcji yield. Wtedy wykonanie zostaje zatrzymane, aż do kolejnego wywołania next.

function* fibonacci_generator()
{
  let last = 0;
  let previous = 1;
  let index = 0;

  while(index++ < 100){
   let value = last + previous;
   previous = last;
   last = value;
   yield value;
  }
}