Ací es mostren les diferències entre la revisió seleccionada i la versió actual de la pàgina.
patrons_disseny [2022/11/24 07:47] albert_palacios_jimenez creat |
patrons_disseny [2022/11/24 08:20] (actual) albert_palacios_jimenez |
||
---|---|---|---|
Línia 6: | Línia 6: | ||
---- | ---- | ||
- | ==== Exemples | + | ==== Patrons de disseny |
+ | |||
+ | - Fa referència a solucions de programació usades habitualment al dissenyar software. | ||
+ | |||
+ | - Habitualment els patrons de disseny es consideren bones pràctiques a la hora de programar. | ||
+ | |||
+ | - A vegades es critica alguns patrons de disseny ja que poden ser resultat d’un mal disseny del software, en aquests casos s’anomenen ‘anti-patrons’ | ||
+ | |||
+ | ==== Paradigmes de programació ==== | ||
+ | |||
+ | Els diferents llenguatges de programació ofereixen diferents eines per solucionar els problemes que surten a la hora de dissenyar el software. En la programació imperativa hi ha dos paradigmes: | ||
+ | |||
+ | - **Programació procedural**, | ||
+ | |||
+ | <code c> | ||
+ | struct persona0 { | ||
+ | String nom; | ||
+ | String edat; | ||
+ | } | ||
+ | |||
+ | function aniversari () { | ||
+ | persona0.edat += 1; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | - **Programació orientada en objectes**, en què les instruccions formen part de l’estat de l’objecte en què es declaren | ||
+ | |||
+ | <code java> | ||
+ | public class Persona { | ||
+ | String nom; | ||
+ | String edat; | ||
+ | public void aniversari () { | ||
+ | edat += 1; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Orientació a objectes ==== | ||
+ | |||
+ | És un paradigma en què la programació es fa definint objectes, que contenen dades i codi. | ||
+ | |||
+ | - **Dades**, o propietats de l’objecte | ||
+ | |||
+ | - **Codi**, procediments que operen sobre les dades (funcions) | ||
+ | |||
+ | |||
+ | Cal distingir entre les definicions i els objectes funcionals | ||
+ | |||
+ | - **Classes**, | ||
+ | |||
+ | <code java> | ||
+ | public class Poligon { | ||
+ | int num_vertex = 1; | ||
+ | String nom = “linia” | ||
+ | public Poligon (v1, n1) { | ||
+ | num_vertex = v1; | ||
+ | nom = n1; } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | - **Instàncies**, | ||
+ | |||
+ | <code java> | ||
+ | public static void Main (String[] args) { | ||
+ | Poligon pL = new Poligon(2, “linia”); | ||
+ | Poligon pT = new Poligon(3, “triangle”); | ||
+ | Poligon pQ = new Poligon(4, “quadrat”); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | pL, pT i pQ són instàncies de la classe Poligon | ||
+ | |||
+ | == Java, extends == | ||
+ | |||
+ | Quan volem fer servir les propietats i mètodes d’una classe pare fem servir ‘**extends**’. | ||
+ | |||
+ | També podem sobre-escriure els mètodes de la classe filla amb ‘**@Override**' | ||
+ | |||
+ | <code java> | ||
+ | package SuperClass; | ||
+ | public class SuperClass { | ||
+ | | ||
+ | | ||
+ | } | ||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | |||
+ | package SuperClass; | ||
+ | public class SubClass extends SuperClass { | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | |||
+ | package SuperClass; | ||
+ | public class Main { | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | == Java, implements == | ||
+ | |||
+ | Una clase abstracta que agrupa mètodes buits, que s’han d’implementar en altres classes | ||
+ | |||
+ | |||
+ | <code java> | ||
+ | package Animals; | ||
+ | interface Animal { | ||
+ | | ||
+ | | ||
+ | } | ||
+ | |||
+ | package Animals; | ||
+ | class Anec implements Animal { | ||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | |||
+ | package Animals; | ||
+ | class Gos implements Animal { | ||
+ | | ||
+ | | ||
+ | } | ||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | |||
+ | package Animals; | ||
+ | public class Main { | ||
+ | | ||
+ | Anec a = new Anec(); | ||
+ | Gos g = new Gos(); | ||
+ | |||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | == Java, mètodes abstractes == | ||
+ | |||
+ | Les classes abstractes es declaren sense implementació, | ||
+ | |||
+ | <code java> | ||
+ | package Animals2; | ||
+ | abstract class Animal { | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | |||
+ | package Animals2; | ||
+ | class Anec extends Animal { | ||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | |||
+ | package Animals2; | ||
+ | class Gos extends Animal { | ||
+ | | ||
+ | | ||
+ | } | ||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | |||
+ | package Animals2; | ||
+ | public class Main { | ||
+ | | ||
+ | Anec a = new Anec(); | ||
+ | Gos g = new Gos(); | ||
+ | | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Prototype ==== | ||
+ | |||
+ | Prototype és un patró que ens permet crear còpies d’objectes sense que dependre de les seves classes. | ||
+ | |||
+ | Quan volem una còpia exacte d’un objecte tenim algun problema: | ||
+ | |||
+ | - Els objectes poden tenir mètodes privats que no podem veure | ||
+ | |||
+ | - Com que cal conèixer la classe original, pot ser que implementi una altre classe | ||
+ | |||
+ | El patró “prototype” delega el procés de clonar un objecte als propis objectes que estan essent clonats. | ||
+ | |||
+ | Per fer-ho, es crea una “interficie” amb almenys el mètode “clone” que crea un objecte de la classe actual amb tots els valors de l’objecte antic | ||
+ | |||
+ | Quan es fa servir Prototype? | ||
+ | |||
+ | - Quan el teu codi no hagi de dependre de les classes que necessites copiar | ||
+ | |||
+ | - Imagina que tens una clase complexa que és complicada de configurar, prototype permet tenir diferents objectes configurats llestos per clonar i usar | ||
+ | |||
+ | Amb prototype hem de crear el mètode ' | ||
+ | <code java> | ||
+ | import java.util.Objects; | ||
+ | |||
+ | public abstract class Poligon { | ||
+ | |||
+ | public int x; | ||
+ | public int y; | ||
+ | public String color; | ||
+ | |||
+ | public Poligon() {} | ||
+ | |||
+ | public Poligon(Poligon target) { | ||
+ | if (target != null) { | ||
+ | this.x = target.x; | ||
+ | this.y = target.y; | ||
+ | this.color = target.color; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public abstract Poligon clone(); | ||
+ | |||
+ | @Override | ||
+ | public boolean equals(Object object2) { | ||
+ | if (!(object2 instanceof Poligon)) return false; | ||
+ | Poligon cast2 = (Poligon) object2; | ||
+ | return cast2.x == x && cast2.y == y && cast2.color.equals(color); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <code java> | ||
+ | public class Cercle extends Poligon { | ||
+ | |||
+ | public int radius; | ||
+ | |||
+ | public Cercle() { | ||
+ | } | ||
+ | |||
+ | public Cercle(Cercle target) { | ||
+ | super(target); | ||
+ | if (target != null) { | ||
+ | this.radius = target.radius; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public Poligon clone() { | ||
+ | return new Cercle(this); | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public boolean equals(Object object2) { | ||
+ | if (!(object2 instanceof Poligon) || !super.equals(object2)) return false; | ||
+ | Cercle cast2 = (Cercle) object2; | ||
+ | return cast2.radius == radius; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | == Exemple de Prototype == | ||
+ | |||
+ | {{ :: | ||
+ | |||
+ | ==== Factory ==== | ||
+ | |||
+ | Substituir la creació d’objectes a través de ‘new’ per crides a metodes ‘factory’ específics de cada classe. | ||
+ | |||
+ | Els objectes es segueixen creant amb ‘new’, però es fa de manera interna al mètode ‘factory’ | ||
+ | |||
+ | El objectes creats a través de ‘factory’ s’anomenen ‘products’ | ||
+ | |||
+ | Quina és la motivació de Factory? | ||
+ | |||
+ | - Els clients d’una llibreria no tenen que saber com funciona aquesta llibreria, o fins i tot el tipus d’objecte o constructors que es fan servir. | ||
+ | |||
+ | - Amb factory tens un ‘product’ que declara una interfície comú a tots els objectes i les seves subclasses | ||
+ | |||
+ | - Cal que hi hagi una classe ‘creator’ que té el mètode per fabricar ‘products’ | ||
+ | |||
+ | Quan es fa servir factory? | ||
+ | |||
+ | - Quan no sabem amb quins tipus d’objectes i dependencies haurà de treballar el nostre codi | ||
+ | |||
+ | - Quan volem donar maneres d’extendre els components de la nostre llibreria | ||
+ | |||
+ | - Quan volem estalviar recursos del sistema, aprofitant objectes enlloc de refentlos cada vegada | ||
+ | |||
+ | En Java Factory es pot implementar a través de: | ||
+ | |||
+ | - interface i classes que implementen les seves funcions | ||
+ | |||
+ | - amb classes derivades (extends) i sobreescrivint les funcions necessàries | ||
+ | |||
+ | Transportation.java | ||
+ | <code java> | ||
+ | public interface Transportation { | ||
+ | void deliverPackage(); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | FactoryTransportation.java | ||
+ | <code java> | ||
+ | public class FactoryTransportation { | ||
+ | |||
+ | public static Transportation getTransportation(String method) { | ||
+ | |||
+ | if(" | ||
+ | return new TransportationShip(); | ||
+ | else if(" | ||
+ | return new TransportationTruck(); | ||
+ | else if(" | ||
+ | return new TransportationVan(); | ||
+ | |||
+ | return null; | ||
+ | } | ||
+ | |||
+ | public static void deliver (Transportation transport) { | ||
+ | transport.deliverPackage(); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | TransportationShip.java | ||
+ | <code java> | ||
+ | public class TransportationShip implements Transportation { | ||
+ | |||
+ | @Override | ||
+ | public void deliverPackage() { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | TransportationTruck.java | ||
+ | <code java> | ||
+ | public class TransportationTruck implements Transportation { | ||
+ | |||
+ | @Override | ||
+ | public void deliverPackage() { | ||
+ | System.out.println(" | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | == Exemple de Factory == | ||
+ | |||
+ | {{ :: | ||
+ | |||
+ | ==== Singleton ==== | ||
+ | |||
+ | Singleton assegura que només hi ha una instància d’una classe determinada | ||
+ | |||
+ | Crees un objecte, si al cap d’una estona tones a crear un objecte d’aquella classe reps l' | ||
+ | |||
+ | Permet accedir un objecte de manera global en un programa, protegint-lo de sobre-escriptures | ||
+ | |||
+ | Com es fa? | ||
+ | |||
+ | - Posar el constructor de la classe com a ‘private’ | ||
+ | |||
+ | - Fer un mètode de creació ‘static’ que funciona com a constructor, | ||
+ | |||
+ | * El primer cop crida al constructor privat | ||
+ | * | ||
+ | * La resta de vegades, retorna l’objecte creat originalment | ||
+ | |||
+ | Avantages: | ||
+ | |||
+ | - Assegura que només hi ha una instància de la classe | ||
+ | |||
+ | - Es pot accedir a l’objecte de manera global | ||
+ | |||
+ | - Només s’inicia el primer cop | ||
+ | |||
+ | Crítiques: | ||
+ | |||
+ | - Es considera un ‘anti-patró’ que pot amagar un mal disseny de l’aplicació | ||
+ | |||
+ | - En entorns amb threads cal assegurar que diversos fils no creen un objecte ‘singelton’ diverses vegades | ||
+ | |||
+ | Exemple de Singleton: | ||
+ | |||
+ | <code java> | ||
+ | public final class ExempleSingleton { | ||
+ | |||
+ | | ||
+ | | ||
+ | |||
+ | | ||
+ | | ||
+ | } | ||
+ | |||
+ | | ||
+ | if (instance == null) { | ||
+ | | ||
+ | } | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Els Singletons també es poden ' | ||
+ | |||
+ | <code java> | ||
+ | ExempleSingleton instanceOne = ExempleSingleton.getInstance(" | ||
+ | ExempleSingleton instanceTwo = null; | ||
+ | try { | ||
+ | Constructor[] constructors = ExempleSingleton.class.getDeclaredConstructors(); | ||
+ | for (Constructor constructor : constructors) { | ||
+ | //Below code will destroy the singleton pattern | ||
+ | constructor.setAccessible(true); | ||
+ | instanceTwo = (ExempleSingleton) constructor.newInstance(" | ||
+ | break; | ||
+ | } | ||
+ | } catch (Exception e) { e.printStackTrace(); | ||
+ | </ | ||
+ | |||
+ | Què passa amb els objectes serialitzables? | ||
+ | |||
+ | Tot i que hi ha maneres d’implementar Singleton en objectes serialitzables, | ||
+ | |||
+ | == Exemple de Singleton == | ||
+ | |||
+ | {{ :: | ||
+ | |||
+ | ==== DAO, Data Access Object ==== | ||
+ | |||
+ | DAO es fa servir per separar la manera de guardar les dades i la l’estructura lògica amb la que s’hi treballa a nivell de programació. | ||
+ | |||
+ | Una interfície base DAO defineix les funcions que implementen tots els objectes DAO | ||
+ | |||
+ | Cada objecte DAO implementa la transformació entre la seva Classe i on estan emmagatzemades les dades | ||
+ | |||
+ | Una manera habitual de definir quines funcions ha de implementar DAO és CRUD, que significa: | ||
+ | |||
+ | - Create | ||
+ | |||
+ | - Read | ||
+ | |||
+ | - Update | ||
+ | |||
+ | - Delete | ||
+ | |||
+ | I són les operacions més habituals al treballar amb models de dades | ||
+ | |||
+ | <code java> | ||
+ | public interface Dao< | ||
+ | |||
+ | void add(T t); // Equival a Create | ||
+ | |||
+ | T get(int id); // Equival a Read | ||
+ | | ||
+ | |||
+ | void update(int id, T t); | ||
+ | |||
+ | void delete(int id); | ||
+ | |||
+ | void print(); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Avantatges de DAO | ||
+ | |||
+ | - Simplifica el treball dels programadors al abstreure l’estructura de dades de l’arquitectura del programa | ||
+ | |||
+ | - Unifica la manera de fer les crides pels diferents tipus d’objectes independentment de com es guarden les seves dades | ||
+ | |||
+ | Inconvenients: | ||
+ | |||
+ | - A vegades es defineixen massa funcions DAO fent que sigui difícil de mantenir o impossible d’aplicar a totes les dades per igual | ||
+ | |||
+ | - Resta flexibilitat, | ||
+ | |||
+ | - Separar la lògica de les dades pot implicar necessitar operacions “extra” per guardar les modificacions del model de dades | ||
+ | |||
+ | == Exemple de DAO == | ||
+ | |||
+ | {{ :: | ||
+ | |||
+ | ==== Observer ==== | ||
+ | |||
+ | Es crida a un mètode quan una variable cambia de valor. | ||
+ | |||
+ | Tradicionalment un objecte, manté una llista d’observadors que criden a mètodes quan hi ha canvis a l’estat de l’objecte (valors de les variables). | ||
+ | |||
+ | No hi ha una manera específica d’implementar l’observer, | ||
+ | |||
+ | Usos més habituals d' | ||
+ | |||
+ | - En interfícies gràfiques quan un usuari canvia un ‘input’ (text, select, checkbox, …) per executar funcions que s' | ||
+ | |||
+ | - Quan les dades canvien remotament i rebem els nous valors, aquests han d’actualitzar la interfície gràfica automàticament. O executar processos que tractin aquestes noves dades rebudes adeqüadament. | ||
+ | |||
+ | - Quan s’observa una carpeta d’arxius o un arxius i si hi ha canvis s’executa una acció (normalment ho fa un ‘thread’ paral·lel al procés) | ||
+ | |||
+ | En java cal definir l' | ||
+ | |||
+ | <code java> | ||
+ | Observable< | ||
+ | @Override | ||
+ | public void propertyChange(Integer oldValue, Integer newValue) { | ||
+ | System.out.printf(" | ||
+ | } | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | Observable.java | ||
+ | <code java> | ||
+ | public abstract class Observable< | ||
+ | |||
+ | private T value; | ||
+ | |||
+ | public Observable(T value) { | ||
+ | this.value = value; | ||
+ | } | ||
+ | |||
+ | public T getValue() { | ||
+ | return value; | ||
+ | } | ||
+ | |||
+ | public void setValue(T value) { | ||
+ | T oldValue = this.value; | ||
+ | this.value = value; | ||
+ | this.propertyChange(oldValue, | ||
+ | } | ||
+ | |||
+ | public abstract void propertyChange(T oldValue, T newValue); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Cada classe que vol observar alguna propietat necessita una llista d' | ||
+ | |||
+ | <code java> | ||
+ | import java.beans.PropertyChangeListener; | ||
+ | import java.beans.PropertyChangeSupport; | ||
+ | |||
+ | public class CotxeEvents { | ||
+ | private PropertyChangeSupport llistaObservers = new PropertyChangeSupport(this); | ||
+ | |||
+ | private CotxeEstats estat; | ||
+ | private int gasolina; | ||
+ | |||
+ | CotxeEvents () { | ||
+ | this.estat = CotxeEstats.ATURAT; | ||
+ | this.gasolina = 3; | ||
+ | } | ||
+ | |||
+ | public void addPropertyChangeListener(String name, PropertyChangeListener listener) { | ||
+ | llistaObservers.addPropertyChangeListener(name, | ||
+ | } | ||
+ | |||
+ | public void removePropertyChangeListener(String name, PropertyChangeListener listener) { | ||
+ | llistaObservers.removePropertyChangeListener(name, | ||
+ | } | ||
+ | |||
+ | public void setEstat (CotxeEstats newValue) { | ||
+ | CotxeEstats oldValue = this.estat; | ||
+ | if (oldValue != newValue) { | ||
+ | if (newValue != CotxeEstats.ATURAT) { | ||
+ | this.setGasolina(this.gasolina - 1); | ||
+ | } | ||
+ | if (this.gasolina > 0) { | ||
+ | this.estat = newValue; | ||
+ | llistaObservers.firePropertyChange(" | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public void setGasolina (int newValue) { | ||
+ | int oldValue = this.gasolina; | ||
+ | if (newValue > 0) { | ||
+ | this.gasolina = newValue; | ||
+ | } else { | ||
+ | this.gasolina = 0; | ||
+ | this.setEstat(CotxeEstats.ATURAT); | ||
+ | } | ||
+ | if (oldValue != this.gasolina) { | ||
+ | llistaObservers.firePropertyChange(" | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | |||
+ | == Exemple de Observer == | ||
+ | |||
+ | {{ :: |