POO
Conceptes de POO
Abstracció
L'abstracció amaga la complexitat a l'usuari i només mostra la informació rellevant. Permet centrar-nos en el "què" en lloc del "com". Els detalls d'un mètode abstracte són implementats de forma separa per cada classe.
Objectius: reusabilitat de codi, flexibilitat d'implementació, herència múltiple.
Java: classes abstractes (abstracció parcial) i interfícies (abstracció total).
Sintaxi: abstract
(classes abstractes) i interface
, implements
(interfícies).
Encapsulació
L'encapsulació lliga les dades i els mètodes relacionats dins d'una classe. També protegeix les dades (estat) fent els camps privats i donant accés a ells només mitjançant els mètodes relacionats que implementen les regles de negoci i invariants.
Objectius: protecció de dades (estat), llegibilitat del codi.
Java: camps privats i mètodes públics (només els exposats).
Sintaxi: private, setX(), getX()
.
Herència
Amb l'objectiu de reusar codi, l'herència permet que una classe fill hereti les característiques (camps i mètodes) d'una altra classe pare. La relació es diu is-a. L'herència s'utilitza quan sabem que:
- Les dues classes es troben en el mateix domini lògic.
- La superclasse és un contracte que no canviarà (interfície o classe abstracta).
- Les millores realitzades per la subclasse són principalment additives.
L'alternativa és la composició.
Objectius: reusabilitat de codi, llegibilitat del codi.
Java: Classe pare i class fill. Alternativa vàlida: patrons de composició i delegació.
Sintaxi: extends
.
Composició
La composició és una alternativa de l'herència per a reusar codi. La relació es diu has-a. Es pot aconseguir utilitzant instàncies d'altres objectes. És una tècnica preferible a la herència, ja que redueix l'acoblament del codi. Un tipus específic de composició és l'agregació, que no implica la propietat dels objectes interns i, per tant, tampoc la destrucció d'aquests quan l'objecte contenidor es destrueix.
Objectius: reusabilitat de codi.
Java: variables d'instància d'altres classes.
Sintaxi: private ClassName instanceName
.
Polimorfisme
El polimorfisme permet substituir i estendre la funcionalitat d'objectes que tenen una mateixa interfície per uns altres durant el temps d'execució (dynamic binding).
Objectius: llegibilitat del codi, flexibilitat del codi.
Java: sobreescriptura de mètode (dinàmic), menys rellevant: sobrecàrrega de mètode (estàtic).
Sintaxi: myMethod() myMethod(int x) myMethod(int x, String y)
(estàtic) i ParentClass.myMethod() ChildClass.myMethod()
(dinàmic).
Relacions entre objectes
Aquests són els tipus de relacions comunes entre objectes:
- Associació: un objecte fa referència a un altre.
- Dependència: un objecte rep una referència en una de les seves operacions.
- Agregació: un objecte fa referència a diversos objectes.
- Composició: un objecte crea diversos objectes.
Aquest és un diagrama que les mostra.
- Herència de Number i implementació de Comparable
- Associació entre Child1 i Parent1
- Associació navegable des de Child2 cap a Parent2
- Agregació: Wheel pot existir sense Car
- Composició: Heart no pot existir sense Human
- Dependència: Object1 usa l'Object2
Tipus d'objectes
Un objecte és una entitat que pot integrar comportament i estat. La pràctica més acceptada és la d'encapsular l'estat darrere del comportament, perquè formen part de la implementació, que és convenient amagar.
Una primera classificació dels objectes es pot fer en funció de dues característiques: si tenen estat (stateful) o no (stateless), i en el cas que en tinguin, si són immutables o no (mutables).
- Un objecte té estat si el seu comportament depèn d'interaccions prèvies. Els objectes sense estat tenen un comportament esperable que només depèn dels paràmetres de les crides, com passa a la programació funcional.
- Un objecte immutable té un estat que no pot canviar-se després de ser creat. Permeten escriure codi més fàcil d'entendre i segurs quan hi ha concurrència.
Segons el seu ús, podem fer una classificació dels dos tipus d'objectes que podem trobar més freqüentment a Java.
- Abstract Data Types (ADT): són objectes amb comportament i estat encapsulat, i habitualment mutables. Cal definir detalladament el seu comportament, i evitar retornar referències a objectes mutables del seu estat.
- Estructures de dades: són objectes amb només estat, i poden ser immutables. Permeten transferir dades o representar un estat.
Els ADT
Patrons de disseny
Un patró de disseny és una solució general a un problema comú i recurrent en el disseny de programari. Un patró de disseny no és un disseny acabat que es pot transformar directament en codi; és una descripció o plantilla per resoldre un problema que es pot utilitzar en moltes situacions diferents.
Tenim algunes categories generals:
- De comportament: identifiquen patrons de comunicació entre objectes.
- Estructurals: faciliten el disseny quan s'han d'establir relacions entre entitats.
- Creacionals: relacionats amb mecanismes de creació d'objectes de la forma més adient per cada cas.
- Concurrència: tracten el paradigma de programació multifil.
A continuació es mostren alguns patrons importants.
Patrons de comportament
Command
El patró command encapsula una sol·licitud com a objecte, de manera que us permetrà parametrizar altres objectes amb diferents peticions, cues o peticions de registre i donar suport a operacions reversibles.
Tenim dos actors principals: el Client crea el ConcreteCommand i assigna el seu Receiver. L'Invoker té Commands que pot executar.
Iterator
El patró iterator proporciona una manera d'accedir als elements d'un objecte agregat seqüencialment sense exposar la seva representació subjacent.
Exemples: java.util.Iterator i java.util.Enumeration
Observer
El patró observer defineix una dependència entre molts objectes de manera que quan un objecte canvia d'estat, tots els seus dependents són notificats i actualitzats automàticament.
Exemples: java.util.EventListener
State
El patró state permet a un objecte alterar el seu comportament quan canvia el seu estat intern. L'objecte semblarà canviar de classe.
El Context pot tenir un nombre de States. Quan cridem request(), el que fem es cridar el handle corresponent del State actual. Un Client no coneix el funcionament intern dels States.
Strategy
El patró strategy s’utilitza quan tenim diversos algorismes per a una tasca específica i el client decideix que s’utilitzi la implementació real en temps d’execució.
És molt similar a State, però és el Client qui habitualment escull la Strategy. Exemples: Collections.sort() amb el paràmetre Comparator
Dependency Injection
El patró de Dependency Injection permet a un objecte rebre les instàncies d'altres objectes de que depèn. Això permet que el client no s'hagi de referir a instàncies concretes, seguint el principi d'inversió de dependència. S'utilitza per implementar el principi de la inversió de control (IoC) mitjançant un contenidor.
Hi ha tres tipus d'injecció: Constructor, Setter i Interface.
- Constructor: li diem al contenidor quina implementació s'utilitza per als paràmetres del constructor, que poden ser interfícies.
- Setter: li diem al contenidor quines propietats (amb setters) cal instanciar d'un cert objecte.
- Interface: li diem al contenidor que el nostre client rebrà les dependències mitjançant un mètode (del tipus setService).
Per utilitzar-los, cal configurar el contenidor. Es pot fer programàticament o mitjançant un arxiu de configuració.
L'objecte que injecta les dependències es diu injector, i els objectes de que depèn solen ser serveis.
Service Locator
El Service Locator és una alternativa al Dependency Injector, amb la particularitat que el codi client té una dependència del contenidor, que utilitza per localitzar tots els serveis que necessita. És a dir, no es produeix la injecció automàtica.
No es tracta d'una Factory, ja que no crea els serveis cada cop, només si cal. Es tracta més bé d'un registre.
Template method
El patró template method defineix l'esquelet d'un algorisme d'un mètode, diferint alguns passos a subclasses. Aquest patró permet que les subclasses redefineixin certs passos d'un algorisme sense canviar l’estructura de l’algoritme.
El templateMethod fa ús del subMethod. Exemple: construcció d'una casa de fusta o de vidre
Patrons estructurals
Adapter
El patró adapter converteix la interfície d'una classe en una altra interfície que els clients esperen. L'adaptador permet que les classes treballin conjuntament, tot i tenir interfícies incompatibles.
El ConcreteAdapter està composat amb l'Adaptee, que li permet una operació addicional: adaptedOperation. Exemple: un capità que només pot utilitzar barques de rem i no pot navegar
Composite
El patró composite permet compondre objectes en estructures d’arbre per representar jerarquies parcials. Composite permet als clients tractar objectes individuals i composicions d'objectes de manera uniforme.
Tant els objectes individuals com els composats poden ser tractats igual amb operation.
Decorator
El patró decorator atribueix dinàmicament responsabilitats addicionals a un objecte. Els decorators proporcionen una alternativa flexible a la subclasse per ampliar la funcionalitat.
Podem afegir un comportament al ConcreteDecorator. Exemple: trol decorat amb una porra
Facade
El patró facade proporciona una interfície unificada i més senzilla a un conjunt d’interfícies d’un subsistema. La façana defineix una interfície de més alt nivell que facilita la utilització del subsistema, seguint el principi del "Least knowledge".
Exemple: treballadors de la mina d'or
Proxy
El patró de proxy proporciona un substitut per a un altre objecte per controlar-ne l'accés.
Qualsevol Client pot tractar el Proxy com el RealSubject, ja que implementen Subject. Exemple: els tres mags que entren a la torre
Patrons creacionals
Separa la construcció d'un objecte complex de la seva representació, de forma que el mateix procés de construcció pot generar diferents representacions.
Builder
Factory method
Factory method defineix una interfície per crear un objecte, però permet que les subclasses decideixin quina classe s'inicia. Permet diferir la instància de classe a subclasses.
El mètode factoryMethod és abstracte, i s'encarrega de crear Products. Permet seguir el principi de "Dependency inversion": evitar dependències de tipus concrets. Exemple: el regne que necessita tres objectes temàtics
Singleton
El patró de Singleton assegura que una classe només té una instància i li proporciona un punt d'accés global.
Exemple: només pot haver una torre d'ivori