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

sobota, 31 października 2015

[HTML|JS|CSS] ES6: Strings, arrays, collections

Strings

Poza kilkoma dodatkowymi metodami na obiekcie stringa, takimi jak includes, startsWith, endsWith, znanymi z innych języków programowania, najważniejsza zmiana to tzw. template strings - wyrażania opakowane w backticki, służące do wygodnego budowania stringów.

let a = 20;
let b = 6;
let c = "JavaScript";
let str = `My age is ${a+b} and I love ${c}`;

console.log(str);

Arrays 

Metoda from przypomina select z C# z możliwością wstawienia opcji jako trzeci parametr. Dodatkowo wprowadzono metodę fill do wstawiania danej wartości w istniejącej już tablicy (z możliwością zdefiniowania granic wstawiania). Kolejne ważne metody to find oraz findIndex przyjmujące predykat.

Collections

W ES5 mieliśmy do dyspozycji tylko tablice. Teraz dochodzą nowe kolekcje, takie jak:
  • Array buffer - wydajna kolekcja przechowująca liczby, nie może rosnąć dynamicznie
  • Typed array - wrapper na array buffer pozwalający w prosty sposób czytać z niej i zapisywać obiekty
  • Set - kolekcja unikalnych wartości dowolnego typu, powtarzające się wartości są wyrzucane
  • Weak Set - w odróżnieniu od set przechowuje jedynie referencje do obiektów
  • Map - kolekcja par klucz  wartość
  • Weak Map - w odróżnieniu od map kluczami mogą być jedynie referencje obiektów

let set = new Set("Hello!!!");
set.add(12);
console.log(set.has("!")); //check if value exists
console.log(set.size);
set.delete(12);
console.log(...set); //H e l o !
set.clear();

let map = new Map();
let o = {n: 1};
map.set(o, "A"); //add
map.set("2", 9);
console.log(map.has("2"));
console.log(map.get(o));
console.log(...map);
map.delete("2"); //remove key with value
map.clear();

[HTML|JS|CSS] ES6: Syntax

Nowa specyfikacja ECMAScript wnosi sporo nowości do JavaScriptu. Nie możemy ich jeszcze w pełni wykorzystywać bez potrzeby transpilacji, ale kwestią czasu jest, kiedy przeglądarki zaimplementują wszystkie nowości w swoich engine'ach. W tym momencie do transpilacji można używać np. babeljs, będącego pakietem node.js. Instalujemy go poprzez wywołanie

npm install -g babel-cli
npm install --save-dev babel-preset-es2015
babel --presets es2015 script_es6.js --out-file script_es5.js

Ostatnie polecenie reprezentuje transpilację z jednego pliku do drugiego. Od kilku dni należy do tego celu wykorzystywać preset es2015, wcześniej transpilacja była domyślną operacją.

Najważniejsze zmiany składniowe w ES2015 (ES6):

let keyword

Deklaruje zmienną typu block-scoped, co jest naturalne w większości języków programowania, w JS nie było, kiedy mieliśmy do wyboru tylko var (function-scoped variables).

function myFunction()
{
  console.log(a);

  let b = 13; //accessible throughout function
  console.log(b);

  if(true)
  {
    let c = 14; //accessible throughout function
    let b = 15;
    console.log(b);
  }

  //console.log(c); //c is not defined
}

Zmienne możemy nadpisywać poprzez let, ale tylko jeżeli są w innym block-scope. Zaleca się używanie let zamiast var, ze względu na zmniejszenie podatności na bugi takiego kodu.

const keyword

Zmienne typu read-only, ostatnia linijka zwróci SyntaxError podczas transpilacji, a docelowo także błąd w run-time. Zmienne typu const są również block-scoped.

const pi = 3.141;
var r = 2;

console.log(pi * r * r); //Output "12.564"

pi = 12; //throws read-only exception

 W przypadku obiektów deklarowanych jako const będziemy mogli modyfikować ich propercje, a błąd dostaniemy tylko przy próbie nadpisania całego obiektu.

default parameter values

W ES5 nie musieliśmy przekazywać wszystkich argumentów wywołując funkcję. Nieprzekazane zmienne przyjmowały wartość undefined. Teraz możemy od razu zadeklarować dla każdego parametru domyślne wartości.

function myFunction(x = 1, y = 2, z = 3){
 console.log(x, y, z); //Output "6 7 3"
}

myFunction(6, 7);

spread operator

Operator ten reprentują ... , a jego zadaniem jest rozdzielenie obiektu typu iterable na pojedyncze elementy. Po transpilacji otrzymamy metodę apply (przyjmuje listę parametrów jako tablicę) wołaną na funkcji, ale docelowo wykorzystane będą znacznie szybsze mechanizmy w runtime.

function myFunction3(a, b)
{
 return a + b;
}

let data = [1, 4];
let result = myFunction3(...data);
console.log(result); //Output "5"

Można go wykorzystać także przy budowaniu tablicy z innej tablicy.

rest parameter

Trzech kropek można także użyć do obsługi funkcji ze zmienną liczbą argumentów. Rest parameter zmapuje nadmiarowe zmienne jako tablicę.

function myFunction(a, b, ...args)
{
  console.log(args); //Output "3, 4, 5"
}

myFunction(1, 2, 3, 4, 5);

array destructuring assignment

Pozwala na przypisanie wartości do wielu zmiennych za pomocą jednego wyrażenia. Prawą stroną musi być obiekt typu iterable. Array destructuring pozwala na ignorowanie niektórych elementów oraz użycie spread operator-a.

let myArray2 = [1, 2, 3];
let [g, h, i] = myArray2;
let [j,  , k] = myArray2;
let [l, ...m] = myArray2;

object destructuring assignment

W analogiczny sposób możemy wykonywać destructuring na obiektach.

let object = {"name" : "John", "age" : 23};
let {name, age} = object; //object destructuring assignment syntax

arrow function

Przypomina dobrze znane np. z C# lambda expression. Więcej niż jedną linię należy opakowywać w nawiasy klamrowe.

let circleArea = (pi, r) => pi * r * r;
let result = circleArea(3.14, 3);

let circleArea2 = (pi, r) => {
 let area = pi * r * r;
 return area;
};
let result = circleArea2(3.14, 3);


enhanced object literals

Wprowadzono kilka udogodnien składniowych przy budowie obiektów. Mamy do dyspozycji skróconą składnię przy definiowaniu propercji (również tych z wyliczanymi nazwami) oraz metod, 

let x = 1, y = 2;
let object = { x, y };

console.log(object.x); //output "1"

let object = {
  ["first" + "Name"]: "Eden",
};

//extract
console.log(object["first" + "Name"]); //Output "Eden"

let object = {
  myFunction(){
    console.log("Hello World!!!"); //Output "Hello World!!!"
  }
}