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

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

poniedziałek, 9 lipca 2012

[Wzorce projektowe] Decorator

Analogia z życia:

Pakiet Matlab - Simulink jest jednym z najpopularniejszych narzędzi do modelowania matematycznego w naukach inżynierskich. Klient może zamówić najtańszą podstawową wersję składającą się z Matlaba oraz podstawowych rozszerzeń Simulinka. Po jakimś czasie może pojawić się potrzeba modelowania ekonomicznego, więc klient jest zmuszony dokupić Financial Toolbox. Zatem podstawowe funkcjonalności zostaną nienaruszone, natomiast całość zostanie rozszerzona o dodatkowe możliwości. Podobnie sytuacja będzie się miała w przypadku przetwarzania obrazów, czy symulacji bioinformatycznych. Zawsze będziemy otrzymywali pewne rozszerzenie posiadanej już wersji.




Zastosowanie:

Wzorzec dekoratora umożliwia dynamiczne rozszerzanie istniejących obiektów. Umożliwia to elastyczność projektowania i staje się poważną konkurencją dla pod klas w hierarchii dziedziczenia. Czasami bowiem mamy do czynienia z zagadnieniem na tyle złożonym, że rozpatrywanie każdego podprzypadku, jako osobnej klasy dziedziczącej z klasy bazowej spowodowałoby rozrośnięcie się programu do ogromnych rozmiarów. Tu z pomocą przychodzi dekorator zapewniający dynamikę przy rozszerzaniu istniejących obiektów. Wzorzec ten może okazać się także przydatny przy rozszerzaniu klas zapieczętowanych (sealed) lub gdy mamy za zadanie rozszerzyć czyjś system, a nie chcemy go modyfikować wewnątrz.

Zasada działania:

Tworzymy pewną bazową abstrakcyjną klasę, lub interfejs, który ma być dekorowany. Konkretne implementacje w konstruktorze przyjmują jako parametr obiekt tej właśnie klasy. W metodach z interfejsu wywoływane są metody z klasy bazowej, plus to, co ma do zaoferowania dekorator. Parametr konstruktora jest zawsze dekorowany przez tworzony tym konstruktorem obiekt.

Przykład implementacyjny:

 public interface IDeveloperFunction
    {
        void Print();
    }

    public class Tester : IDeveloperFunction
    {
        public void Print()
        {
            Console.WriteLine("Tester");
        }
    }

    public class Coder : IDeveloperFunction
    {
        private IDeveloperFunction _myFunction;

        public Coder(IDeveloperFunction function)
        {
            _myFunction = function;
        }

        public void Print()
        {
            if(_myFunction != null)
                _myFunction.Print();
            Console.WriteLine("Coder");
            
        }
    }

    public class Architect : IDeveloperFunction
    {
        private IDeveloperFunction _myFunction;

        public Architect(IDeveloperFunction function)
        {
            _myFunction = function;
        }

        public void Print()
        {
            if(_myFunction != null)
                _myFunction.Print();
            Console.WriteLine("Architect");
            
        }
    }

    public class Manager : IDeveloperFunction
    {
        private IDeveloperFunction _myFunction;

        public Manager(IDeveloperFunction function)
        {
            _myFunction = function;
        }

        public void Print()
        {
            if (_myFunction != null)
                _myFunction.Print();
            Console.WriteLine("Manager");
        }
    }
 class Program
    {
        static void Main(string[] args)
        {
            IDeveloperFunction manager1 = new Manager(null);
            IDeveloperFunction manager2 = new Manager(new Architect(new Coder(new Tester())));
            Console.WriteLine("Manager 1");
            manager1.Print();
            Console.WriteLine("Manager 2");
            manager2.Print();
            Console.Read();
        }
    }