Home » Featured, Programare

Factory Method: realizare tradiţională sau Generics

18 June 2009 10 Comments

Şablonul de proiectare Factory Method este un şablon frecvent utilizat în proiectarea obiect-orientată şi oferă posibilitatea ca tipul obiectului instanţiat să fie decis de subclasele Creator-ului.

factory

Factory Method design pattern [Sursa: http://dofactory.com/Patterns]

În mod obişnuit, “clientul” fabricării obiectului este chiar Creator-ul, ce permite descendenţilor săi să redefinească metoda de “fabricare”, asigurând principiul Protected Variations definit de GRASP.

Pentru facilitarea înţelegerii, să simplificăm intenţia şablonului şi să admitem că “clientul” poate fi nu numai clasa de bază. Astfel codul sursă ce reprezintă şablonul acestuia poate fi următorul:

public abstract class Product
{
    public Product()    {    }
}
public class ConcreteProduct : Product
{
   public ConcreteProduct() { }
}
 
public abstract class Creator
{
   /* Factory Method */
   public abstract Product CreateProduct();
}
 
public class ConcreteCreator: Creator
{
   public override Product CreateProduct()
   {
       return new ConcreteProduct();
   }
}

Deşi codul este simplificat, el oricum arată cam “mult” :) . De aici şi dorinţa firească a coder-ului să-şi “optimizeze” obiectul muncii.

Generics i-ar fi ca şi un colac de salvare, căci este cunoscut faptul că tehnicile Generics permit coduri elegante. Astfel fabrica prezentată mai sus poate fi redusă la:

public class GenericFactory where T : Product, new()
{
   public Product CreateProduct()
   {
      return new T();
   }
}

Bineînţeles,  nu mai este nevoie de subclasare. Mai mult decât atât, implementarea cu Generics se consideră a fi o soluţie mai eficientă, căci legarea se face în compile-time.

class Program
{
   static void Main(string[] args)
   {
      GenericFactory  genericFactory = new GenericFactory ();
      ConcreteCreator classicCreator = new ConcreteCreator();
 
      Product classicProduct = classicCreator.CreateProduct();
      Product genericProduct = genericFactory.CreateProduct();
   }
}

Pentru diverse limbaje de programare implementarea cu Generics puţin diferă, dar principiile rămîn a fi aceleaşi (Mihai Tataran(C#), jerron (C), Hugo Troche (Java)Gaston Milano (C#), etc.) Spre deosebire de mulţi Hugo Troche şi  Gaston Milano menţionează în articolele lor că implementarea şablonului Factory Method cu Generics, aduce şi anumite inconveniente, pentru care oferă şi soluţii, utilizînd tot şabloanele ;) !

Cu toate acestea am văzut deseori recomandarea de a recurge la fabricarea obiectelor cu Generics doar ca şi un remediu de evitare a subclasării. Dar în acest caz, aş dori să văd soluţia în care fiecărui tip de obiect creat de fabrică să-i fie aplicată o politică specifică de creare… Presupun că dacă se respectă open-closed principle, atunci obligator soluţia va include şi subclasarea ;) !

Mai există încă un detaliu, care scapă la mulţi. În descrierea motivaţiei şablonului  se recurge la exemplul unui framework în care există următoarea dilemă:

The framework must instantiate classes, but it only knows about abstract classes, which it cannot instantiate.

Şi aici soluţia recomandată este anume şablonul Factory Method în forma prezentată în GoF:

It encapsulates the knowledge of which Document (Product n.a.) subclass to create and moves this knowledge out of the framework.

Deci dacă noi substituim aici subclasarea cu un alt tip de relaţie, riscăm să pierdem esenţa şablonului Factory Method. Este adevărat că un cod “optimizat” poate fi mai eficient, dar deseori  la “optimizarea” codului se uită ideea ce este menţionată şi în Design Patterns: Elements of Reusable Object-Oriented Software (Gamma şi alţii):

Dynamic, highly parameterized software is harder to understand than more static software. There are also run-time inefficiencies, but the human inefficiencies are more important in the long run.

Astfel utilzînd anumite tehnici de programare să fim atenţi ce cîştigăm, dar şi ce riscăm să pierdem.

10 Comments »

  • Andronachi Vadim said:

    Citind acest articol mi-am adus aminte de fraza "Optimization hinders evolution" :) , si intradevar domn Ciorba, eu inteleg pentru ce este nevoie de optimizarea Sabloanelor in limbajele de nivelul Java sau C# ;) , care sint niste limbaje orientate mai mult spre disign.

  • Dumitru said:

    În general, "optimizarea" codului este necesară, dar mult depinde cînd ea este realizată şi cum. Dar nu prea înţeleg de ce le zici la Java şi C# "limbaje orientate mai mult spre design"? Ce ai vrut să spui?

  • Andronachi Vadim said:

    Am vrut sa spun sint deacord cu optimizarea codului pentru sabloanele de proiectare in limbajele care ofera multe posibilitati si flexibilitate :) . Dar in unele cazuri la optimizarea se perde conceptul sablonului.

    Cit despre Java si C# le spun "limbaje orientate mai mult spre design" deoarece eu le vad ca niste limbaje care folosesc mai mult reutilizarea disign-ului si programatorul este responsabil numai de implimentare.

  • Andronachi Vadim said:

    - Domnule Ciorba, ce ati vrut sa spuneti prin fraza "Dar în acest caz, aş dori să văd soluţia în care fiecărui tip de obiect creat de fabrică să-i fie aplicată o politică specifică de creare…" ?
    - In caz ca se evita subclasare, se perde posibilitatea de extindere a functionalitatilor
    fabricii ?

  • Dumitru said:

    Unui obiect este posibil de adăugat noi funcţionalităţi şi fără subclasarea directă şi acest lucru avem prezent în diverse şabloane de proiectare. În acelaşi timp cunoscutul principiu Open/Closed, indiferent de ce formă este vorba, implică subclasarea ca şi un mecanism "natural" pentru extensia funcţionalităţilor obiectelor.
    Iar în articol am vorbit despre condiţiile de creare specializată a fiecărui obiect în parte creat de fabrică, şi dacă dorim să evitîm subclasarea ce ne facem… revenim la switch sau la diverse metode (funcţii membre) de creare ;) .

  • Anonymous said:

    ahh, all this "Romanian" talk just reminds me how hard and sometimes weird, not to mention difficult to understand to someone, is to express technical "thoughts" in Romanian, sometimes… thought, we should still stick to our language, it's *our* language

    (P.S. sorry, I seem to have forgotten the language (: )

  • Dumitru said:

    Maybe you are right. Sometimes something is more simple to express directly in modern 'technical' English language. Therefore I don't want to be ridiculous in translating all things in Romanian.
    And in same time, I don't know where are you, but we must to love our language which is live because we speak and write using it.

  • Andronachi Vadim said:

    Dacă dorim să evităm subclasarea și cunoaștem bine mecanismul de funcționare a templaturilor (C++) (mecanism extins de generics) atunci putem defini politica de creare specifică fiecărei clase ;) , fără subclasare. Dar această soluție este o idiomă. :)

    Pentru o solutie mai generală cred că ar fi vorba de dependency inversion principle. ;)

  • Andronachi Vadim said:

    D-le profesor, legarea compile-time se face pentru mecanismul template din C++, iar pentru generic din Java sau C#, se face run-time. ;)
    Referinta: "Professional .NET 2.0 Generics", Tod Golding, pag.36 :)

    Dicutind cu prietenii mei sablonul, am adus vorba si de acest articol, problema este pusa foarte bine. Dupa 22 de saptamini perceperea acestei probleme este radical diferita. :|

  • diciorba said:

    :) Vadim, să începem cu aceea că acum câţiva ani prin legare se înţelegea "the act of associating a name or symbol with a machine address" [wiki, ” target=”_blank”>http://en.wikipedia.org/wiki/Binding_%28computer_... Acum legarea numai poate fi definită la fel de trivial, căci avem procese deloc simple: atît C#, cît şi Java utilizează o compilare în două faze. Astfel nu pot să spun că sunt greşit, poate m-am exprimat, în cel mai rău caz, superficial. Deoarece mă refeream în articol în special la tip uite ce scrie pe o pagina SUN [” target=”_blank”>http://java.sun.com/j2se/1.5.0/docs/guide/languag... despre Java Generics: "generic type information is present only at compile time, after which it is erased by the compiler"; şi pentru C# avem aproximativ acelaşi lucru: "While the roots of C# generics are C++ templates, C# takes generics to a new level by providing compile-time safety and support." [” target=”_blank”>http://msdn.microsoft.com/en-us/library/ms379564%... Ei, dar oricum POSIBIL ar trebui sa-mi revizuiesc exprimarea în articol pentru a evita pe viitor comentariile ;) .
    Da şi priveşte ce este scris şi pe IMB[Classworking toolkit: Reflecting generics, ” target=”_blank”>http://www.ibm.com/developerworks/java/library/j-...
    "Generics are implemented on the Java platform as a compile-time transformation. The compiler actually generates the same bytecode instructions as would be used for non-generic source code, inserting run-time casts to convert values to the appropriate type on each access. Despite this identical bytecode, the type parameter information is recorded in the class format using a new signature attribute. The JVM records this signature information when loading a class and makes it available at run time using reflection…"

    Succese! :)

  • Andronachi Vadim said:

    D-le Ciorbă aceasta întrebare ar necesita ”un articol” :) pentru ca să discutăm mai detaliat ;) , căci în contextul dat se pierde esența problemei, deoarece nu prea are importanță aici când și cum se face legătura între fabrică și produsul concret. Important aici este aceia că produsele concrete care au a fi create de fabrică, sânt supuse la aceeași politică de creare :( .

    PS: Observația despre legarea compile-time am făcut-o în linii generale.

Leave your response!

Add your comment below, or trackback from your own site. You can also subscribe to these comments via RSS.

Be nice. Keep it clean. Stay on topic. No spam.

You can use these tags:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">

This is a Gravatar-enabled weblog. To get your own globally-recognized-avatar, please register at Gravatar.