Pure Fabrication

Quale oggetto deve avere la responsabilità, quando non si vuole violare High Cohesion e Low Coupling, o altri obiettivi, ma le soluzioni suggerite da Expert (per esempio) non sono appropriate?

Gli oggetti software, come abbiamo detto, vengono rappresentati prendendo spunto dal modello di dominio reale, in modo tale che ci sia una salto rappresentazionale basso. Tuttavia, ci sono molte situazioni in cui, assegnare responsabilità solo a classi software ispirate a classi concettuali del modello di dominio provoca problemi in termini di coesione o accoppiamento non desiderati.

La soluzione è quella di assegnare un insieme di responsabilità altamente coeso ad una classe artificiale, puramente inventata (pure fabrication), in modo da poter sostenere coesione alta e accoppiamento basso.

Esempio POS
...

Supponiamo che si voglia salvare in una base di dati un oggetto Sale del POS NextGen.
Si supponga che sia necessario un supporto per salvare le istanze di Sale in una base di dati relazionale. Information Expert suggerisce di assegnare questa responsabilità alla classe Sale, poiché essa possiede i dati delle SaleLineItem. Ma una tale assegnazione di responsabilità avrebbe le seguenti implicazioni:

  • la classe Sale perderebbe coesione (focus), poiché il compito richiede un numero relativamente alto di operazioni di supporto circa le basi di dati, nessuna delle quali è correlata al concetto di vendita;
  • la classe Sale dovrebbe essere accoppiata all'interfaccia della base di dati relazionale, il che porta all'aumento del livello di accoppiamento, inoltre l'interfaccia della base di dati, sarebbe anche un elemento che non partecipa al dominio;
  • il salvataggio degli oggetti nella base di dati è un compito molto generale, per il quale molte classi necessitano di supporto. Assegnare questa responsabilità alla classe Sale implicherebbe difficoltà nel riuso di queste funzionalità (un conto è riusare una classe che esegue operazioni di salvataggio su una base di dati, un altro è riutilizzare una classe specifica (Sale) che esegue il salvataggio si una base di dati).

Pertanto, anche se l'information expert sembrerebbe una scelta logica, ciò porta ad un progetto con bassa coesione, alto accoppiamento e difficilmente riusabile.

La soluzione è creare una classe apposita che Sale usa per salvare sé stessa in una base di dati. Tale classe sarebbe una Pure Fabrication.

Tale classe non farà parte del modello di dominio. Il modello di dominio è un dizionario visuale per aiutare i progettisti a comprendere meglio il problema, ma soprattutto un insieme di realtà che il cliente comprende bene e attraverso il quale poter comunicare.

La pure fabrication usata per salvare i dati di Sale nella base di dati risolve i seguenti problemi:

  • La Sale rimane coesa.
  • La nuova classe per salvare la Sale sul database è di per sé coesa, poiché ha il solo scopo di memorizzare o inserire oggetti in un supporto di memorizzazione persistente.
  • La nuova classe è un oggetto generico e riusabile (su altri progetti per il salvataggio sulle basi di dati).

Esempio Monopoly
...

In questo progetto, il Player tira i dadi e somma il totale. I dadi sono oggetti molto generali, che possono essere usati in diversi giochi. Ma se si assegna la responsabilità di "lanciare i dadi" e fare la somma a Player si rischia di non poter riutilizzare tale funzione in altri progetti.

Inoltre il Player sarebbe accoppiato ai dadi in modo non banale, poiché deve conoscere diverse informazioni su di essi: quanti sono? quante facce hanno? quali sono le regole per calcolare il totale del lancio dei dadi? Il cambiamento di uno di questi aspetti (che riguardano i dadi, e non il giocatore) richiederebbe anche il cambiamento della classe Player.

La soluzione è una pure fabrication.
Si potrebbe creare un oggetto, puramente inventato, come un contenitore per mischiare e lanciare i dadi: Cup. Tale classe contiene una collezione di più oggetti Die (dadi). Quando un Player deve lanciare (roll) i dadi, allora Cup si occuperà di tale operazione.

Ovviamente in questo contesto, nel diagramma di sequenza corrispondente a questo scenario, il Player non lancia direttamente i dati, ma usa Cup per farlo. Il diagramma di sequenza va modificato.

Discussione
...

Nella progettazione a oggetti vengono usati, in linea di massima, due tipi di oggetti:

  1. Oggetti scelti per decomposizione rappresentazionale.
  2. Oggetti scelti per decomposizione comportamentale.

Per esempio, la scelta di una classe software come Sale si basa su una decomposizione rappresentazionale; la classe software è ispirata a un oggetto in un dominio.

A volta, si desidera assegnare delle responsabilità per raggruppare dei comportamenti oppure in base a un algoritmo, senza alcuna preoccupazione di creare una classe con un nome o uno scopo correlato a un concetto del dominio del mondo reale. Un buon esempio è un oggetto "algoritmo" tipo TableOfContentsGenerator, il cui scopo è quello di generare un indice, senza alcuna preoccupazione per scegliere un nome dalla terminologia di dominio dei libri e dei documenti. Si tratta di una classe di convenienza concepita dallo sviluppatore per raggruppare alcuni comportamenti o metodi correlati, e dunque è motivata da una decomposizione comportamentale.

Al contrario una classe TableOfContents è ispirata ad una decomposizione rappresentazionale, e deve contenere informazioni coerenti con il concetto del dominio reale, poi tale classe utilizzerà TabelOfContentsGenerator.

Gli oggetti scelti di solito per una decomposizione comportamentale sono di solito algoritmi, servizi, oggetti di supporto e così via.

Tali classi sono di solito progettate per raggruppare alcuni comportamenti comuni, e pertanto ispirate dalla decomposizione comportamentale anziché da quella rappresentazionale.

Controindicazioni
...

Talvolta la decomposizione comportamentale in oggetti pure fabrication è usata in modo eccessivo da chi non è esperto di progettazione a oggetti e ha maggiore familiarità con la decomposizione o l'organizzazione del software in termini di funzioni. Per estremizzare, le funzioni diventano oggetti.
La decomposizione comportamentale deve essere bilanciata.