• Skip to primary navigation
  • Skip to main content
  • Skip to footer

Codemotion Magazine

We code the future. Together

  • Discover
    • Events
    • Community
    • Partners
    • Become a partner
    • Hackathons
  • Magazine
    • Backend
    • Dev community
    • Carriere tech
    • Intelligenza artificiale
    • Interviste
    • Frontend
    • DevOps/Cloud
    • Linguaggi di programmazione
    • Soft Skill
  • Talent
    • Discover Talent
    • Jobs
    • Manifesto
  • Companies
  • For Business
    • EN
    • IT
    • ES
  • Sign in
ads

peduz91Aprile 23, 2024

Alla scoperta dei Design Pattern Strutturali: Fondamenta per un Codice Robusto

Linguaggi di programmazione
design pattern strutturali con java. Una guida. cell based architecture
facebooktwitterlinkedinreddit

Nel vasto panorama dello sviluppo del software, esistono strumenti e concetti fondamentali che ogni programmatore dovrebbe conoscere per scrivere codice efficiente e manutenibile. Tra questi, i Design Pattern Strutturali emergono come pilastri essenziali, offrendo un approccio sistematico alla composizione di classi e oggetti per creare strutture flessibili e scalabili.

I Design Pattern Strutturali rappresentano una serie di soluzioni progettuali ricorrenti per problemi comuni nell’organizzazione delle relazioni tra le entità software; essi si concentrano sul modo in cui classi e oggetti vengono composti per formare strutture più ampie, garantendo una separazione efficace delle responsabilità e promuovendo il riutilizzo del codice.

Recommended article
Maggio 7, 2025

Evitare la duplicazione del codice sfruttando le high-order function in JavaScript

Fabrizio Tedeschi

Fabrizio Tedeschi

Linguaggi di programmazione
Table Of Contents
  1. Modelli strutturali di classe e oggetti
  2. Esploriamo i design pattern strutturali in Java
    • Pattern Adapter
    • Pattern Composite
    • Pattern Decorator
  3. Semplicità ed importanza dei design pattern strutturali

Modelli strutturali di classe e oggetti

Iniziamo esplorando i modelli strutturali di classe, che si basano sull’ereditarietà per comporre interfacce o implementazioni, un esempio lampante di questo approccio è il pattern Adapter perché fornisce un’astrazione uniforme tra interfacce diverse, garantendo così una maggiore interoperabilità tra i componenti del software.


Passando ai modelli strutturali di oggetti, ci immergiamo nella composizione dinamica di oggetti per realizzare nuove funzionalità; per esempio, il pattern Composite, permette di costruire gerarchie di classi composte da oggetti primitivi e compositi, consentendo la creazione di strutture complesse in modo flessibile; il pattern Proxy, invece, agisce come un surrogato per un altro oggetto, permettendo di controllare l’accesso a risorse sensibili o di gestire la comunicazione con oggetti remoti.

Esploriamo i design pattern strutturali in Java

Ora che abbiamo una comprensione di base dei Design Pattern Strutturali, è il momento di esplorare come vengono implementati in Java.
Saranno proposti alcuni dei design pattern strutturali con del codice esemplificativo per poter spiegare l’utilità, l’efficacia e le funzionalità di questi strumenti davvero utili.

Pattern Adapter

Lo scopo è convertire un’interfaccia di una classe in un’altra interfaccia che i clients si aspettano, l’adapter consente di far lavorare insieme classi che altrimenti non potrebbero farlo a causa di interfacce incompatibili; di fatto funziona come un adattatore schuko per adattare un cavo che ha una spina fatta in un certo modo ad una presa che è stata pensata per altre spine.
Nell’esempio seguente, supponiamo di avere due librerie di terze parti con interfacce diverse ma funzionalità simili: utilizzando il pattern Adapter, possiamo creare un adattatore che renda uniforme l’interfaccia di entrambe le librerie, semplificando l’integrazione nel nostro codice.

public interface LibreriaUno {
    void metodoUno();
}

public interface LibreriaDue {
    void metodoDue();
}

public class Adattatore implements LibreriaUno {
    private LibreriaDue libreriaDue;

    public Adattatore(LibreriaDue libreriaDue) {
        this.libreriaDue = libreriaDue;
    }

    public void metodoUno() {
        libreriaDue.metodoDue();
    }
}

//Esempio di utilizzo
public class Main {
    public static void main(String[] args) {
        // Creazione di un'istanza della libreria uno
        LibreriaUno libreriaUno = new LibreriaUnoImpl();
        
        // Utilizzo diretto della libreria uno
        libreriaUno.metodoUno();

        // Creazione di un'istanza della libreria due
        LibreriaDue libreriaDue = new LibreriaDueImpl();
        
        // Utilizzo diretto della libreria due
        libreriaDue.metodoDue();

        // Utilizzo dell'adattatore per integrare la libreria due con l'interfaccia della libreria uno
        Adattatore adattatore = new Adattatore(libreriaDue);
        adattatore.metodoUno(); // Utilizzo dell'adattatore come se fosse la libreria uno
    }
}

Code language: PHP (php)

In questo esempio, abbiamo un metodo main che:

  1. Crea un’istanza della LibreriaUno e chiama il metodo metodoUno() direttamente su di essa.
  2. Crea un’istanza della LibreriaDue e chiama il metodo metodoDue() direttamente su di essa.
  3. Utilizza l’adattatore per integrare la LibreriaDue con l’interfaccia della LibreriaUno e chiama il metodo metodoUno() sull’adattatore, che di fatto esegue il metodo corrispondente della LibreriaDue.

In questo modo, l’adattatore permette di utilizzare la funzionalità della LibreriaDue come se fosse la LibreriaUno, facilitando l’integrazione di librerie con interfacce diverse nel tuo codice.

Pattern Composite

Il composite è un modello di progettazione strutturale che consente di comporre gli oggetti in strutture ad albero e di lavorare con queste strutture come se fossero oggetti singoli: Composite consente ai client di trattare in modo uniforme i singoli oggetti e le composizioni di oggetti.
Per l’esempio qui di seguito: immaginiamo di dover gestire una struttura gerarchica di documenti, dove sia possibile combinare documenti singoli in documenti composti, utilizzando il pattern Composite, possiamo rappresentare questa struttura in modo uniforme, semplificando l’operazione di navigazione e manipolazione.

public interface Documento {
    void stampa();
}

public class DocumentoSingolo implements Documento {
    public void stampa() {
        System.out.println("Stampa documento singolo");
    }
}

public class DocumentoComposto implements Documento {
    private List<Documento> documenti = new ArrayList<>();

    public void aggiungiDocumento(Documento documento) {
        documenti.add(documento);
    }

    public void stampa() {
        for (Documento documento : documenti) {
            documento.stampa();
        }
    }
}
//Esempio di utilizzo:
public class Main {
    public static void main(String[] args) {
        // Creazione di documenti singoli
        Documento documento1 = new DocumentoSingolo();
        Documento documento2 = new DocumentoSingolo();
        Documento documento3 = new DocumentoSingolo();

        // Creazione di un documento composto
        Documento documentoComposto = new DocumentoComposto();
        documentoComposto.aggiungiDocumento(documento1);
        documentoComposto.aggiungiDocumento(documento2);
        documentoComposto.aggiungiDocumento(documento3);

        // Chiamata al metodo stampa() sul documento composto
        documentoComposto.stampa();
    }
}
Code language: PHP (php)

In questo esempio, abbiamo un metodo main che:

  1. Crea tre documenti singoli (documento1, documento2, documento3) utilizzando la classe DocumentoSingolo.
  2. Crea un documento composto (documentoComposto) utilizzando la classe DocumentoComposto.
  3. Aggiunge i tre documenti singoli al documento composto utilizzando il metodo aggiungiDocumento().
  4. Chiama il metodo stampa() sul documento composto, che a sua volta chiama il metodo stampa() su ciascun documento singolo aggiunto, stampando così il contenuto di tutti i documenti nella struttura gerarchica.

In questo modo, il pattern Composite consente di trattare in modo uniforme sia i documenti singoli che i documenti composti, semplificando la gestione di strutture gerarchiche complesse nel tuo codice.

Pattern Decorator

Il pattern decorator fornisce un’alternativa alternativa flessibile all’ereditarietà per estendere le funzionalità di una classe; il decorator è un modello di progettazione strutturale che consente di associare nuovi comportamenti agli oggetti, collocandoli all’interno di speciali oggetti wrapper che contengono i comportamenti.
Immagina di avere un’interfaccia Componente che rappresenta un oggetto base e vuoi aggiungere diverse decorazioni a questo oggetto, come un bordo, un’ombra o altre caratteristiche, utilizzando il pattern Decorator, puoi creare una serie di classi Decoratore che estendono l’interfaccia Componente e aggiungono le funzionalità desiderate senza dover modificare direttamente l’oggetto base.

// Interfaccia Componente
public interface Componente {
    void disegna();
}

// Implementazione di base del componente
public class ComponenteBase implements Componente {
    @Override
    public void disegna() {
        System.out.println("Disegno del componente base");
    }
}

// Decoratore astratto
public abstract class Decoratore implements Componente {
    protected Componente componente;

    public Decoratore(Componente componente) {
        this.componente = componente;
    }

    @Override
    public void disegna() {
        componente.disegna();
    }
}

// Decoratore che aggiunge un bordo al componente
public class DecoratoreBordo extends Decoratore {
    public DecoratoreBordo(Componente componente) {
        super(componente);
    }

    @Override
    public void disegna() {
        super.disegna();
        aggiungiBordo();
    }

    private void aggiungiBordo() {
        System.out.println("Aggiunta di un bordo al componente");
    }
}

// Decoratore che aggiunge un'ombra al componente
public class DecoratoreOmbra extends Decoratore {
    public DecoratoreOmbra(Componente componente) {
        super(componente);
    }

    @Override
    public void disegna() {
        super.disegna();
        aggiungiOmbra();
    }

    private void aggiungiOmbra() {
        System.out.println("Aggiunta di un'ombra al componente");
    }
}

// Esempio sull'utilizzo
public class Main {
    public static void main(String[] args) {
        // Creazione di un componente base
        Componente componenteBase = new ComponenteBase();

        // Aggiunta di un bordo al componente base utilizzando il decoratore
        Componente componenteConBordo = new DecoratoreBordo(componenteBase);
        componenteConBordo.disegna();

        // Aggiunta di un'ombra al componente base utilizzando il decoratore
        Componente componenteConOmbra = new DecoratoreOmbra(componenteBase);
        componenteConOmbra.disegna();

        // Aggiunta di entrambi bordo e ombra al componente base
        Componente componenteConBordoEOmbra = new DecoratoreOmbra(new DecoratoreBordo(componenteBase));
        componenteConBordoEOmbra.disegna();
    }
}
Code language: PHP (php)

In questo esempio:

  • Componente rappresenta l’interfaccia di base per tutti i componenti.
  • ComponenteBase è l’implementazione di base di Componente.
  • Decoratore è una classe astratta che implementa Componente e contiene un riferimento a un altro oggetto Componente. Questo è il fulcro del pattern Decorator.
  • DecoratoreBordo e DecoratoreOmbra sono classi concrete che estendono Decoratore e aggiungono funzionalità specifiche.
  • Nel metodo main, creiamo un componente base e aggiungiamo dinamicamente bordi, ombre o entrambi utilizzando i decoratori.

In questo modo, il pattern Decorator ci permette di estendere le funzionalità di un oggetto in modo flessibile e modulare, mantenendo il codice pulito e facilmente manutenibile.

Semplicità ed importanza dei design pattern strutturali

In questo articolo abbiamo esplorato i Design Pattern Strutturali, scoprendo soluzioni eleganti per problemi comuni nello sviluppo del software, la loro semplicità d’uso e flessibilità li rende strumenti preziosi per scrivere codice leggibile e manutenibile: comprendere e utilizzare questi pattern è fondamentale per migliorare la qualità del proprio codice e diventare sviluppatori migliori.
Unisciti a noi nella prossima parte della nostra saga, dove esamineremo i Design Pattern Comportamentali, completando così il quadro delle best practice nello sviluppo del software.

Codemotion Collection Background
Dalla community
Selezionati per te

Vuoi scoprire più articoli come questo? Dai un’occhiata alla collection Dalla community dove troverai sempre nuovi contenuti selezionati dal nostro team.

Share on:facebooktwitterlinkedinreddit
peduz91
Sono uno sviluppatore software con forte passione per lo sviluppo, la tecnologia, il calcio e gli scacchi. Mi piace mettermi sempre in gioco, provo spesso cose nuove. Credo che la cosa più importante sia lavorare bene con il team.
10 sorprese e paradossi nell’industria dei giochi
Previous Post
Tutti i developer possono diventare manager? Un approccio basato sugli interessi particolari
Next Post

Footer

Discover

  • Events
  • Community
  • Partners
  • Become a partner
  • Hackathons

Magazine

  • Tech articles

Talent

  • Discover talent
  • Jobs

Companies

  • Discover companies

For Business

  • Codemotion for companies

About

  • About us
  • Become a contributor
  • Work with us
  • Contact us

Follow Us

© Copyright Codemotion srl Via Marsala, 29/H, 00185 Roma P.IVA 12392791005 | Privacy policy | Terms and conditions