#1 Design Patterns - Creational Design Patterns: Il Potere di Creare Oggetti con Stile
Nel corso della vita di uno sviluppatore, è probabile che si sia imbattuto almeno una volta nel concetto di "design pattern". Questi design pattern, sebbene possano sembrare inizialmente solo chiacchiere e tutta teoria, sono in realtà un elemento chiave nello sviluppo software. Rappresentano soluzioni ricorrenti a problemi comuni che si presentano durante la progettazione e l'implementazione del software e forniscono un insieme di linee guida e best practices che aiutano gli sviluppatori a scrivere codice più efficace, manutenibile ed estendibile.
I design pattern sono spesso suddivisi in quattro categorie principali in base alla loro funzione e al problema che cercano di risolvere:
- Creational Design Patterns: Questa categoria si concentra sulla creazione di oggetti e affrontano il problema di come istanziare gli oggetti in modo flessibile ed efficiente.
- Structural Design Patterns: Questi pattern riguardano la composizione delle classi o degli oggetti per formare strutture più complesse. Sono utilizzati per migliorare l'efficienza e la chiarezza del sistema.
- Behavioral Design Patterns: Questa categoria tratta delle interazioni tra gli oggetti e di come essi comunicano tra loro. I Behavioral Design Patterns definiscono come i ruoli e le responsabilità sono distribuiti tra gli oggetti.
Creational Design Patterns
I Creational Design Patterns sono una categoria di design pattern nel mondo dello sviluppo software che si concentrano sulla creazione degli oggetti. Questi pattern cercano di affrontare il problema di come istanziare oggetti in modo flessibile e efficiente. Analizziamo insieme i più comuni, mostrando anche alcune casistiche reali.
Singleton Pattern
Il Singleton Pattern è uno dei Creational Design Patterns più noti. Questo pattern garantisce la creazione di una singola istanza e ne fornisce un punto globale di accesso.
In Java, è spesso implementato in questo modo:
public class Singleton {
private static Singleton instance;
private Singleton() { }
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
Obiettivo
Immagina di sviluppare un videogioco in cui è necessario tenere traccia del punteggio del giocatore in modo coerente in tutto il gioco. Ogni volta che il giocatore guadagna punti, il punteggio dovrebbe essere aggiornato e reso disponibile da qualsiasi parte del gioco.
Soluzione
public class PlayerScore {
private static PlayerScore instance;
private int score = 0;
private PlayerScore() {
// Costruttore privato per evitare la creazione diretta di istanze
}
public static PlayerScore getInstance() {
if (instance == null) {
instance = new PlayerScore();
}
return instance;
}
public int getScore() {
return score;
}
public void increaseScore(int points) {
score += points;
}
}
Nel contesto di un videogioco, la classe PlayerScore è progettata come un Singleton. Ha un costruttore privato e un metodo getInstance che restituisce l'istanza esistente o ne crea una nuova se non ne esiste ancora una.
Ora puoi utilizzare il Singleton PlayerScore per gestire il punteggio del giocatore in tutto il gioco:
PlayerScore playerScore = PlayerScore.getInstance();
// In tutto il gioco, puoi incrementare il punteggio del giocatore come segue:
playerScore.increaseScore(10);
playerScore.increaseScore(5);
// Ottieni il punteggio corrente del giocatore da qualsiasi parte del gioco:
int currentScore = playerScore.getScore();
System.out.println("Punteggio attuale del giocatore: " + currentScore);
Factory Method Pattern
Il Factory Method Pattern è utilizzato quando si vuole delegare la creazione di oggetti alle sottoclassi. In Java, questo pattern può essere implementato come segue:
public interface Product {
void create();
}
public class ConcreteProductA implements Product {
@Override
public void create() {
// Implementazione specifica di ConcreteProductA
}
}
public class ConcreteProductB implements Product {
@Override
public void create() {
// Implementazione specifica di ConcreteProductB
}
}
public interface ProductFactory {
Product createProduct();
}
public class ConcreteProductFactoryA implements ProductFactory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
public class ConcreteProductFactoryB implements ProductFactory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
In questo modo, le classi ConcreteProductFactoryA e ConcreteProductFactoryB sono responsabili della creazione di istanze di Product specifiche.
Obiettivo
Immagina di sviluppare un videogioco in cui ci sono diversi tipi di personaggi, come guerrieri e maghi, e ogni tipo di personaggio ha comportamenti e abilità uniche. È necessario creare istanze di personaggi in base al tipo selezionato in modo flessibile.
Soluzione
// Interfaccia per i personaggi del gioco
public interface Character {
void attack();
void defend();
}
// Implementazione di un guerriero
public class Warrior implements Character {
@Override
public void attack() {
System.out.println("Il guerriero attacca con la spada.");
}
@Override
public void defend() {
System.out.println("Il guerriero si difende con uno scudo.");
}
}
// Implementazione di un mago
public class Mage implements Character {
@Override
public void attack() {
System.out.println("Il mago utilizza una magia offensiva.");
}
@Override
public void defend() {
System.out.println("Il mago evoca una barriera difensiva.");
}
}
// Interfaccia Factory per creare personaggi
public interface CharacterFactory {
Character createCharacter();
}
// Factory per i guerrieri
public class WarriorFactory implements CharacterFactory {
@Override
public Character createCharacter() {
return new Warrior();
}
}
// Factory per i maghi
public class MageFactory implements CharacterFactory {
@Override
public Character createCharacter() {
return new Mage();
}
}
Ora puoi utilizzare il Factory Method Pattern per creare diversi tipi di personaggi del gioco in modo flessibile:
CharacterFactory factory = new WarriorFactory(); // Cambia la factory per creare
// guerrieri o maghi
Character character = factory.createCharacter();
character.attack(); // Output: Il guerriero attacca con la spada o
// Il mago utilizza una magia offensiva
character.defend(); // Output: Il guerriero si difende con uno scudo o
// Il mago evoca una barriera difensiva
Abstract Factory Pattern
L'Abstract Factory Pattern fornisce un'interfaccia per creare famiglie di oggetti correlati senza specificarne le classi concrete. In Java, questo può essere realizzato come segue:
public interface AbstractFactory {
ProductA createProductA();
ProductB createProductB();
}
public class ConcreteFactory1 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ConcreteProductA1();
}
@Override
public ProductB createProductB() {
return new ConcreteProductB1();
}
}
public class ConcreteFactory2 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ConcreteProductA2();
}
@Override
public ProductB createProductB() {
return new ConcreteProductB2();
}
}
In questo esempio, ConcreteFactory1 e ConcreteFactory2 creano diverse famiglie di prodotti, ciascuna con le proprie implementazioni.
Obiettivo
Immagina di sviluppare un videogioco in cui ci sono diverse fazioni di personaggi, come Orchi e Elfi, ciascuna con i propri tipi di personaggi e oggetti del gioco. È necessario creare istanze di personaggi e oggetti del gioco che siano correlati alla fazione selezionata in modo flessibile.
Soluzione
// Interfaccia per i personaggi del gioco
public interface Character {
void display();
}
// Implementazione di un guerriero elfo
public class ElfWarrior implements Character {
@Override
public void display() {
System.out.println("Elfo guerriero.");
}
}
// Implementazione di un mago elfo
public class ElfMage implements Character {
@Override
public void display() {
System.out.println("Elfo mago.");
}
}
// Implementazione di un guerriero orco
public class OrcWarrior implements Character {
@Override
public void display() {
System.out.println("Orco guerriero.");
}
}
// Implementazione di un mago orco
public class OrcMage implements Character {
@Override
public void display() {
System.out.println("Orco mago.");
}
}
// Interfaccia per gli oggetti del gioco
public interface GameObject {
void use();
}
// Implementazione di un'arma elfica
public class ElvenWeapon implements GameObject {
@Override
public void use() {
System.out.println("Arma elfica utilizzata.");
}
}
// Implementazione di un'arma orca
public class OrcishWeapon implements GameObject {
@Override
public void use() {
System.out.println("Arma orca utilizzata.");
}
}
// Factory astratta per creare famiglie di personaggi e oggetti correlati
public interface GameFactory {
Character createCharacter();
GameObject createWeapon();
}
// Factory per gli Elfi
public class ElfFactory implements GameFactory {
@Override
public Character createCharacter() {
return new ElfMage();
}
@Override
public GameObject createWeapon() {
return new ElvenWeapon();
}
}
// Factory per gli Orchi
public class OrcFactory implements GameFactory {
@Override
public Character createCharacter() {
return new OrcWarrior();
}
@Override
public GameObject createWeapon() {
return new OrcishWeapon();
}
}
Ora puoi utilizzare l'Abstract Factory Pattern per creare famiglie di personaggi e oggetti del gioco correlati in modo flessibile, in base alla fazione selezionata:
GameFactory factory = new OrcFactory(); // Cambia la factory per creare personaggi e
// oggetti correlati delle diverse fazioni
Character character = factory.createCharacter();
character.display(); // Output: Orco guerriero o Elfo mago
GameObject weapon = factory.createWeapon();
weapon.use(); // Output: Arma orca utilizzata o Arma elfica utilizzata
Conclusione
I Creational Design Patterns in Java offrono approcci strutturati per gestire la creazione degli oggetti in un'applicazione. Quando si sceglie un Creational Design Pattern, è importante considerare le esigenze specifiche del progetto e l'obiettivo di progettazione. Con una corretta implementazione, questi pattern possono semplificare notevolmente la creazione di oggetti complessi.
Commenti
Posta un commento