Design Patterns

Creational

Builder

Costruisce oggetti complessi usando un approccio step by step, per esempio creando altri oggetti minori che compongono l'oggetto principale e assegnandoli di volta in volta.

Singleton

Questo pattern coinvolge una singola classe la quale è responsabile di creare un oggetto mentre si assicura che solo un singolo oggetto venga creato. In pratica questa classe non può essere istanziata, o meglio ogni istanza è in realtà l'accesso alla stessa univoca istanza.

Prototype

Consiste nell'implementazione di un'interfaccia "prototype" che comunica la creazione di un clone dell'oggetto corrente. Simile al Factory come concetto genera tuttavia dei cloni dell'oggetto da cui viene eseguita la creazione della nuova istanza invece di essere una istanza base con valori di default come nel Factory.

Factory

Una classe riceve istruzione di generare e restituire altri tipi di classi in base a varie condizioni.

Es. Rectangle, Square e Circle implementano Shape. ShapeFactory, in base alla stringa ricevuta nel metodo getShape(), restituisce una nuova istanza di una di queste classi.

Structural

Adapter

Es. classe MediaPlayer ha play() mentre AudioPlayer ha playVLC() e playMp3(). Entrambe estendono un'interfaccia, i metodi li hanno entrambi ma non sono implementati. Creo una classe "adapter" con un metodo play() che, in base al tipo, esegue il metodo corretto (tra playVlc() e playMp3()). E’ utilizzato come possibile soluzione di refactoring in caso il code smelling rilevi violazioni del quarto principio SOLID (Interface Segregation).

Composite

Consiste in una classe che si auto implementa in maniera gerarchica avendo una lista di istanze figlie della stessa classe.

Bridge

Si crea una classe astratta nel cui costruttore viene fornita l'implementazione di un'interfaccia.

Es. RedCircle e GreenSquare implementano l'interfaccia DrawApi e il metodo draw(). La classe astratta Shape ha un attributo drawApi di tipo DrawApi e il metodo astratto draw().

La classe Circle estende Shape implementando draw() come drawApi.drawCircle() e fornendo i suoi attributi specifici.

In questo modo posso fornire il tipo di estensione di Shape come parametro del costrutture quando vado a creare una Shape, es: Shape redCircle = new Circle(100,100,10, new RedCircle()).

Filter

Avendo una classe raggruppabile in liste, creo delle classi che estendono l'interfaccia "filter" con un metodo di filtraggio. In questo metodo, riceveranno una lista di istanze della prima classe e ne restituiranno la stessa filtrata in base ai criteri implementati nelle varie implementazioni del filter.

Decorator

Avendo due classi che implementano la stessa interfaccia, il decorator consiste in un'ulteriore oggetto il quale ha una delle primi classi come attributo e nei suoi metodi esegue chiamate a quegli stessi attributi permettendo, però, di eseguire operazioni prima e/o dopo. In pratica si crea un "wrapper" della funzione intorno ad una funzione.

Facade

Avendo diverse classi che implementano un'interfaccia, creo una classe "facade" la quale genera delle istanze di queste classi e ne "espone" dei metodi. Serve per semplificare l'utilizzo di gerarchie complesse di oggetti.

Flyweight

Avendo un oggetto, si crea una classe "factory" che conserva una mappa di istanze dell'oggetto utilizzando come chiave il valore assegnabile ad un attributo dell'oggetto. L'algoritmo richiede al factory un'istanza con un determinato attributo. Se già esistente la classe factory la fornisce prendendola dalla mappa e usando come chiave il valore dell'attributo stesso, altrimenti la crea e la inserisce nella mappa per poi restituirla.

Proxy

La classe "proxy" consiste in una classe che "contiene" come attributo un'istanza di un'altra classe, decorandone i metodi. Serve a creare un'interfaccia tra lo scope esterno e la classe contenuta.

Utilizzato nel CacheService del servizio Onboarding (Finance Evolution).

Behevioural

Chain of Responsability

Questo pattern crea una catena di oggetti che processano una richiesta. Ognuna delle classi che implementa il pattern possiede un riferimento a quella successiva che prenderà in carico di eseguire l'operazione successiva della catena.

Command

Delle classi implementano delle azioni. Altre classi possono quindi eseguire queste azioni creando delle operazioni più complesse che possono comprendere più azioni.

Interpreter

Fornisce un modo di valutare un'espressione ed eseguirla via codice.

Iterator

Permette di scorrere un elemento iterabile. Ogni istanza che implementa l'iterator possiede un riferimento all'elemento successivo (come nel Chain of Responsability avviene con l'incaricato nella catena di esecuzione). In questo modo, partendo dal primo, si possono raggiungere tutti gli elementi da iterare finchè il metodo hasNext() non restituisce nulla.

Mediator

Riduce la complessità di comunicazione tra due o più oggetti.

Es. l'oggetto User della chat ha un metodo sendMessage(String message) il quale chiama il metodo statico showMessage() dell'oggetto ChatRoom consentendo di non dover chiamare, da parte dell'esecuzione principale, sia il send() che lo show().

Observer

Notifica a tutti gli elementi in ascolto il cambiamento di un valore su un oggetto osservato dai vari listener.

Es. l'oggetto Subject ha il metodo setState(int state) dentro al quale viene chiamato il metodo notifyAllObservers(). Quest’ultimo cicla gli Observer, memorizzati in una lista del Subject, e ne esegue il metodo update(). Gli estensori dell'interfaccia Observer ricevono il Subject in fase di costruzione e si auto assegnano al subject di modo da averne coscienza dello stato, ma di consentirgli anche di eseguire sugli stessi l'update().

Memento

Tiene traccia degli stati di un oggetto per ripristinarli in caso di necessità.

Un oggetto Originator cambia stato frequentemente. Ad ogni cambio di stato genera un oggetto Memento che contiene lo stato. Un CareTaker colleziona questi "mementi", quando salvati, e li restituisce in base alle richieste di ripristino.

State

In questo pattern una classe cambia il suo comportamento in base allo stato assegnato. Lo stato è un oggetto con dei metodi di esecuzione. Quando questi metodi vengono eseguiti viene passato come argomento un Context il quale imposta come stato attuale l'oggetto-stato appena eseguito.

Null Object

Dato un oggetto crea un'estensione dell'oggetto che rappresenti l'oggetto come null in modo da non dover creare troppe condizioni. Questo oggetto si comporterà come se fosse nullo ma senza interrompere il flusso di codice.

Strategy

Prevede che una classe possa cambiare comportamento a runtime.

Es. diverse operazioni consistono in alcune classi che implementano l'interfaccia Strategy con un metodo doOperation(). Il Context contiene un attributo di tipo Strategy che può variare durante l'esecuzione assegnando implementazioni differenti di Strategy. Al momento della chiamata al metodo Context.executeStrategy() viene eseguito il doOperation() dello Strategy corrente.

Hint: state e strategy sono praticamente "complementari": nel primo viene impostato uno stato in seguito ad un'azione, nel secondo viene eseguita un'azione in seguito all'assegnazione di una strategia (anche se non necessariamente immediatamente conseguente nel secondo caso).

Template

Una classe astratta espone dei template per eseguire i suoi metodi. Le sue sottoclassi possono overridare l'implementazione del metodo a necessità, ma l'invocazione deve essere la stessa della classe astratta.

Visitor

Un oggetto di tipo "visitor" implementa diversi algoritmi di esecuzione ricevendo diversi oggetti come argomento overloadando il metodo visit(). I vari oggetti possiedono un metodo accept() nel quale ricevono il visitor che al suo interno esegue visit(this). In questo modo quando un oggetto esegue accept(istanza del visitor), l'algoritmo dello specifico visitor relativo allo specifico oggetto viene eseguito.

Next Post