You are on page 1of 70

Catàleg de

patrons
PID_00276105

Jordi Pradel i Miquel


José Antonio Raya Martos

Temps mínim de dedicació recomanat: 6 hores


© FUOC • PID_00276105 Catàleg de patrons

Jordi Pradel i Miquel José Antonio Raya Martos

Enginyer en Informàtica per la per Enginyer en Informàtica per la per la


la Universitat Politècnica de Cata- Universitat Politècnica de Catalunya.
lunya. Soci fundador i enginyer de Soci fundador i Enginyer de Softwa-
programari a Agilogy. També exer- re a Agilogy. Professor Associat al
ceix com a professor d'enginyeria Departament de Llenguatges i Siste-
del programari al Departament de mes Informàtics de la UPC. Consul-
Llenguatges i Sistemes Informàtics tor dels Estudis d'Informàtica i Multi-
de la UPC i com a consultor dels Es- mèdia a la Universitat Oberta de Ca-
tudis d'Informàtica i Multimèdia a la talunya.
Universitat Oberta de Catalunya.

La revisió d'aquest recurs d'aprenentatge UOC ha estat coordinada


pel professor: Abel Gómez Llana

Segona edició: setembre 2020


© d’aquesta edició, Fundació Universitat Oberta de Catalunya (FUOC)
Av. Tibidabo, 39-43, 08035 Barcelona
© Jordi Pradel i Miquel, José Antonio Raya Martos
Producció: FUOC
Tots els drets reservats

Cap part d'aquesta publicació, incloent-hi el disseny general i la coberta, no pot ser copiada,
reproduïda, emmagatzemada o transmesa de cap manera ni per cap mitjà, tant si és elèctric com
mecànic, òptic, de gravació, de fotocòpia o per altres mètodes, sense l'autorització prèvia
per escrit del titular dels drets.
© FUOC • PID_00276105 Catàleg de patrons

Índex

Introducció.................................................................................................. 5

Objectius....................................................................................................... 6

1. Taula de referència............................................................................ 7

2. Principis de disseny........................................................................... 9
2.1. Baix acoblament (Low coupling) .................................................. 9
2.2. Alta cohesió (High cohesion) ........................................................ 9
2.3. Obert-tancat (OCP, Open-closed principle) .................................... 10
2.3.1. Llei de Demeter (Law of Demeter) .................................. 10
2.4. No-repetició (DRY, Don't repeat yourself) ..................................... 11
2.5. Substitució de Liskov (LSP, Liskov substitution principle) .............. 11
2.6. Segregació d'interfícies (ISP, Interface-segregation principle) .......... 11
2.7. Inversió de dependències (DIP, Dependency inversion principle) ... 12

3. Patrons d'anàlisi................................................................................. 13
3.1. Associació històrica ..................................................................... 13
3.2. Objecte compost (Composite) ...................................................... 17
3.3. Quantitat (Quantity) .................................................................... 18
3.4. Rang (Range) ................................................................................ 21

4. Patrons d'arquitectura..................................................................... 23
4.1. Arquitectura en capes (Layers) .................................................... 23
4.2. Injecció de dependències (Dependency injection) ......................... 25
4.3. Model, vista i controlador (MVC, Model-view-controller) ............. 28

5. Patrons d'assignació de responsabilitats..................................... 32


5.1. Controlador (Controller) .............................................................. 32
5.2. Expert (Expert) ............................................................................. 35
5.3. Fabricació pura (Pure fabrication) ................................................ 36
5.4. Creador (Creator) ......................................................................... 38

6. Patrons de disseny............................................................................. 40
6.1. Estat (State) .................................................................................. 40
6.2. Façana (Facade) ............................................................................ 43
6.3. Iterador (Iterator) ......................................................................... 46
6.4. Mètode factoria (Factory method) ................................................ 49
6.5. Mètode plantilla (Template method) ............................................ 51
6.6. Observador (Observer) .................................................................. 53
6.7. Ordre (Command) ........................................................................ 57
© FUOC • PID_00276105 Catàleg de patrons

6.8. Altres patrons de disseny ............................................................ 59


6.8.1. Adaptador (Adapter) ....................................................... 59
6.8.2. Decorador (Decorator) .................................................... 59
6.8.3. Estratègia (Strategy) ........................................................ 60
6.8.4. Instància única (Singleton) ............................................. 61
6.8.5. Objecte nul (Null object) ................................................ 62
6.8.6. Representant (Proxy) ...................................................... 63
6.8.7. Servidor abstracte (Abstract server) ................................. 64

Exercicis d'autoavaluació........................................................................ 67

Solucionari.................................................................................................. 68

Bibliografia................................................................................................. 70
© FUOC • PID_00276105 5 Catàleg de patrons

Introducció

Per a l'aplicació dels patrons a les diferents etapes del cicle de vida del desen-
volupament, cal disposar d'un catàleg que ens permeti cercar i fer referència
de manera ràpida als diferents patrons.

Aquest mòdul conté un catàleg organitzat segons les etapes del cicle de vida
en què es poden aplicar els diferents patrons (anàlisi, arquitectura, assignació
de responsabilitats i disseny). De cada etapa s'han escollit aquells patrons que
s'han considerat més representatius de la seva categoria i l'ús dels quals és més
generalitzat.

Segons l'estructura vista en el mòdul anterior, "Introducció als patrons", de


cada patró se n'explica la problemàtica que adreça, les solucions proposades i
també les conseqüències (beneficis i inconvenients) de la seva aplicació. Tam-
bé s'indica el catàleg on es va publicar originalment per tal de donar la possi-
bilitat d'ampliar la informació disponible tant sobre el propi patró com d'altres
patrons del mateix catàleg que hi puguin estar relacionats.

Aquest mòdul també es pot fer servir com a referència durant la realització de
les activitats de l'assignatura.
© FUOC • PID_00276105 6 Catàleg de patrons

Objectius

L'objectiu principal d'aquest mòdul és mostrar un conjunt de patrons i faci-


litar-ne la seva comprensió mitjançant exemples. També serveix com a refe-
rència on trobar els patrons que ens ajudin a millorar l'anàlisi i disseny dels
sistemes d'informació a desenvolupar. Els objectius concrets que ha d'assolir
l'estudiant amb aquest mòdul didàctic són els següents:

1. Conèixer una selecció de patrons aplicables a les diferents etapes del cicle
de vida del desenvolupament.

2. Conèixer els diferents patrons, així com les seves conseqüències.


© FUOC • PID_00276105 7 Catàleg de patrons

1. Taula de referència

A continuació s'inclou una taula de referència amb un resum de tots els pa-
trons del catàleg. De cada patró se n'indica el nom, el tipus, l'apartat del mò-
dul on s'explica en detall i un resum; també n'indiquem algunes referències
bibliogràfiques.

Nom Tipus Ref. Ap. Resum

Adaptador Disseny GOF, LAR, MAR 6.8.1 Permet utilitzar una classe fent servir un conjunt
d'operacions diferents al que oferia originalment.

Arquitectura en capes Arquitectura POSA, LAR 4.1 Organitza un sistema fent que cada element treballi a un
únic nivell d'abstracció.

Associació històrica Anàlisi FOW 3.1 Representa els valors d'una associació al llarg del temps.

Controlador Assig. resp. LAR 5.1 Assigna la responsabilitat de tractar esdeveniments a un


objecte controlador.

Creador Assig. resp. LAR 5.4 Assigna la responsabilitat de crear instàncies a un objecte.

Decorador Disseny GOF, MAR 6.8.2 Afegeix responsabilitats a un objecte sense modificar-ne la
classe.

Estat Disseny GOF, LAR, MAR 6.1 Permet a un objecte variar el seu comportament depenent
del seu estat.

Estratègia Disseny GOF, LAR, MAR 6.8.3 Permet triar entre una família d'algorismes.

Expert Assig. resp. LAR 5.2 Assigna una responsabilitat a l'objecte que té la informació
per a dur-la a terme.

Fabricació pura Assig. resp. LAR 5.3 Crea una nova classe per assignar-li una responsabilitat in-
dependent del domini.

Façana Disseny GOF, LAR, MAR 6.2 Redueix l'acoblament entre subsistemes.

Injecció de dependències Arquitectura FOWDI 4.2 Permet escollir i canviar la implementació dels serveis sen-
se haver de canviar el codi dels clients.

Instància única Disseny GOF, LAR, MAR 6.8.4 Assegura, per a una classe, que només n'hi pot haver una
instància.

Iterador Disseny GOF 6.3 Permet recórrer els elements d'una col·lecció.

Mètode factoria Disseny GOF, LAR 6.4 Crea instàncies d'una classe sense conèixer la subclasse
exacta.

Mètode plantilla Disseny GOF, LAR, MAR 6.5 Implementa diversos algorismes semblants reutilitzant la
part comuna.

MVC Arquitectura POSA 4.3 Organitza les responsabilitats d'interacció amb l'usuari.

Objecte compost Anàlisi GOF, LAR, MAR 3.2 Permet tractar una col·lecció d'objectes i els seus elements
de la mateixa manera.

Objecte nul Disseny MAR 6.8.5 Tracta el valor nul com un objecte.

Observador Disseny GOF, LAR, MAR 6.6 Notifica els canvis en l'estat d'un objecte a altres objectes.
© FUOC • PID_00276105 8 Catàleg de patrons

Nom Tipus Ref. Ap. Resum

Ordre Disseny GOF, LAR, MAR 6.7 Tracta una invocació a una operació com un objecte.

Quantitat Anàlisi FOW 3.3 Representa una mesura amb la seva unitat.

Rang Anàlisi FOW 3.3 Representa un rang de valors.

Representant Disseny GOF, LAR, MAR 6.8.6 Controla l'accés a un objecte.

Servidor abstracte Disseny MAR 6.8.7 Redueix l'acoblament entre clients i servidors.
© FUOC • PID_00276105 9 Catàleg de patrons

2. Principis de disseny

Per a poder avaluar la qualitat d'un disseny és necessari establir uns certs prin-
cipis que aquest ha de complir. D'aquesta manera, direm que un disseny és de
qualitat en la mesura que s'ajusti a aquests principis de disseny.

La finalitat de l'aplicació dels patrons de disseny és millorar la qualitat del


sistema desenvolupat aprofitant l'experiència prèvia. Per tant, a l'hora de de-
cidir sobre l'aplicació dels patrons ho haurem de fer a la llum dels principis
esmentats abans. És per això que, en aquest mòdul, s'inclou un petit catàleg
de principis de disseny.

A continuació es dóna un seguit de principis de disseny àmpliament acceptats


que, en alguns casos, ens poden ajudar en altres etapes del cicle de vida del
desenvolupament com ara l'anàlisi.

2.1. Baix acoblament (Low coupling)

Hem de minimitzar l'acoblament.

L'acoblament mesura la dependència entre elements (com classes, paquets,


etc.), típicament com a resultat de la col·laboració entre els elements per a
proporcionar un servei.

Quan una classe té un acoblament alt respecte a altres classes ens trobem amb
el següent:

• Un canvi a les classes relacionades pot provocar un canvi a la nostra classe.

• És difícil entendre la nostra classe de manera aïllada.

• És difícil reutilitzar la nostra classe, ja que el seu ús requereix la presència


de les classes relacionades.

2.2. Alta cohesió (High cohesion)

Hem de maximitzar la cohesió.


© FUOC • PID_00276105 10 Catàleg de patrons

La cohesió mesura el grau de relació entre les diferents responsabilitats d'una


classe. Com més relacionades entre elles estiguin aquestes responsabilitats,
més alta serà la cohesió de la classe.

Una classe amb cohesió baixa té els problemes següents:

• És difícil d'entendre.
• És difícil de reutilitzar.
• És difícil de mantenir.
• És fràgil respecte als canvis que es produeixin al sistema.

Una versió més restrictiva d'aquest principi és el conegut com a principi de Bibliografia
la responsabilitat única (SRP, Single-responsibility principle): "Una classe només complementària

hauria de tenir un motiu per a canviar". Podeu trobar més informa-


ció sobre el principi SRP a
[MAR].
2.3. Obert-tancat (OCP, Open-closed principle)

Bibliografia
Una entitat de programari (classe, mòdul, funció, etc.) hauria d'estar complementària

oberta a l'extensió però tancada a la modificació. Podeu trobar més informació


sobre el principi obert-tancat
a [MAR] i a [MEY]. També a
[LAR] sota el nom de Variaci-
En termes de disseny, aquest principi ens indica que, per a afegir noves res- ons protegides.
ponsabilitats al nostre sistema, ho hem de poder fer afegint noves classes (ex-
tensió), però sense modificar les que hi havia.

Una manera de complir aquest principi és crear abstraccions al voltant dels Principi de Variacions
aspectes que preveiem que han de canviar de manera que es pugui crear una protegides

interfície estable respecte als canvis. Mitjançant aquesta solució, el nostre sis- El nostre disseny ha de mini-
tema estarà obert a l'extensió (afegint noves implementacions de les interfíci- mitzar l'impacte en el sistema
actual dels canvis que es pro-
es definides) però tancat a la modificació (no hem de modificar les interfícies dueixin en el futur i que ja po-
dem anticipar.
definides).

2.3.1. Llei de Demeter (Law of Demeter)

(1)
La llei de Demeter, de vegades resumida com a "No parlis amb desconeguts1", En anglès, "Don't talk to stran-
gers".
és un heurístic que ens ajuda a complir el principi Obert-tancat:

Una operació d'un objecte només hauria de fer servir:

• Les operacions del mateix objecte.


• Els objectes que tingui associats el mateix objecte (o que en siguin
atributs).
• Els objectes que rep com a paràmetre l'operació.
• Els objectes que creï l'operació.
© FUOC • PID_00276105 11 Catàleg de patrons

Aquesta llei ens ajuda a reduir l'acoblament respecte a una estructura de clas- Llei de Demeter
ses concreta. A més, potencia l'encapsulació, ja que, per a accedir als objec-
De vegades es fa servir el nom
tes associats a un objecte O, ho haurem de fer per mitjà de les operacions de de Llei de Demeter com a prin-
l'objecte O, de manera que ens assegurem que aquest s'assabentarà de l'accés cipi de disseny en substitució
del principi Obert-tancat.
o manipulació.

2.4. No-repetició (DRY, Don't repeat yourself)

Bibliografia
Cada peça de coneixement ha de tenir una única i inambigua represen- complementària
tació en el sistema.
Podeu trobar més informació
sobre el principi de No-repe-
tició a [HUNT].
Aquest principi ens indica que hem d'evitar, sempre que puguem, la duplicació
d'informació i de responsabilitats. Si ho aconseguim, el nostre sistema serà
més senzill i més fàcil de mantenir, ja que, davant d'un canvi o error, podrem
identificar fàcilment quin és el component afectat.

2.5. Substitució de Liskov (LSP, Liskov substitution principle)

Bibliografia
Les instàncies d'una subclasse C han de ser substituïbles per instàncies complementària
de les superclasses de C.
Podeu trobar més informació
sobre el principi de Substitu-
ció de Liskov a [MAR].
Aquest principi ens indica que una bona jerarquia d'herència ha de respectar
el comportament de les superclasses. Una classe o programa que utilitzés ins-
tàncies d'una superclasse S hauria de poder fer servir qualsevol instància d'una
subclasse de S sense que el seu comportament es vegi afectat negativament.

2.6. Segregació d'interfícies (ISP, Interface-segregation principle)

Bibliografia
Els clients no haurien de dependre d'operacions que no fan servir. complementària

Podeu trobar més informació


sobre el principi de segrega-
Una classe pot oferir moltes operacions. En aquests casos, és normal que ens ció d'intefícies a [MAR].
trobem que algunes de les classes client només fan servir un subconjunt de les
operacions. Aquest principi ens diu que, en aquests casos, hem de separar la
interfície de la classe en subconjunts d'operacions per tal d'evitar l'acoblament
de les classes client cap a operacions que no fan servir.
© FUOC • PID_00276105 12 Catàleg de patrons

2.7. Inversió de dependències (DIP, Dependency inversion


principle)

Bibliografia
Els mòduls o classes d'alt nivell no haurien de dependre dels de baix complementària
nivell sinó d'una abstracció.
Podeu trobar més informació
sobre el principi d'Inversió de
Les abstraccions no haurien de dependre dels detalls. Els detalls haurien dependències a [MAR].
de dependre de les abstraccions.

Per a poder maximitzar la reutilització de les nostres classes hem d'evitar


l'acoblament respecte a les classes de més baix nivell. Això ens permetrà limi-
tar l'impacte d'un canvi al nivell d'abstracció en què es produeixi. Per exem-
ple, si canviem de servidor de base de dades, la capa de domini no s'hauria
de veure afectada.
© FUOC • PID_00276105 13 Catàleg de patrons

3. Patrons d'anàlisi

Els patrons d'anàlisi són aquells patrons que documenten solucions aplicables Vegeu també
durant la realització del diagrama estàtic d'anàlisi per a resoldre els problemes
Podeu trobar més informació
que hi sorgeixen: ens proporcionen maneres provades de representar concep- sobre els patrons d'anàlisi en
tes generals del món real en un diagrama estàtic de l'anàlisi. l'apartat 2.1 del mòdul "Intro-
ducció als patrons".

3.1. Associació històrica

Context

Una classe A participa en una associació de grau N.

Problema

Volem poder recuperar els valors que l'associació ha anat prenent al llarg
del temps.

En concret:

• Cal poder conèixer el valor de l'associació en un moment determinat.

• Cal poder consultar la llista de valors de l'associació en un període de


temps.

Per exemple, suposem que el sistema que estem desenvolupant necessita em-
magatzemar informació sobre els projectes en què ha participat un empleat,
per la qual cosa ens trobem amb el diagrama estàtic de l'anàlisi següent:

Aquesta solució, però, només ens serveix per a saber, en el moment actual, en
quins projectes participa l'empleat. Com podem saber en quins projectes va
participar l'empleat l'any passat? D'aquesta informació sobre l'estat del sistema
en el passat en diem "informació històrica" i és una situació força habitual en
el desenvolupament de sistemes d'informació.
© FUOC • PID_00276105 14 Catàleg de patrons

Solució

Afegir una dimensió a l'associació (convertint l'associació n-ària en n+1-


ària) que representi el temps.

Per a una associació com la següent:

la solució seria aquesta:

Si els valors que pren l'associació tenen un cert període de validesa, el moment
final d'aquest període s'indicarà com a associació de la classe associativa:

Tornant al cas dels projectes dels empleats, la solució seria la següent:


© FUOC • PID_00276105 15 Catàleg de patrons

Així, doncs, ara el nostre sistema sap, per a cada parella Empleat-Projecte, la
data en què es va produir aquesta assignació i la data en què l'assignació va
finalitzar. Per a saber quina és l'assignació actual, haurem de mirar quina data
és la més recent o també podríem considerar que és aquella que encara no
tingui dataFi.

A l'hora de definir les noves cardinalitats, cal anar amb compte. En aquest cas,
la cardinalitat al costat d'Empleat ens indicarà quantes instàncies d'Empleat
poden estar associades a un mateix Projecte amb la mateixa Data, però no ens
indica res respecte a altres moments en el temps (per exemple, un Empleat pot
estar assignat més d'una vegada a un Projecte sempre que la data d'assignació
sigui diferent).

Conseqüències

• Permet reflectir la informació històrica correctament.

• El resultat és més complex, ja que s'ha augmentat el grau de l'associació.

• No es pot traslladar la multiplicitat de l'associació original a l'associació


històrica.

• El diagrama no indica que els períodes de temps no es poden encavalcar.

Variacions

Si en lloc d'una associació el que tenim és un atribut del qual volem conèixer
l'historial de valors, cal fer una transformació prèvia: representem l'atribut com
una associació binària entre la classe i el tipus de dades que correspongui i
després apliquem el patró normalment.

Per exemple, suposem que el sistema que estem desenvolupant té Empleats el


sou dels quals es vol conèixer històricament:
© FUOC • PID_00276105 16 Catàleg de patrons

Per a aplicar aquest patró, primer ens cal convertir l'atribut en una associació
binària:

Un cop feta la transformació, apliquem el patró de la manera que hem vist


anteriorment:

Alguns detalls sobre l'exemple d'aplicació del patró:

• En aquest cas hem suposat que un mateix empleat no pot canviar de sou
més d'una vegada en una mateixa data (restricció que es reflecteix al dia-
grama per la cardinalitat 0..1 al costat Diners). Si no fos així, la cardinalitat
al costat Diners seria *.

• El diagrama no reflecteix, però, que un empleat no pot tenir dos sous en


una mateixa data (és a dir, que els períodes de temps pels quals es defineix
cada sou no es poden encavalcar).

Variacions

A [FOW] podem trobar tot un conjunt de patrons d'anàlisi dedicats a aspectes


més concrets relacionats amb la problemàtica de representar el temps.
© FUOC • PID_00276105 17 Catàleg de patrons

3.2. Objecte compost (Composite)

Context

Hi ha una associació que agrupa elements en col·leccions.

Problema

Volem tractar les col·leccions d'elements i els elements de manera uni-


forme.

Per exemple, suposem que el sistema d'informació que estem desenvolupant Patró d'anàlisi o de
fa servir arxius per a emmagatzemar informació. D'un usuari concret en sabem disseny?

els arxius que li pertanyen, i també el nom i els permisos de cada arxiu. Aquest patró habitualment,
es considera un patró de dis-
seny. L'hem inclòs com a patró
d'anàlisi perquè també pot ser
molt útil en aquesta etapa del
cicle de vida del desenvolupa-
ment de programari.

Ara, però, volem afegir la possibilitat d'agrupar els arxius en carpetes. Sobre
aquestes carpetes volem aplicar el mateix tractament que sobre els arxius (és
a dir, també tindran un nom i uns permisos). Com ho podem fer?

Solució

Crear, mitjançant una generalització, una superclasse comuna als ele-


ments i a les col·leccions i fer que la resta del sistema no conegui la
subclasse concreta amb què està associat.

En el nostre exemple, el resultat d'aplicar el patró seria el següent:


© FUOC • PID_00276105 18 Catàleg de patrons

Conseqüències

• Elements i col·leccions són d'un mateix tipus, la qual cosa soluciona el


problema del tractament uniforme.

• Solució més complexa: hem fet una generalització i, per tant, s'ha afegit
una classe nova.

3.3. Quantitat (Quantity)

Context

Es vol enregistrar una quantitat.

Problema

La representació d'una quantitat mitjançant un valor numèric és poc


acurada, pot portar a confusió o no és viable quan poden intervenir
diferents unitats de mesura.

En concret, podem voler:

• que la unitat de mesura sigui explícita.


• fer servir diferents unitats de mesura.
• poder fer la conversió entre unitats de manera senzilla.

Per exemple, suposem que el sistema d'informació que estem desenvolupant


ha de gestionar informació sobre persones, concretament l'alçada, per la qual
cosa afegim un atribut de tipus enter que conté l'alçada de la persona:
© FUOC • PID_00276105 19 Catàleg de patrons

Aquesta solució, però, no és suficient, ja que no ens indica en quina unitat


està expressada l'alçada. Està en metres? En centímetres? En mil·límetres? Una
possible solució pot ser indicar aquesta unitat de mesura com a part del nom
de l'atribut. Així, per exemple, podríem canviar el nom de l'atribut per alça-
daEnCm per indicar que l'alçada s'indica en centímetres. Però aleshores hi ha
encara alguns problemes que no es poden resoldre:

• La unitat de mesura és implícita en el nom de l'atribut i, per tant, queda


poc clara pel que fa a l'anàlisi.

• Es força sempre a fer servir la unitat de mesura indicada a l'atribut.

• Si la mesura de què es disposa està en una altra unitat de magnitud caldrà


fer la conversió.

• L'anàlisi no reflecteix com es pot fer aquesta conversió.

• No es poden enregistrar mesures diferents en unitats diferents.

Quina solució es pot aplicar en una situació com aquesta que ens permeti
solucionar tots els problemes que hem esmentat abans?

Solució

Modelar la mesura com un tipus de dada més complex format pels


camps valor i unitat.

Podem veure, doncs, que d'aquesta manera podem solucionar els problemes
que hem esmentat abans pel que fa a l'anàlisi i, més endavant, durant l'etapa
del disseny, podrem solucionar el problema de les conversions entre unitats
sense grans dificultats. En el nostre cas, el resultat seria el següent:
© FUOC • PID_00276105 20 Catàleg de patrons

Hem de tenir en compte que, si dues persones fan la mateixa alçada (per exem-
ple, 1,70 cm), cada una tindrà associada una instància diferent d'Alçada amb
els mateixos valors als seus atributs. D'aquesta manera, si es modifica l'alçada
d'una de les dues persones, no es modificarà la de l'altra. És per això que tenim
un 1 a la cardinalitat del costat de Persona.

Conseqüències

• Tenim més classes, la qual cosa provoca que el model guanyi expressivitat
a canvi de perdre simplicitat.

• Podem tractar la quantitat com un tipus abstracte de dades, afegint el com-


portament que creiem necessari (conversions, comparacions, etc.) aug-
mentant, d'aquesta manera, la cohesió global del sistema modelat.

• La unitat de mesura és explícita en el diagrama estàtic de l'anàlisi.

• Es poden fer servir diverses unitats de mesura.

Variacions

Altres patrons d'anàlisi documentats a [FOW] (vegeu bibliografia) i que no


veurem amb detall es relacionen amb el patró Quantitat, la qual cosa permet
un model d'anàlisi molt més complet que prevegi, entre d'altres coses:

• Conversió entre unitats d'una mateixa magnitud (com, per exemple, les
ràtios de conversió de centímetres a polzades).

• Unitats compostes (com, per exemple, el m/s2 per a mesurar acceleraci-


ons).

• Mesures i observacions: tenir coneixement del moment i el mecanisme pel


qual es va fer una observació.

Aquests altres patrons d'anàlisi, però, ja són molt més específics per a dominis
d'àmbits científics i, especialment, mèdics.
© FUOC • PID_00276105 21 Catàleg de patrons

3.4. Rang (Range)

Context

Volem representar un rang de valors.

Problema

La nostra anàlisi hauria de reflectir la semàntica pròpia d'un rang, com


ara saber si dos rangs s'encavalquen o si un valor és dins d'un rang.

Per exemple, suposem que el sistema d'informació que estem desenvolupant


permet publicar notícies a Internet. Aquestes notícies tindran un període de
validesa. Una solució podria ser afegir aquestes dates a la classe Notícia:

El problema d'aquesta solució és que estem afegint a la classe Notícia totes les
responsabilitats associades a la gestió del rang de dates (comparar-les, gestio-
nar-les, etc.). A més a més, si tenim més classes amb rangs de dates, la solució
es complica, ja que el coneixement sobre el funcionament dels intervals de
dates es troba dispers.

Solució

Crear una classe que representa un rang de valors i que s'encarregarà de


contenir la semàntica del rang.
© FUOC • PID_00276105 22 Catàleg de patrons

Aquesta classe tindrà un valor d'inici i un valor de final. Considerarem que una Exemple
instància del rang conté tots els valors situats entre el valor d'inici i el valor de
Opcionalment podríem fer ser-
final i que dues instàncies s'encavalquen si contenen algun valor comú. vir una classe parametritzada
per a representar el rang.

En el cas de les notícies i els anuncis, la solució seria la següent:

Conseqüències

• Només es pot aplicar amb tipus que suportin la comparació (és a dir, que
tinguin definit un ordre per defecte), tot i que es podria fer una versió més
sofisticada en la qual es pogués indicar el criteri d'ordenació.

• Com tractem els rangs oberts (per exemple, més gran que sis)? Podem fer
servir un valor especial (el valor nul, o el màxim representable, etc.) com
a indicador de la manca de límit i encapsular aquest fet dins de la classe
de manera que els clients no es vegin afectats.

• A més de les operacions que indiquen que un valor pertany a un


rang, podem afegir operacions que comparin rangs, que ens diguin si
s'encavalquen, etc.

Variacions

En lloc de tenir un atribut principi i un atribut final, podem tenir un atribut


principi i una longitud.
© FUOC • PID_00276105 23 Catàleg de patrons

4. Patrons d'arquitectura

Els patrons d'arquitectura són aquells que s'apliquen en la definició de Vegeu també
l'arquitectura del programari i que, per tant, resolen problemes que afectaran
Podeu trobar més infor-
el conjunt del disseny del sistema. mació sobre els patrons
d'arquitectura en l'apartat 2.2
del mòdul "Introducció als pa-
4.1. Arquitectura en capes (Layers) trons".

Context

Volem estructurar el nostre sistema de manera que cada component


d'aquest treballi a un cert nivell d'abstracció.

Problema

• El nostre sistema ha de treballar a diferents nivells d'abstracció.

• Els components d'alt nivell no poden utilitzar directament serveis amb un


nivell d'abstracció molt més baix perquè volem minimitzar la complexitat
del sistema resultant. Per exemple, no volem que les classes d'interfície
gràfica accedeixin directament a la base de dades.

• Es vol dissenyar una arquitectura que eviti que els canvis al codi d'una part
es propaguin per tot el sistema reduint l'acoblament entre parts.

• Es vol evitar que la lògica de l'aplicació i la interfície d'usuari d'aquesta


estiguin excessivament acoblades per a poder reutilitzar, en cas que sigui
necessari, la mateixa lògica amb una interfície diferent.

• Es vol evitar que la lògica de negoci i els serveis tècnics de l'aplicació esti-
guin excessivament acoblats per a poder reutilitzar, distribuir o substituir
fàcilment un servei tècnic per una implementació diferent.

Per exemple, el sistema que estem desenvolupant gestiona informació sobre


les nòmines dels treballadors d'una empresa. El sistema treballarà a diferents
nivells d'abstracció:

• Elements d'interfície gràfica (botons, menús, etc.).


• Operacions d'entrada/sortida (escriptures en disc, missatges de xarxa, etc.).
• Consultes i actualitzacions a una base de dades.
© FUOC • PID_00276105 24 Catàleg de patrons

• Classes de programari que representen conceptes del domini (Empleats,


Nòmines, etc.).

Solució

Organitzar l'estructura lògica a gran escala del sistema en capes separa-


des de responsabilitats diferents de tal manera que les capes més baixes
són serveis generals de baix nivell i les més altes són més específiques
de l'aplicació.

La col·laboració i l'acoblament és des de les capes més altes cap a les més baixes.
Una capa de nivell N només estarà acoblada a la capa de nivell N-1. D'aquesta
manera, cada classe tindrà una visió del sistema més coherent i més senzilla.

Una aplicació molt típica d'aquest patró és l'arquitectura en tres capes:

• Presentació: el seu nivell d'abstracció és el dels elements d'interfície gràfica


d'usuari (pantalles, botons, etc.) i les seves responsabilitats les de recollir
les entrades dels usuaris, cridar la capa de domini i mostrar els resultats
als usuaris.

• Domini: el seu nivell d'abstracció és el dels conceptes del sistema i les se-
ves responsabilitats les d'implementar les regles de negoci. Les classes de
la capa de domini veuen el sistema com un conjunt de classes d'entitat
(Nòmina, Empleat, etc.) que contenen la informació que gestiona el siste-
ma i una sèrie de casos d'ús o operacions de sistema que en determinen
la funcionalitat.
© FUOC • PID_00276105 25 Catàleg de patrons

• Serveis tècnics: el seu nivell d'abstracció és el de la plataforma (sistema


operatiu, fitxers, etc.) i les seves responsabilitats les d'aïllar en la mesura
del possible la capa de domini dels elements de la plataforma.

En el nostre exemple, per a calcular una nòmina, les diferents capes intervin-
dran de la manera següent:

1) Capa de presentació: només ens preocuparem de saber que s'ha polsat un


cert botó i que, com a resultat d'això, s'ha de mostrar una certa pantalla amb
unes dades que rebrem de la capa de domini.

2) Capa de domini: rebrem una crida a una operació d'una classe que ha de
saber com es fa el càlcul a partir dels seus paràmetres. No ens hem de preocupar
de quins botons s'han polsat ni de quines pantalles es mostren, com tampoc
d'en quins fitxers físics rau la informació que hem de fer servir.

3) Capa de serveis tècnics: la visió que aquestes classes tenen del sistema cons-
ta d'una base de dades, una sèrie de fitxers auxiliars i, potser, un servidor re-
mot al qual enviem dades a través de la xarxa. No saben res de nòmines, ni
empleats, ni interfícies gràfiques d'usuari i només han d'atendre peticions de
lectura/escriptura en disc, consultes a la base de dades, etc.

Conseqüències

• Redueix la complexitat del sistema, ja que cada component treballa a un


únic nivell d'abstracció clarament definit per la capa que el conté.
• Permet l'intercanvi d'una capa sencera per una altra en un sistema amb
un impacte mínim.
• Redueix l'eficiència a causa de l'augment potencial de nivells d'indirecció
entre els objectes a l'hora d'executar una tasca. A més, es pot donar el cas
que una capa faci feina que les capes superiors no necessitin.
• Facilita la reutilització de capes senceres gràcies a la definició d'interfícies
clares entre elles. Aquesta reutilització pot ser, per tant, d'un volum més
gran de classes que no pas la reutilització de components individuals.

Variacions

El nombre i la funció de les capes pot variar d'un sistema a un altre i, de fet,
són dues de les variables a tenir en compte en el moment d'aplicar el patró.
Per exemple, podem fer servir dues capes com als sistemes client/servidor.

4.2. Injecció de dependències (Dependency injection)

Context
© FUOC • PID_00276105 26 Catàleg de patrons

En el nostre sistema tenim definits una sèrie de serveis (com ara l'enviament
de missatges de correu electrònic o un servei de persistència) pels quals tenim
(o podem tenir en un futur) diverses implementacions.

Problema

Volem escollir i canviar la implementació de cada servei sense haver


de modificar el codi de les nostres classes, que no dependran de cap
implementació concreta sinó de la interfície del servei.

Suposem que estem desenvolupant un sistema que ha d'accedir a una base de


dades. Algunes de les classes necessitaran fer servir un component d'accés a
base de dades per a dur a terme les seves funcions:

En aquest cas, tenim dues implementacions de la mateixa interfície. Es po-


dria donar el cas que, en l'entorn de desenvolupament, treballéssim amb una
base de dades en memòria per accelerar l'execució (IMDBImpl) i, en l'entorn
d'explotació, amb un servidor de bases de dades (MySQLImpl). En aquest cas,
necessitem un mecanisme que ens permeti escollir la implementació que fa-
rem servir de manera transparent a la classe GestorPersonaBD.

Solució

Oferir operacions per a poder injectar a les classes usuàries d'un servei
la implementació d'aquest. Aquesta injecció la durà a terme una tercera
classe que, típicament, formarà part d'un bastiment especialitzat.
© FUOC • PID_00276105 27 Catàleg de patrons

Aquesta classe o bastiment ens ha de permetre configurar quina implementa-


ció volem fer servir de manera transparent a les nostres classes (per exemple,
mitjançant un fitxer de configuració).

En el nostre cas, aquest seria el resultat d'aplicar la injecció de dependències


a la classe GestorPersonaBD:

public void setBD(BD implementacio) {


this.bd = implementacio;
}
public void load(String id) {
bd.executaConsulta("SELECT ...");
}

Conseqüències

• Les dependències són explícites, ja que apareixen com a operacions o atri-


buts de la classe client. Per exemple, setBD a la classe GestorPersonaBD fa
explícita la dependència de GestorPersonaBD cap a BD.

• Les dependències es barregen amb les operacions o atributs propis de la


classe. En l'exemple anterior, no queda clar si BD és una propietat de la
classe PersonaBD o una dependència.

• Es facilita la reutilització en no dependre de cap implementació concreta


d'injecció de dependències (si volem distribuir les nostres classes, no caldrà
distribuir el servei d'injecció de dependències).

• És més fàcil provar la nostra classe de manera aïllada, ja que no necessitem Bibliografia
modificar el mecanisme de resolució de dependències sinó, simplement, complementària

configurar la instància sobre la qual executarem les proves unitàries amb Podeu trobar més informa-
les implementacions que més ens convinguin per a fer la prova. Per exem- ció sobre proves unitàries
de classes de programari a
ple, per a provar una instància de GestorPersonaBD només li hem de pro- [BECK].
porcionar una implementació de BD cridant l'operació setBD.

• És fàcil identificar cada servei, ja que tots tenen un nom (aquest nom serà
determinat pel nom de l'operació que fem servir per a injectar la imple-
mentació del servei).

• Hem de garantir que tota instància estigui configurada correctament en el


moment de fer-la servir.

Variacions
© FUOC • PID_00276105 28 Catàleg de patrons

• Injecció per constructor: les dependències són paràmetres del constructor Implementacions
del nostre objecte. Això ens assegura que és impossible tenir una instància
Alguns bastiments Java que
que no estigui configurada correctament, ja que es configura en el mateix faciliten l'aplicació d'aquest
moment de la seva creació. Aquesta solució és problemàtica en els llen- patró són PicoContainer
(www.picocontainer.org)
guatges de programació en què els paràmetres no tenen nom si en tenim i Spring Framework
(www.springframework.org).
més d'un del mateix tipus, ja que no tenim manera de diferenciar els dos
serveis.

• Injecció per interfícies: es crea una interfície que la nostra classe ha


d'implementar. A la interfície es defineixen les operacions que es faran ser-
vir per a injectar les dependències.

4.3. Model, vista i controlador (MVC, Model-view-controller)

Context

Estem desenvolupant un sistema amb interfície gràfica d'usuari.

Problema

Volem desacoblar la interfície gràfica del nostre sistema de la resta del


sistema.

En una arquitectura en capes, volem definir l'arquitectura de la capa de pre-


sentació.

A l'hora de dissenyar les classes de la interfície gràfica d'usuari de la nostra


aplicació, hem de tenir en compte que la interacció amb l'usuari és una tas-
ca molt complexa i que si no fem una bona separació de responsabilitats el
manteniment d'aquesta part de l'aplicació pot esdevenir fàcilment molt cos-
tós. D'altra banda, la interfície d'usuari d'un sistema és, probablement, la part
més propensa a canvis.

Prenem com a exemple un formulari d'edició de les dades d'un usuari que
permet modificar-ne el nom. Un cop canviat el nom, l'usuari ha de polsar el
botó Acceptar.

Solució

Dividir el sistema en tres tipus de components: models, vistes i contro-


ladors.
© FUOC • PID_00276105 29 Catàleg de patrons

(2)
Els models encapsulen l'estat del sistema. El seu nivell d'abstracció és el do- Comportament del sistema en
2 termes del domini.
mini del problema. És on s'implementa l'anomenada lògica de negoci . També
notifiquen a les vistes els canvis en l'estat del sistema.

Les vistes presenten les dades als usuaris i en recullen les interaccions (per
exemple, s'ha polsat un botó d'una pantalla) per enviar-les als controladors.
Demanen l'estat del sistema al model.

Els controladors estableixen la correspondència entre les accions de l'usuari


i els esdeveniments del sistema. També decideixen quines vistes es mostren
als usuaris.

En el nostre exemple, quan l'usuari polsi el botó Acceptar, la vista cridarà el


controlador; aquest traduirà l'esdeveniment a una operació de sistema sobre el
model (setNom d'un usuari). Com a resultat del canvi en el seu estat, l'usuari
notificarà les vistes que s'han d'actualitzar.
© FUOC • PID_00276105 30 Catàleg de patrons

En una arquitectura en capes, el model estarà format pel conjunt de classes de


la capa de domini. Tanmateix, com que la capa de domini no pot estar acobla-
da a la capa de presentació, els models no actualitzaran les vistes directament.
Una manera de solucionar aquest problema és que els controladors, que co-
neixen el model i hi provoquen els canvis, siguin els encarregats de notificar
les vistes. D'aquesta manera, els controladors, després de modificar el model,
avisen les vistes per tal que s'actualitzin.

En una arquitectura en capes, per tant, el nostre exemple funcionaria de la


manera següent:

Conseqüències

• La cohesió de les classes és més alta perquè totes les seves responsabilitats
estan relacionades.

• Podem suportar més d'una tecnologia de visualització (per exemple, inter-


fícies web i interfícies "rich client").

• Els desenvolupadors es poden especialitzar en un domini concret, sia el


domini de l'aplicació o el domini del desenvolupament d'interfícies gràfi-
ques.

• Podem actualitzar la lògica de visualització independentment de la lògica


de negoci (o actualitzar la lògica de negoci sense haver de modificar la
lògica de visualització).
© FUOC • PID_00276105 31 Catàleg de patrons

• Si volem que les vistes s'actualitzin automàticament, cada vegada que hi


hagi un canvi en el model haurem d'establir un mecanisme de notificació
d'aquests canvis. Això ens pot crear fàcilment una dependència de la capa
de domini a la capa de presentació, cosa que no és desitjable si estem se-
guint una arquitectura en capes.
© FUOC • PID_00276105 32 Catàleg de patrons

5. Patrons d'assignació de responsabilitats

Els patrons d'assignació de responsabilitats són un tipus especial de patró de Vegeu també
disseny que no s'apliquen per a resoldre un problema concret de disseny sinó
Podeu trobar més informa-
que s'apliquen, de manera general, per a repartir les responsabilitats entre les ció sobre els patrons GRASP a
diferents classes del diagrama de classes. Així doncs, la seva aplicació acostuma [LAR] o a l'apartat 2.3 del mò-
dul "Introducció als patrons".
a ser prèvia a l'aplicació de la resta de patrons de disseny.

Per a documentar aquests patrons modificarem lleugerament l'estructura de Vegeu també


l'explicació, ja que tots resolen un mateix problema: com assignar les respon-
Consulteu el catàleg de prin-
sabilitats a les nostres classes per tal que el nostre disseny respecti els principis cipis de disseny a l'apartat 2
del disseny de qualitat. d'aquest mòdul didàctic.

5.1. Controlador (Controller)

Context

Es produeixen esdeveniments i cal decidir qui serà el responsable de gestio- Exemple


nar-los.
Cal no confondre aquest patró
amb el controlador del patró
En una arquitectura en capes, quan la capa de presentació detecta que s'ha Model-Vista-Controlador vist
a l'apartat 4.3 "Model, vista i
produït un esdeveniment que cal notificar a la capa de domini (per exemple, controlador", d'aquest mòdul.

l'usuari vol donar d'alta un client), necessitem determinar quina classe de la


capa de domini rebrà aquest esdeveniment per a tractar-lo.

Solució

Crear una classe que anomenarem Controlador i assignar-li la responsa-


bilitat de gestionar l'esdeveniment.

Hi ha diversos tipus de Controlador:

• Controlador de façana: representa el sistema global o un subsistema. Con-


té tantes operacions com esdeveniments es poden produir al sistema o
subsistema.

• Controlador de cas d'ús: representa el cas d'ús en el qual es produeix


l'esdeveniment. Conté tantes operacions com esdeveniments es poden
produir al llarg de l'execució del cas d'ús.

• Controlador de sessió: representa una sessió d'un actor. Una sessió és una
instància d'una conversa entre l'actor i el sistema i pot tenir qualsevol du-
© FUOC • PID_00276105 33 Catàleg de patrons

ració. S'hi poden produir esdeveniments corresponents a tots els casos d'ús
en què l'actor pugui intervenir.

• Controlador de transacció: representa un únic esdeveniment de sistema.


Els paràmetres de l'esdeveniment seran atributs de la classe. Només conté
una operació que anomenarem executar.

Exemple

En una aplicació web, per exemple, la capa de presentació s'encarregarà


d'interactuar amb els usuaris per mitjà del seu navegador. El navegador in-
teractuarà amb el sistema enviant peticions HTTP que el sistema respondrà
amb l'HTML que l'usuari veurà. Serà la capa de presentació qui tradueixi les
peticions HTTP a esdeveniments del sistema en termes de la capa de domini.
Però, aleshores, qui s'encarregarà en la capa de domini de gestionar aquests
esdeveniments?

Suposem que en un cas d'ús de compra de productes d'una botiga virtual tenim
una petició de consulta d'una categoria de productes:

GET http://www.emusica.com/consultarCategoria?idCategoria=321 HTTP/1.1

La capa de presentació la traduirà en un esdeveniment de sistema que corres-


pongui a una operació de la capa de domini amb la signatura següent:

public Categoria consultarCategoria(String idCategoria)

Però quin objecte de la capa de domini serà l'encarregat d'atendre aquesta pe-
tició? Segons el patró Controlador, ho farà un objecte controlador que podem
definir a diferents nivells:

• Controlador de façana:

Aquest tipus de controlador té l'avantatge de ser una solució senzilla i ele-


gant. Quan el fem servir, cal escollir un nom que representi el conjunt
del sistema i que proporcionarà una abstracció de la capa de domini de
cara a altres capes. En cas de dubte, ControladorSistema sempre és prou
indicatiu.
El controlador de façana serà adequat quan no hi hagi gaires esdeveni-
ments diferents o bé quan es vulgui alleugerir a la capa usuària dels con-
© FUOC • PID_00276105 34 Catàleg de patrons

troladors de la tasca d'anar canviant de controlador i de triar en cada cas


el controlador més adequat.

• Controlador de cas d'ús:

Aquesta estratègia és interessant quan hi ha molts esdeveniments dife-


rents, ja que ens permet agrupar-los. En aquests casos, el controlador de
façana esdevé massa complicat i ens porta a dissenys amb baixa cohesió
i/o alt acoblament.
D'altra banda, té l'avantatge de permetre controlar l'estat del cas d'ús de
tal manera que es pugui gestionar i raonar sobre quins esdeveniments són
vàlids en cada moment del cas d'ús. Finalment, l'anàlisi basada en els es-
tereotips de classes de frontera, control i entitat es tradueix a aquest tipus
de controlador d'una manera molt directa facilitant la tasca de disseny.

• Controlador de sessió:

Aquests controladors comparteixen bona part dels avantatges dels con-


troladors de casos d'ús. Normalment, el nombre de controladors seguint
aquesta estratègia és inferior i, per tant, poden ser adequats en sistemes
amb un nombre d'esdeveniments mitjà.

• Controlador transacció:

Finalment, els controladors transacció tenen l'avantatge que ens permeten


tractar cada esdeveniment com un objecte. A més a més, ens permeten
© FUOC • PID_00276105 35 Catàleg de patrons

estendre fàcilment el sistema afegint noves classes per als nous esdeveni-
ments que vulguem afegir.

Les quatre opcions proposades pel patró controlador tenen els seus avantatges
i inconvenients. Cal escollir una modalitat que no generi un nombre gaire
excessiu de controladors massa simples, però que no creï tampoc controladors
massa sobrecarregats i difícils d'entendre i gestionar.

Corol·lari

(3)
Una conclusió important d'aquest patró de disseny és que s'ha d'evitar de to- en anglès, servlet.
tes maneres que la capa de presentació i els seus components (com ara botó,
finestra, text àrea per a aplicacions d'escriptori o miniaplicacions de servidor3
i JSP per a aplicacions web, etc.) siguin responsables de gestionar els esdeveni-
ments del sistema. És a dir, els esdeveniments de sistema haurien de ser gesti-
onats per la capa de domini.

5.2. Expert (Expert)

Context

S'ha de fer un tractament o càlcul sobre una informació.

Solució

Assignar la responsabilitat de fer el tractament o càlcul a la classe que


conté la informació necessària. Anomenarem aquesta classe Expert (pel
que fa a aquest tractament o càlcul).

Per exemple, suposem que estem desenvolupant un sistema de reserva de bu-


taques per a un cinema. Volem dissenyar el tractament relatiu a la consulta de
la llista de butaques disponibles per a una sessió:
© FUOC • PID_00276105 36 Catàleg de patrons

Per a dissenyar l'operació executar() del controlador, partirem de


l'identificador de la sessió. Per tant, el primer expert que cal buscar és qui co-
neix l'identificador d'una sessió: la mateixa sessió.

Un cop determinada la sessió, haurem de consultar la llista de butaques de la


sala on es fa la sessió. L'expert respecte a aquesta llista és la sala.

Finalment, caldrà saber, per a cada butaca de la llista, si està o no reservada


per a aquella sessió. En aquest cas, l'expert és la butaca.

El disseny resultant, doncs, és el següent:

5.3. Fabricació pura (Pure fabrication)

Context

S'ha de fer un tractament o càlcul independent de la lògica del domini.

Solució
© FUOC • PID_00276105 37 Catàleg de patrons

Assignar un conjunt de responsabilitats altament cohesionades entre


elles a una classe artificial o de conveniència que no representa un con-
cepte del domini del problema; una classe inventada per a suportar alta
cohesió, baix acoblament i reutilització.

Se'n diu Fabricació pura perquè la nova classe és una fabricació de la imaginació
(no existeix en el món real) i, atès que les seves responsabilitats estan molt
cohesionades, el seu disseny és molt net, o pur.

Per exemple, suposem que el sistema que estem desenvolupant ha de calcular


la mitjana i la desviació estàndard de les qualificacions d'un estudiant, i també
les d'una assignatura a partir del model conceptual següent:

Podem assignar la responsabilitat de fer el càlcul de la mitjana i la desviació


estàndard de les qualificacions de l'estudiant (vegeu el patró Expert) a la classe
Estudiant, i també a la classe Assignatura les de l'assignatura, però aquesta
solució no ens permetrà reutilitzar fàcilment el càlcul.
© FUOC • PID_00276105 38 Catàleg de patrons

Aplicant el patró Fabricació pura, podrem assignar la responsabilitat de fer el


càlcul de la mitjana i la desviació estàndard de manera que la puguem reuti-
litzar i només l'hàgim d'implementar una vegada:

5.4. Creador (Creator)

Context

Ens cal crear instàncies d'una classe.

Solució

Assignar a la classe B la responsabilitat de crear una instància de la classe


A si es compleix un o més dels casos següents:

B agrega objectes d'A.

B conté objectes d'A.

B registra objectes d'A.

B utilitza més estretament objectes d'A.

B té les dades d'inicialització que es passaran a un objecte d'A quan sigui


creat (i, per tant, B és un Expert respecte a la creació d'A).

Si es pot aplicar més d'una opció, és més recomanable inclinar-se per


una classe B que agregui o contingui la classe A.

Per exemple, suposem que estem desenvolupant un sistema de suport a la


matriculació d'alumnes d'una universitat:
© FUOC • PID_00276105 39 Catàleg de patrons

Qui serà el responsable de la creació d'un nou grup d'una assignatura? Segons
el patró creador, com que l'assignatura agrega instàncies de grup, serà la res-
ponsable de la creació de les instàncies de grup. Per tant, la solució serà la
següent:
© FUOC • PID_00276105 40 Catàleg de patrons

6. Patrons de disseny

Els patrons de disseny són aquells que s'apliquen per a resoldre problemes Vegeu també
concrets de disseny que no afectaran el conjunt de l'arquitectura del sistema.
Podeu trobar més informació
sobre els patrons de disseny
6.1. Estat (State) l'apartat 2.4 del mòdul "Intro-
ducció als patrons".

Context

Els objectes d'una classe varien el seu comportament depenent del seu
estat.

Tenim operacions amb grans blocs condicionals que depenen de l'estat de


l'objecte. Les combinacions d'estat són comunes a més d'un bloc i/o operació.

Problema

Volem aprofitar el polimorfisme per a triar el comportament adequat en cada


moment sense necessitat de fer servir condicionals, però no podem fer servir
la generalització dinàmica per a especificar una subclasse diferent per a cada
estat.

Per exemple, imaginem que, durant el desenvolupament del nostre sistema,


ens hem trobat amb una classe anomenada Connexió que representa una con-
nexió a un servidor remot. El seu diagrama d'estats és el següent:

Aquesta classe té tres operacions: obrir, tancar i escriure. El comportament


serà diferent segons l'estat: per exemple, quan es crida l'operació tancar sobre
una connexió oberta, s'envia un missatge de final de connexió al servidor i es
© FUOC • PID_00276105 41 Catàleg de patrons

modifica l'estat de la connexió (que passa a estar tancada), mentre que, quan
està tancada, cridar l'operació tancar produeix un error. Se'ns acut un possible
disseny que solucionaria el nostre problema:

Aquesta solució, però, no ens és vàlida perquè el llenguatge que farem servir no
suporta la generalització dinàmica (és a dir, no ens permet que una instància
de connexió canviï de subclasse durant l'execució del sistema), de manera que
necessitem una altra manera de resoldre el problema.

Solució

Separar la part de la classe que varia depenent de l'estat de la resta i


tractar cada estat com una classe que implementarà el comportament
esperat quan l'objecte es troba en aquell estat.
© FUOC • PID_00276105 42 Catàleg de patrons

El Context delega en la classe Estat les operacions que variïn depenent


d'aquest, i si és necessari s'afegeix com a paràmetre.

Aplicat al nostre cas, obtindríem el resultat següent:

Ara, per a canviar l'estat de la connexió, en lloc d'haver de canviar de subclas-


se, les instàncies de connexió només han de modificar la instància concreta
d'EstatConnexio que tenen associada.

Conseqüències

• Es facilita l'extensió del disseny afegint nous estats.


• Cal decidir qui és el responsable d'efectuar les transicions entre estat (el
context o l'estat concret). Aquesta decisió implicarà un acoblament entre
aquesta classe i els estats concrets (la qual cosa afectarà l'extensibilitat del
disseny).
• Les transicions d'estat esdevenen explícites.
• Els objectes Estat poden ser compartits entre diferents contextos.
• Cal determinar quan es creen i quan es destrueixen els objectes Estat (la
seva creació i eliminació pot no estar lligada a la creació i destrucció del
context).

Variacions

En la solució, hem suposat que l'associació entre el context i l'estat és un a


un (és a dir, per a cada objecte Context tenim un objecte Estat). Modificant
aquesta cardinalitat, arribem a dues situacions interessants: un objecte Con-
text que estigui en més d'un Estat al mateix temps i/o un objecte Estat que
serveixi per a més d'un Context.

Per a fer que un objecte Context estigui en més d'un estat al mateix temps,
haurem de modificar la cardinalitat de l'associació al costat de l'Estat posant-hi
un asterisc (*). Això ens pot ser útil quan les diferències entre els estats són
© FUOC • PID_00276105 43 Catàleg de patrons

només relatives als atributs i associacions que es poden tenir, però serà pro-
blemàtic si hi ha operacions que estiguin implementades de manera diferent
en dos dels estats simultanis.

Per a fer que un objecte Estat estigui associat a més d'un context, hem de mo-
dificar la cardinalitat de l'associació al costat del context posant-hi un asterisc
(*). En aquest cas, hem de posar en l'objecte Context tots els atributs de tots
els estats, ja que si estiguessin en l'objecte Estat el seu valor seria compartit per
tots els contextos. Aquesta solució és especialment interessant si els objectes
Estat són costosos de crear i/o mantenir en memòria.

6.2. Façana (Facade)

Context

El nostre sistema està estructurat en subsistemes o capes. Un subsistema (o


capa) A utilitza un altre subsistema (o capa) B.

Problema

Volem reduir l'acoblament entre dos subsistemes.

Volem accedir a una versió simplificada d'un subsistema.

Volem oferir un únic punt d'entrada a un subsistema.

Per exemple, suposem que el sistema que estem desenvolupant té una classe
anomenada PersonaDAO que conté les operacions per a accedir a la base de
dades de persones. Aquesta classe ha d'accedir a les interfícies de programació
de la base de dades, de manera que està acoblada amb totes les classes del
subsistema de bases de dades:
© FUOC • PID_00276105 44 Catàleg de patrons

Aquest acoblament es repetirà per a totes les classes que accedeixin a la base
de dades. Per exemple, si afegim una classe ClientDAO per accedir a les dades
dels clients, també estarà acoblada amb tot el subsistema de base de dades.

Com podem evitar que tantes classes depenguin del subsistema de base de
dades?

Solució

Crear una nova classe Façana que representi tot el subsistema o capa
accedit.

La Façana concentra la dependència en un únic punt de manera que els canvis


en el subsistema utilitzat tindran el mínim impacte sobre les classes del sub-
sistema client. A més, concentrarà part de la complexitat derivada de l'ús del
subsistema que representa i n'ocultarà l'estructura de classes interna.

La Façana pot pertànyer a un subsistema o a l'altre depenent de les nostres


necessitats i possibilitats.

Si el que volem és reduir l'acoblament entre A i B o accedir a una versió sim-


plificada de B, afegirem una Façana al subsistema client (A) que concentri
l'acoblament i/o ens simplifiqui la visió que tenim de B.
© FUOC • PID_00276105 45 Catàleg de patrons

En cas que vulguem oferir un únic punt d'entrada a B, afegirem una Façana
a B que serà l'única classe pública del subsistema (la resta de classes poden
tenir visibilitat de paquet) per tal d'assegurar-nos que no hi ha cap altra via
d'entrada.

En el nostre exemple, volem reduir l'acoblament respecte al subsistema de


base de dades i no el podem modificar perquè és un subsistema desenvolupat
per una altra organització. Per tant, el que farem serà crear una façana dins
el subsistema client:
© FUOC • PID_00276105 46 Catàleg de patrons

Com es pot veure, en el nou disseny les dependències estan més concentrades.
Qualsevol canvi en el subsistema de base de dades afectarà només la classe
FaçanaBD.

Conseqüències

• Les dependències estan concentrades en una única classe i, per tant, si


es produeix un canvi en el subsistema utilitzat, aquest afectarà només la
façana i no es propagarà a la resta de classes del subsistema client.

• La solució és més complexa. Si la interfície que ofereix el subsistema B és


prou estable i senzilla, és possible que la façana no afegeixi prou valor per
a justificar-ne l'ús.

6.3. Iterador (Iterator)

Context

Un objecte agregat vol donar accés als seus elements.

Un objecte vol donar accés als objectes que té associats.


© FUOC • PID_00276105 47 Catàleg de patrons

Problema

No volem exposar l'estructura de dades que fem servir internament per


a emmagatzemar les referències als objectes associats o agregats (la re-
presentació interna de l'agregació o l'associació) a qui hi accedeixi.

D'aquesta manera volem evitar l'acoblament de les nostres classes Client cap
a aquesta estructura (fet que ens dificultaria canviar-la per una altra), així com
possibles manipulacions d'aquesta estructura sense el nostre control.

Volem proporcionar diferents estratègies per a accedir als elements (per exem-
ple, amb criteris d'ordenació diferents).

No volem que la classe agregada tingui la responsabilitat de portar el control


de quins elements ha mostrat ja a cada classe Client que pugui accedir-hi.

No volem assignar la responsabilitat de saber com es recorre l'estructura de


dades interna a les nostres classes Client.

Per exemple, suposem que el sistema que estem desenvolupant té les classes
següents:

(4)
Com que una Empresa ha de mantenir un conjunt de referències als Empleats En anglès, array.
4
que hi treballen, decidim fer servir un vector per a emmagatzemar aquestes
referències:

Volem afegir una operació a Empresa que ens permeti consultar el conjunt dels
empleats de l'empresa. Si retornem el vector directament, estarem exposant
l'estructura interna de la classe Empresa (amb els problemes que això compor-
ta).

Solució
© FUOC • PID_00276105 48 Catàleg de patrons

Crear una classe Iterador que coneixerà l'estructura interna, com s'ha de
recórrer i com s'ha de modificar (per exemple, notificant-ho a la classe)
i que encapsularà aquest coneixement.

Podem associar una instància d'iterador a cada client de manera que sigui
l'iterador qui tingui la responsabilitat de saber quins elements ha mostrat i
quins no a cada client.

La classe Iterador proporcionarà una interfície independent de la representació


interna amb operacions genèriques per a recórrer els elements de l'estructura:

• reset() Ens permet reiniciar el recorregut.

• next() Avança cap al següent element i el retorna.

• hasNext() Ens indica si encara queden elements per recórrer.

• També podem afegir altres operacions (remove, last, etc.) segons les neces-
sitats concretes de cada cas.

En el nostre cas, la solució aplicant el patró Iterador seria la següent:

En aquest cas, si volguéssim fer un recorregut dels treballadors d'una empresa


faríem:

IteradorEmpleats empleats = empresa.empleats();


while(empleats.hasNext()) {
Empleat actual = empleats.next();
}

Una possible implementació d'IteradorEmpleats seria la següent:

public class IteradorEmpleats {


Empleat[] dades;
int posicioActual;
IteradorEmpleats(Empleat[] empleatsEmpresa) {
dades = empleatsEmpresa;
© FUOC • PID_00276105 49 Catàleg de patrons

posicioActual = -1;
}
public void reset() {
posicioActual = -1;
}
public boolean hasNext() {
return posicioActual + 1 < dades.length;
}
public Empleat next() {
posicioActual++;
return dades[posicioActual];
}
}

Conseqüències

• Podem modificar la representació interna sense haver de modificar les clas-


ses Client.

• Podem variar l'estratègia utilitzada per a accedir als elements (per exemple,
modificant el criteri d'ordenació).

• Podem restringir la capacitat de modificació dels elements agregats. Si ex-


posem la representació interna, els clients la podrien modificar sense que
la classe agregada se n'assabentés. Amb un Iterador, aquest podria notificar
la classe agregada.

• Se simplifica la interfície de l'objecte agregat a causa de la reducció de res-


ponsabilitats (les delega a l'Iterador).

6.4. Mètode factoria (Factory method)

Context

Un objecte ha de crear objectes d'una altra classe.

Problema

L'objecte que ha de crear altres objectes no pot anticipar la classe


d'aquests.

Volem delegar la responsabilitat d'especificar la classe dels objectes que


s'han de crear a les nostres subclasses.
© FUOC • PID_00276105 50 Catàleg de patrons

Una classe delega una responsabilitat a una o més classes d'ajuda i volem aïllar
la responsabilitat de decidir quina classe d'ajuda farem servir.

Suposem, per exemple, que estem desenvolupant un sistema que ha de no-


tificar certs esdeveniments als usuaris. La classe encarregada de gestionar les
notificacions ha de crear un missatge i enviar-lo, però volem delegar a les sub-
classes la selecció del tipus de missatge a enviar (per exemple, de correu elec-
trònic o un missatge MMS a un telèfon mòbil).

Solució

Definir, a la classe abstracta Creador (la que crea els objectes), un mè-
tode "crear" encarregat de la creació i que anomenarem mètode factoria.
Proporcionar, a cada subclasse de Creador, una implementació del mè-
tode factoria que creï un tipus concret d'objecte.

En el nostre exemple, aplicarem el mètode factoria fent que la decisió del tipus Vegeu també
de missatge a crear sigui de la subclasse. D'aquesta manera, els usuaris que
Vegeu més informació sobre el
treballin amb un notificador de tipus NotificadorEMail rebran els missatges principi de disseny obert-tan-
per correu electrònic i els que facin servir un NotificadorMMS els rebran al seu cat en l'apartat 2.3 d'aquest
mòdul.
telèfon mòbil. A més, si volem afegir nous mecanismes de notificació al nostre
sistema, només hem d'afegir les noves subclasses de Notificador i de Missatge
respectant, així, el principi obert-tancat.
© FUOC • PID_00276105 51 Catàleg de patrons

6.5. Mètode plantilla (Template method)

Context

Tenim un algorisme del qual volem que algunes subclasses en puguin


redefinir algunes parts.

Problema

Volem que les subclasses només hagin de redefinir la part que els és específica Vegeu també
i evitar haver de reescriure les parts comunes, la qual cosa violaria el principi
Vegeu més informació sobre
de no-repetició. el principi de no-repetició en
l'apartat 2.4 d'aquest mòdul.

Volem restringir el conjunt de modificacions possibles fixant una estructura


general i uns punts d'extensió.

Per exemple, suposem que en el sistema que estem desenvolupant tenim una
sèrie de classes que gestionen l'accés a la base de dades:
© FUOC • PID_00276105 52 Catàleg de patrons

Les implementacions de llistar() a GestorPersonaBD i GestorComandaBD se-


gueixen el mateix algorisme general:

1) Obtenir una connexió a la base de dades.

2) Construir una consulta.

3) Executar la consulta.

4) Tractar el resultat.

5) Tancar la connexió a la base de dades.

Solució

Definir una classe abstracta que implementa l'algorisme general cridant


operacions abstractes que les subclasses poden redefinir.
© FUOC • PID_00276105 53 Catàleg de patrons

En el nostre exemple, definirem una operació llistar() que implementarà


l'algorisme general. Les parts específiques de cada subclasse les deixarem a ope-
racions abstractes que les subclasses hauran d'implementar:

public abstract class GestorBD {


public Iterator llistar() {
Connexio c = obtenirConnexio();
String consulta = construirConsulta();
Resultat r = c.executar(consulta);
List result = tractarResultat(r);
c.tancar();
return result.iterator();
}
protected abstract String obtenirConsulta();
protected abstract List tractarResultat(Resultat r);
(...)
}

D'aquesta manera, cada subclasse ha de definir les parts de l'algorisme que


li són pròpies i podrà redefinir altres parts de l'algorisme si la superclasse ho
permet.

Conseqüències

• Aquest patró és molt utilitzat per a millorar les possibilitats de reutilització


de codi, ja que permet reutilitzar no només funcionalitats senceres, si no
parts d'aquestes.

• La superclasse pot oferir implementacions per defecte d'alguns passos de


l'algorisme. Per tant, cal distingir entre les operacions que poden ser rede-
finides de les que ho han de ser per força (i de la resta d'operacions uti-
litzades); en molts llenguatges, això es pot fer mitjançant operacions abs-
tractes.

• En cas de tenir operacions amb implementacions per defecte, la subclasse


pot estendre aquesta implementació per defecte en lloc de substituir-la
completament.

6.6. Observador (Observer)

Context

Un canvi en un objecte implica canvis en d'altres objectes.

Problema
© FUOC • PID_00276105 54 Catàleg de patrons

Volem evitar l'acoblament entre la classe de l'objecte en el qual es pro-


dueix un canvi i la dels objectes que n'han de rebre la notificació.

Volem que el nostre sistema estigui obert a l'extensió per a permetre que nous Vegeu també
objectes siguin notificats sense haver de modificar la classe de l'objecte en el
Vegeu més informació sobre el
qual es produeix el canvi. principi de disseny obert-tan-
cat en l'apartat 2.3 d'aquest
mòdul.
Per exemple, suposem que estem desenvolupant un sistema de subhastes elec-
tròniques i tenim les classes següents:

Cada vegada que un usuari fa una licitació per un producte, el sistema ha


de notificar aquest canvi als compradors mitjançant l'enviament d'un correu
electrònic i ha de refrescar unes pantalles que mostren les últimes licitacions
que s'han produït en el sistema.

Volem evitar que la classe Producte estigui acoblada respecte a Compradors


i Pantalles.

Solució

Crear una nova abstracció per al mecanisme de notificació i fer servir


l'herència i el polimorfisme per a reduir l'acoblament.
© FUOC • PID_00276105 55 Catàleg de patrons

Quan es produeix un canvi en l'estat del subjecte concret, aquest invoca


l'operació notificar de la seva superclasse que s'encarregarà de recórrer la llista
d'observadors i invocar, per a cada un, l'operació actualitzar.

En el cas de les subhastes, el resultat d'aplicar el patró Observador seria el se-


güent:

Així doncs, la implementació de l'operació licitar quedaria de la manera se-


güent:

public void licitar(Moneda import) {


this.preu = import;
notificar();
}

La implementació del subjecte serà independent de les classes concretes:

public void notificar() {


foreach(o: Observador in observadors) {
o.actualitzar(this);
}
© FUOC • PID_00276105 56 Catàleg de patrons

Finalment, cada observador implementarà l'operació Actualitzar de manera


diferent:

public class PantallaEvolucioPreus {


public void actualitzar(Subjecte s) {
refrescar((Producte)s);
}
(...)
}
public class Comprador {
public void actualitzar (Subjecte s) {
String missatge = generarMissatge((Producte)s);
enviarCorreu(missatge);
}
(...)
}

Conseqüències

• L'acoblament es produeix a un nivell d'abstracció superior. El subjecte con-


cret només està acoblat respecte a una interfície molt senzilla.

• El subjecte concret no s'ha de preocupar de quins ni quants objectes exac-


tament estan interessats en la notificació. Això ens permet afegir i treure
observadors dinàmicament.

• Si els observadors poden provocar canvis en el subjecte concret que pro-


voquin noves actualitzacions, haurem de tractar aquest cas de manera es-
pecial per a evitar un bucle infinit d'actualitzacions.

• El protocol de notificació no ens diu què ha canviat exactament.

Variacions

En la solució proposada, el subjecte s'envia a si mateix com a paràmetre de


l'operació notificar. Una possible variació seria enviar una altra informació
amb la notificació (per exemple, els canvis concrets en l'estat que s'hagin pro-
duït).

Una altra opció és que el subjecte no enviï cap informació i que sigui
l'observador qui s'encarregui de demanar el nou estat a tots els subjectes que
observi. D'aquest model se'n diu model estirada en contraposició al model em-
penta, que hem vist anteriorment.
© FUOC • PID_00276105 57 Catàleg de patrons

6.7. Ordre (Command)

Context

Volem tractar les crides a operacions com a objectes, per exemple, per al se-
güent:

• Poder desfer les operacions.

• Executar-les en un altre entorn (en un altre procés o node de la xarxa) o


en un altre moment.

• Parametritzar un objecte (per exemple, quina acció ha d'executar un ele-


ment d'un menú d'usuari).

• Tenir un historial de canvis per tal de poder-los tornar a aplicar si cau el


sistema.

• Estructurar el sistema al voltant d'operacions d'alt nivell (per exemple, te-


nir una classe per a cada operació de sistema).

Problema

Necessitem un mecanisme per a poder tractar una operació com un ob-


jecte i el nostre llenguatge de programació no té aquesta capacitat.

Per exemple, volem que el sistema que estem desenvolupant permeti a l'usuari
desfer les accions portades a terme en el sistema (per exemple, si ha modificat
unes dades, desfer la modificació). Si poguéssim tractar cada acció de l'usuari
com un objecte, podríem desar una llista de les accions efectuades per a po-
der-les desfer; però el nostre llenguatge no ens permet tractar les crides a ope-
racions com a objectes.

Solució

Crear una classe les instàncies de la qual representen invocacions d'una


operació.

Podem convertir els paràmetres i el resultat en atributs de l'ordre. També po-


dem aprofitar per a establir una jerarquia d'herència entre les diferents opera-
cions:
© FUOC • PID_00276105 58 Catàleg de patrons

Aquesta solució es pot aplicar a l'exemple anterior, tractant cada acció de


l'usuari com un objecte i convertint el conjunt d'accions en una llista
d'objectes que recorden el que han fet i són capaços de desfer-ho:

Conseqüències

• L'objecte que invoca l'operació està desacoblat respecte al que la imple-


menta.

• Podem manipular i estendre les ordres com qualsevol altra classe del siste-
ma (per exemple, podem aplicar el patró Mètode plantilla).

• Podem crear composicions d'ordres que permetin l'execució conjunta


d'una sèrie d'ordres (per exemple, per a enregistrar macros).

• És fàcil estendre el nostre sistema afegint noves operacions sense haver de


modificar les existents.
© FUOC • PID_00276105 59 Catàleg de patrons

6.8. Altres patrons de disseny

A continuació es comenten, breument, altres patrons de disseny que, tot i que


potser la seva aplicabilitat és menys general, també s'han considerat impor-
tants.

6.8.1. Adaptador (Adapter)

Vegeu també
Permet utilitzar una classe fent servir un conjunt d'operacions diferent
al que ofereix originalment. Podeu trobar una explicació
detallada amb exemples sobre
adaptadors en l'apartat 1.6.1
del mòdul "Introducció als pa-
trons".
Per exemple, en el bastiment d'aplicacions gràfiques Swing de Java, el compo-
nent JTable utilitza la interfície TableModel per a obtenir les dades que ha de
visualitzar. Si volem mostrar les dades d'una estructura de dades pròpia, po-
dem escriure un adaptador per a poder oferir al component JTable la interfície
que espera sense haver de modificar la nostra classe.

6.8.2. Decorador (Decorator)

Vegeu també
Permet afegir responsabilitats a un objecte dinàmicament.
El Decorador està molt rela-
cionat amb el Representant,
que podeu trobar descrit en
El Decorador té la mateixa interfície que l'objecte que decora, implementa l'apartat 6.8.6 d'aquest mòdul.
les responsabilitats addicionals i delega la resta de responsabilitats a l'objecte
decorat:
Vegeu també

Vegeu més informació sobre


el patró Ordre en l'apartat 6.7
d'aquest mòdul.
© FUOC • PID_00276105 60 Catàleg de patrons

Per exemple, suposem que estem desenvolupant un sistema i volem fer un


estudi de rendiment. Volem poder afegir a les Ordres que implementen les
nostres operacions de negoci la responsabilitat de controlar quant de temps
triguen a executar-se. Com que volem activar i desactivar aquesta capacitat en
temps d'execució, en comptes de modificar les classes que tenim afegim una
nova classe: DecoradorRendiment.

public class DecoradorRendiment implements Ordre {


Ordre decorat;
long tempsEmprat;
(...)
public void executa() {
long inici = System.currentTimeMillis();
decorat.executa();
tempsEmprat=System.currentTimeMillis() - inici;
}
(...)
}

6.8.3. Estratègia (Strategy)

Estratègia i Mètode
Permet definir una família d'algorismes i fer-los intercanviables els uns plantilla
amb els altres. D'aquesta manera, permet escollir-ne un dinàmicament.
El patró de disseny Estratègia
ens permet solucionar proble-
mes molt similars als proble-
mes que soluciona el patró
L'Estratègia permet separar un algorisme general dels detalls concrets de cada Mètode plantilla que podeu
possible implementació facilitant, doncs, la reutilització de l'algorisme general trobar en l'apartat 6.5 d'aquest
mòdul. Sovint en podrem fer
de manera independent dels detalls. A més, permet reutilitzar les implemen- servir un o l'altre indistinta-
ment. La principal diferència
tacions dels detalls per a més d'un algorisme general atès que no fa servir he- rau en el fet que el patró Estra-
tègia fa servir delegació en lloc
rència sinó delegació. d'herència.
© FUOC • PID_00276105 61 Catàleg de patrons

Suposem que en el sistema que estem desenvolupant hem d'ordenar una llista
i volem poder escollir quin algorisme d'ordenació fem servir:

Al disseny resultant, l'operació empleatsOrdenatsPerNom d'una instància


d'empresa es comportarà d'una manera o altra depenent de la implementació
d'OrdenacioEmpleats que faci servir. A més, aquest comportament podria can-
viar dinàmicament si substituïm la instància d'OrdenacióEmpleat utilitzada
dins la classe Empresa per una d'una altra subclasse.

6.8.4. Instància única (Singleton)

Permet assegurar que, d'una determinada classe, només n'hi ha una ins-
tància en tot el sistema.

La implementació d'una classe d'Instància única no tindrà operacions de cons-


trucció accessibles des de fora de la mateixa classe i, per tant, només podrà ser
instanciada per ella mateixa.

public class Singleton {


private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
© FUOC • PID_00276105 62 Catàleg de patrons

Suposem que volem que totes les classes que accedeixin a la base de dades en
la nostra aplicació ho facin per mitjà de la mateixa instància, de tal manera
que puguem controlar fàcilment el nombre de connexions establertes contra
la base de dades. Per a aconseguir-ho, hem de dissenyar la classe d'accés a la
base de dades com una classe d'instància única.

6.8.5. Objecte nul (Null object)

Permet tractar el cas del valor nul com si fos una instància vàlida.

Per exemple, considerem aquest fragment de codi:

int total;
Comanda c = DB.carregaComanda("W2132");
if (c != null) {
total = c.total();
} else {
total = 0;
}

El tractament especial que hem de donar a l'objecte nul augmenta considera-


blement la complexitat del codi. És per aquest motiu que ens plantegem trac-
tar de manera uniforme tots dos casos. Per a fer-ho, el patró Objecte Nul ens
proposa crear una subclasse de Comanda que representa el cas especial en què
una Comanda és buida:
© FUOC • PID_00276105 63 Catàleg de patrons

El nostre fragment de codi quedaria de la manera següent:

int total;
Comanda c = DB.carregaComanda("W2132");
total = c.total();

6.8.6. Representant (Proxy)

Permet controlar l'accés a un objecte des d'un altre de manera transpa-


rent.

Suposem, per exemple, que estem desenvolupant un sistema que gestiona no-
tícies:

Les notícies s'emmagatzemaran en un repositori i suposarem que carregar-ne


el cos és una operació costosa, ja que pot implicar carregar grans quantitats
de text i, segurament, algunes imatges. El noticiari, normalment, mostrarà els
titulars de les notícies i, quan l'usuari en seleccioni una, en mostrarà el cos.

Per a millorar el rendiment del sistema, volem evitar que el cos de les notícies
es carregui fins que sigui necessari. Però voldríem fer-ho de manera transparent
per al Noticiari i per a la Notícia que ja tenim dissenyats.

El patró Representant proposa afegir un objecte (el Representant) que contro-


larà l'accés a la Notícia. Quan aquest representant detecti que es vol accedir al
cos de la notícia (i només en aquest cas), el carregarà:
© FUOC • PID_00276105 64 Catàleg de patrons

Exemple

Hem suposat que l'accés


a l'atribut públic cos
s'implementa amb un mètode
getCos en el nostre llenguatge
de programació.

(5)
En anglès, cache.

Queda per resoldre el problema de substituir l'objecte original pel seu repre-
sentant, condició necessària perquè l'aplicació del patró sigui realment trans-
parent a la classe Client. La manera de solucionar-ho serà diferent en cada cas
i queda fora de l'àmbit d'aquest patró.

El Representant pot tenir altres usos interessants com un representant cau5


(que ens servirà com a memòria cau estalviant invocacions a la classe repre-
sentada), un representant remot (que ens servirà per a invocar operacions a un
objecte que es troba en un altre node de la xarxa), un representant de seguretat
(que ens permetrà bloquejar les peticions que no compleixin els requisits de
seguretat), etc.

Aquest patró, en la seva estructura, és molt semblant al patró Decorador. La Vegeu també
principal diferència, però, rau en la seva finalitat: un Decorador afegeix res-
Vegeu més informació sobre
ponsabilitats a un objecte modificant-ne el comportament, mentre que un el patró Decorador en l'apartat
Representant en controla l'accés. 6.8.2 d'aquest mòdul didàctic.

6.8.7. Servidor abstracte (Abstract server)

Permet desacoblar una classe client respecte d'una classe que fa servir i
que anomenarem servidor.
© FUOC • PID_00276105 65 Catàleg de patrons

Per a fer-ho, introdueix una abstracció que representa l'ús que el client fa del
servidor.

Per exemple, suposem que tenim les classes següents:

Aquest disseny acobla la classe interruptor respecte a la classe Llum. A més a Vegeu també
més, s'incompleix el principi obert-tancat: Què passa si volem un interruptor
Vegeu més informació sobre el
per a un ventilador? principi de disseny obert-tan-
cat en l'apartat 2.3 d'aquest
mòdul didàctic.
El patró Servidor abstracte ens diu que introduïm una abstracció entre Inter-
ruptor i Llum que representi l'ús que fa l'interruptor de llums, ventiladors i
qualsevol altra classe que es vulgui controlar més endavant.

Cal destacar el detall del nom de la interfície. La interfície amb les operacions
de Llum es diu ControlatPerInterruptor i no Llum. Això és degut al fet que les
operacions de la interfície són les que sap controlar l'Interruptor i no necessà-
riament totes les de Llum. Per això diem que la interfície pertany a Interruptor
i no a Llum. Amb aquest disseny, qualsevol aparell que implementi Contro-
latPerInterruptor podrà ser controlat per una instància d'Interruptor.

Si els objectes que han d'implementar la nova interfície ja existeixen, segura- Vegeu també
ment ens caldrà adaptar-ne la interfície actual a la que introdueix el Servidor
Vegeu més informació del
abstracte. Per a fer-ho, ens podem basar en el patró Adaptador. patró Adaptador en l'apartat
6.8.1 d'aquest mòdul.
© FUOC • PID_00276105 67 Catàleg de patrons

Exercicis d'autoavaluació
El sistema que estem desenvolupant gestiona informació sobre cotització d'accions, ordres
de compra, i també unes pantalles que mostren la cotització de les diverses empreses.

Els usuaris poden programar ordres de compra de manera que quan la cotització de les accions
d'una empresa baixa per sota d'un cert valor límit (indicat en l'ordre), el sistema compri
automàticament el nombre d'accions que s'ha programat en l'ordre i les afegeix al compte
de l'usuari.

Nota

La classe AccioEnCompte és el
resultat de normalitzar la classe
associativa de l'associació "en-
Cartera" entre Compte i Em-
presa.

1. Volem dissenyar una operació que anomenarem executarOrdreCompra que, donada una
ordre de compra, calcula el valor de les accions comprades, verifica el saldo del compte i,
si el saldo és suficient, afegeix la quantitat d'accions comprades a la cartera del compte i
descompta el preu del saldo. Aplica els patrons d'assignació de responsabilitats per assignar
les diverses responsabilitats associades a aquesta funcionalitat.

2. En realitat, l'execució de l'operació de la tasca anterior s'hauria de fer automàticament


quan es produeixi un canvi en la cotització de les accions d'una empresa que faci que al-
guna ordre de compra esdevingui executable. A més, volem que les pantalles informatives
s'actualitzin automàticament cada vegada que es modifiqui el valor de cotització de qualse-
vol de les accions mostrades a la pantalla. Feu un disseny per a aquest comportament fent
servir un patró de disseny que us ajudi a evitar l'acoblament de la classe Empresa a pantalles
i ordres de compra.
© FUOC • PID_00276105 68 Catàleg de patrons

Solucionari

1. Per a aquesta operació farem l'assignació de responsabilitats següent:

Verificar si s'ha d'executar l'ordre de Compra: per a fer-ho caldrà consultar el valor de cotit-
zació (és Empresa qui el coneix) i comparar-lo amb el valor límit de l'ordre (és OrdreCompra
qui el coneix). En aquest cas, per cohesió assignarem aquesta responsabilitat a l'Ordre de
compra de tal manera que sigui l'única que conegui com es decideix si una ordre de compra
és executable.

Si l'ordre és executable, caldrà calcular l'import de compra de les accions, verificar el saldo
del compte i, si hi ha prou saldo, afegir el nombre d'accions indicat en l'ordre de compra al
compte de destinació. En aquest cas, l'Ordre és l'expert per a calcular l'import de compra,
mentre que el Compte és qui coneix el saldo i les accions.

L'expert que sap quantes accions d'una determinada empresa té un compte és la classe Ac-
cioEnCompte.

Pot passar, però, que el Compte no tingui prèviament cap Acció de la mateixa empresa i que
calgui, per tant, crear una nova instància d'AccioEnCompte. En aquest cas, segons el patró
Creador, serà el Compte qui s'encarregui de crear aquesta instància, ja que la instància de
Compte conté les seves instàncies d'AccionsEnCompte.

Seguint aquest criteri, doncs, obtenim el diagrama següent:

Nota

En cas que aquesta compra es vulgués efectuar com a resultat d'un esdeveniment generat
per l'usuari caldria, a més, una classe controladora que s'encarregués de rebre aquest es-
deveniment i d'invocar l'operació creada de la classe OrdreCompra.

En l'escenari en què la compra s'acaba efectuant (el valor de cotització està per sota del valor
límit i hi ha prou saldo) i en què el compte no tenia cap acció de l'empresa prèviament, el
diagrama de seqüències resultant de l'assignació de responsabilitats feta anteriorment és el
següent:
© FUOC • PID_00276105 69 Catàleg de patrons

2. El patró Observador ens permet notificar els canvis que es produeixen en una empresa a
aquells objectes que hi estiguin interessats (en el nostre cas, pantalles i ordres de compra)
amb un mínim acoblament.

Nota

L'Empresa cridarà l'operació notificar quan el seu atribut cotitzacio sigui modificat.
L'operació actualitzar d'OrdreCompra invocarà l'operació executar ja dissenyada.

L'estructura de la solució serà la següent:

L'associació "mostra" entre PantallaInformativa i Empresa ja no és necessària, atès que no-


més es feia servir a efectes de notificació (que ara resol el patró). De manera semblant, s'ha
suprimit la navegabilitat d'Empresa cap a OrdreCompra que ja no és necessària (tot i que
s'ha mantingut l'altre sentit de navegació perquè és necessari més enllà de la notificació).
D'aquesta manera, hem reduït l'acoblament entre les classes.
© FUOC • PID_00276105 70 Catàleg de patrons

Bibliografia
[FOW] Fowler, M. (1997). Analysis Patterns: Reusable Object Models. Massachusetts: Addison
Wesley Professional.

[GOF] Gamma E., Helm R., Johnson R., Vlissides J. (1995). Design Patterns: Elements of
Reusable Object-Oriented Software. Massachusetts: Addison Wesley Professional.

[LAR] Larman, C. (2003). Una introducción al análisis y diseño orientado a objetos y al proceso
unificado UML Y PATRONES. (2a ed.). Madrid: Prentice Hall.

[MAR] Martin, R.C. (2003). Agile Software Development: Principles, Patterns, and Practices.
Upper Saddle River, New Jersey: Prentice Hall.

Bibliografia addicional

[BECK] Beck, K. (2002). Test Driven Development: By example. Massachusetts: Addison Wes-
ley Professional.

[FOWDI] Fowler, M. (2004). http://www.martinfowler.com/articles/injection.html

[HUNT] Hunt, A.; Thomas, D. (2000). The Pragmatic Programmer. Massachusetts: Addison
Wesley.

[MEY] Meyer, B. (1999). Construcción de software orientado a objetos. (2a. ed.). Madrid: Pren-
tice Hall.

[POSA] Buschmann, F.; Meunier, R.; Rohnert, H.; Sommerlad, P.; Stal, M. (1996).
Pattern-Oriented Software Architecture: A System Of Patterns. West Sussex, Anglaterra: John Wi-
ley & Sons Ltd.

You might also like