CIAO! Ora continuiamo ad approfondire un argomento utile ampio e super importante: i modelli di progettazione. Oggi parliamo del pattern del ponte. Come altri modelli, il modello bridge serve a risolvere i problemi tipici che uno sviluppatore incontra durante la progettazione dell'architettura software. Oggi studiamo le caratteristiche di questo pattern e scopriamo come utilizzarlo.

Qual è il modello del ponte?

Il modello a ponte è un modello di progettazione strutturale. In altre parole, il suo compito principale è creare una struttura completa di classi e oggetti. Un bridge lo fa dividendo una o più classi in gerarchie separate: astrazione e implementazione . Un cambiamento di funzionalità in una gerarchia non comporta un cambiamento nell'altra. Va tutto bene, ma questa definizione è molto ampia e non risponde alla domanda più importante: "Qual è lo schema del ponte?" Penso che sarà più facile per te capire la sua applicazione pratica. Quindi, subito, creiamo uno scenario classico per il modello del ponte. Abbiamo una Shapeclasse astratta, che rappresenta una figura geometrica generica:
  • Forma.java

    
    public abstract class Shape {
       public abstract void draw();
    }
    

    Quando decidiamo di aggiungere forme come triangoli e rettangoli, faremo ereditare loro la Shapeclasse:

  • Rettangolo.java:

    
    public class Rectangle extends Shape {
       @Override
       public void draw() {
           System.out.println("Drawing rectangle");
       }
    }
    
  • Triangolo.java:

    
    public class Triangle extends Shape {
       @Override
       public void draw() {
           System.out.println("Drawing triangle");
       }
    }
    
Tutto sembra semplice fino al momento in cui introduciamo il concetto di colore. Cioè, ogni forma avrà il suo colore e la funzionalità del draw()metodo dipenderà da questo colore. Per avere diverse implementazioni del draw()metodo, allora dobbiamo creare una classe per ogni combinazione forma-colore. Se abbiamo tre colori, abbiamo bisogno di sei classi : TriangleBlack, TriangleGreen, TriangleRed, e . Sei lezioni non sono un grosso problema. Ma! Se dobbiamo aggiungere una nuova forma o colore, il numero di classi cresce in modo esponenziale. Come uscire da questa situazione? Memorizzare il colore in un campo ed enumerare tutte le opzioni utilizzando istruzioni condizionali non è la soluzione migliore. Una buona soluzione è spostare il colore su un'interfaccia separataRectangleBlackRectangleGreenRectangleRed. Detto fatto: creiamo un'interfaccia Colorcon tre implementazioni: BlackColor, GreenColore RedColor:
  • Colore.java:

    
    public interface Color {
       void fillColor();
    }
    
  • BlackColor.java:

    
    public class BlackColor implements Color {
       @Override
       public void fillColor() {
           System.out.println("Filling in black color");
       }
    }
    
  • GreenColor.java

    
    public class GreenColor implements Color {
       @Override
       public void fillColor() {
           System.out.println("Filling in green color");
       }
    }
    
  • RedColor.java

    
    public class RedColor implements Color {
       @Override
       public void fillColor() {
           System.out.println("Filling in red color");
       }
    }
    

    Ora aggiungiamo un Colorcampo alla Shapeclasse. Otterremo il suo valore nel costruttore.

  • Shape.java:

    
    public abstract class Shape {
       protected Color color;
      
       public Shape(Color color) {
           this.color = color;
       }
    
       public abstract void draw();
    }
    

    Useremo la colorvariabile nelle Shapeimplementazioni. Ciò significa che le forme possono ora utilizzare la funzionalità dell'interfaccia Color.

  • Rectangle.java

    
    public class Rectangle extends Shape {
    
       public Rectangle(Color color) {
           super(color);
       }
    
       @Override
       public void draw() {
           System.out.println("Drawing rectangle");
           color.fillColor();
       }
    }
    
Ta-da! Ora possiamo creare diversi colori e forme all'infinito e il numero di classi aumenterà solo linearmente. Il Color colorcampo è un ponte che collega due gerarchie di classi separate.

Come costruire un ponte: astrazione e implementazione

Diamo un'occhiata a un diagramma di classe che rappresenta il pattern bridge: Presentazione del modello di progettazione del ponte - 2Qui puoi vedere due strutture indipendenti che possono essere modificate senza influire sulla funzionalità l'una dell'altra. Nel nostro caso:
  • L'astrazione è la Shapeclasse
  • RefinedAbstraction sono le classi TriangleeRectangle
  • L'implementatore è l' Colorinterfaccia
  • ConcreteImplementor è il BlackColor, GreenColore RedColorle classi.
La Shapeclasse è un'astrazione — un meccanismo per gestire il riempimento delle forme con vari colori, che delega all'interfaccia Color(Implementor). Le classi Trianglee Rectanglesono classi concrete che utilizzano il meccanismo messo a disposizione dalla Shapeclasse. BlackColore GreenColorsono RedColorimplementazioni concrete nella gerarchia di implementazione.

Dove usare il motivo a ponte

Un enorme vantaggio dell'utilizzo di questo modello è che è possibile apportare modifiche alle classi funzionali in una gerarchia senza interrompere la logica dell'altra. Inoltre, questo approccio aiuta a ridurre l'accoppiamento tra le classi. Il requisito principale quando si utilizza questo modello è "seguire le istruzioni": non ignorarne nessuno! A tal fine, cerchiamo di capire le situazioni in cui dovresti assolutamente usare il pattern bridge:
  1. Se hai bisogno di espandere il numero di entità basate su combinazioni di due concetti (es. forme e colori).

  2. Se si desidera dividere una classe numerosa che non soddisfa il principio della responsabilità singola in classi più piccole con funzionalità limitate.

  3. Se è necessario apportare modifiche alla logica di alcune entità mentre il programma è in esecuzione.

  4. Se è necessario nascondere un'implementazione ai client della classe o della libreria.

Quando usi questo modello, ricorda sempre che aggiunge entità aggiuntive al tuo codice: potrebbe non avere senso usarlo in un progetto in cui c'è solo una forma e uno o due colori possibili.

Pro e contro del modello

Come altri modelli, un ponte presenta sia vantaggi che svantaggi. Vantaggi del modello a ponte:
  1. Migliora la scalabilità del codice: puoi aggiungere funzionalità senza timore di rompere qualcosa in un'altra parte del programma.
  2. Riduce il numero di sottoclassi quando il numero di entità sarebbe altrimenti basato su combinazioni di due concetti (ad esempio, forme e colori).
  3. Consente di lavorare separatamente su due gerarchie separate: astrazione e implementazione. Due diversi sviluppatori possono apportare modifiche senza approfondire i dettagli del codice dell'altro.
  4. Riduce l'accoppiamento tra classi: l'unico posto in cui le due classi sono accoppiate è il bridge (cioè il Color colorcampo).
Svantaggi del modello a ponte:
  1. A seconda della situazione specifica e della struttura complessiva di un progetto, potrebbe influire negativamente sulle prestazioni di un programma (ad esempio, se è necessario inizializzare più oggetti).
  2. Rende il codice meno leggibile a causa della necessità di passare da una classe all'altra.

Differenza dal modello di strategia

Il modello del ponte viene spesso confuso con un altro modello di progettazione: la strategia. Entrambi usano la composizione (sebbene abbiamo usato l'aggregazione nell'esempio con figure e colori, anche il modello Bridge può usare la composizione), delegando il lavoro ad altri oggetti. Ma c'è una differenza tra loro, ed è enorme. Il modello strategico è un modello comportamentale: risolve problemi completamente diversi. La strategia consente di scambiare gli algoritmi, mentre il bridge separa un'astrazione dalle implementazioni per scegliere tra diverse implementazioni. In altre parole, a differenza di una strategia, un ponte si applica a intere entità o strutture gerarchiche. Il modello bridge può essere una buona arma nell'arsenale di uno sviluppatore. La cosa principale è identificare le situazioni in cui vale la pena usarlo e riconoscere quando qualche altro schema è appropriato.