Pattern Decorator
Fa parte della sezione Programmazione orientata agli oggetti del percorso Dart di Coddy. Lezione 96 di 110.
Il pattern Decorator ti permette di aggiungere dinamicamente nuovi comportamenti agli oggetti avvolgendoli in oggetti speciali chiamati decoratori. A differenza dell'ereditarietà, che aggiunge comportamenti in fase di compilazione, i decoratori assegnano responsabilità aggiuntive in fase di esecuzione senza modificare la classe originale.
L'idea chiave è che sia l'oggetto originale sia i decoratori implementano la stessa interfaccia.
Un decoratore avvolge l’oggetto originale, delega a esso e aggiunge il proprio comportamento prima o dopo:
abstract class Coffee {
String getDescription();
double getCost();
}
class SimpleCoffee implements Coffee {
@override
String getDescription() => 'Simple Coffee';
@override
double getCost() => 2.0;
}
class MilkDecorator implements Coffee {
final Coffee _coffee;
MilkDecorator(this._coffee);
@override
String getDescription() => '${_coffee.getDescription()}, Milk';
@override
double getCost() => _coffee.getCost() + 0.5;
}
class SugarDecorator implements Coffee {
final Coffee _coffee;
SugarDecorator(this._coffee);
@override
String getDescription() => '${_coffee.getDescription()}, Sugar';
@override
double getCost() => _coffee.getCost() + 0.2;
}
void main() {
Coffee coffee = SimpleCoffee();
coffee = MilkDecorator(coffee);
coffee = SugarDecorator(coffee);
print(coffee.getDescription()); // Caffè semplice, latte, zucchero
print(coffee.getCost()); // 2.7
}Ogni decoratore avvolge l'oggetto precedente e lo arricchisce. Puoi concatenare più decoratori in qualsiasi combinazione, creando un comportamento flessibile senza creare una sottoclasse per ogni possibile combinazione. Questo pattern è ideale quando devi aggiungere funzionalità agli oggetti senza influire su altri oggetti della stessa classe.
Sfida
FacileCostruiamo un sistema per ordinare pizze usando il pattern Decorator! Creerai una pizza di base che può essere arricchita con vari condimenti, in cui ogni condimento avvolge l’elemento precedente e contribuisce sia alla descrizione sia al prezzo.
Organizzerai il codice in due file:
pizza.dart: questo file contiene l’interfaccia della pizza e tutte le classi decorator. Inizia con una classe astrattaPizzache definisce due metodi:String getDescription()edouble getPrice(). Poi crea una classePlainPizzache implementaPizza: restituiscePlain Pizzacome descrizione e costa8.0. Ora crea tre classi decorator, ognuna delle quali avvolge unaPizzae la arricchisce:CheeseDecorator- aggiunge, Cheesealla descrizione e1.5al prezzoPepperoniDecorator- aggiunge, Pepperonialla descrizione e2.0al prezzoMushroomDecorator- aggiunge, Mushroomsalla descrizione e1.25al prezzo
Pizza, accettare unaPizzanel proprio costruttore e delegare alla pizza avvolta prima di aggiungere il proprio contributo.main.dart: importa il file della pizza e mostra come i decorator si combinano per creare un ordine personalizzato. Inizia con unaPlainPizza, poi avvolgila conCheeseDecorator, quindi avvolgi il risultato conPepperoniDecoratore infine avvolgi tutto conMushroomDecorator. Stampa la descrizione della pizza finale, poi stampaTotal: $seguito dal prezzo.
Nota come ogni decorator si basa sul precedente: stai creando una pizza ricca di condimenti combinando decorator semplici, ciascuno con una singola responsabilità!
Output previsto:
Plain Pizza, Cheese, Pepperoni, Mushrooms
Total: $12.75Provalo tu
import 'pizza.dart';
void main() {
// TODO: Crea una PlainPizza
// TODO: Avvolgila con CheeseDecorator
// TODO: Avvolgi il risultato con PepperoniDecorator
// TODO: Avvolgi il tutto con MushroomDecorator
// TODO: Stampa la descrizione della pizza finale
// TODO: Stampa "Total: $" seguito dal prezzo
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniLibrerie e importazioniIntroduzione alla programmazione orientata agli oggettiClassi e oggettiLa parola chiave thisMetodiVariabili di istanzaFondamenti dei costruttoriRiepilogo - Calcolatrice semplice4Sicurezza dei valori null
Introduzione alla sicurezza dei valori nullNullable e non nullableGli operatori ? e !La keyword late e la sicurezza dei valori nullOperatori null-awareSicurezza dei valori null nelle classiRiepilogo - Sistema di profili utente7Classi astratte e interfacce
Classi astratteMetodi astrattiInterfacce in DartInterfacce impliciteImplementare vs estendereInterfacce multipleRiepilogo - Calcolatore di forme10Collezioni e generici
Panoramica di List, Set e MapCollezioni sicure rispetto ai tipiClassi genericheMetodi genericiVincoli sui genericiIterable e IteratorRiepilogo - Archiviazione generica13Concetti avanzati di OOP
Composizione vs ereditarietàMetodi di estensioneClassi chiamabiliClassi sealed (Dart 3)Record (Dart 3)Pattern e matching (3.0)Enum con metodi16Progetto: Gestione della biblioteca
Panoramica del progettoClassi Libro e Utente2I costruttori in Dart
Costruttore predefinitoCostruttori con nomeListe di inizializzazioneCostruttori costantiCostruttori factoryCostruttori di reindirizzamentoRiepilogo - Crea forme5Incapsulamento
Membri pubblici e privatiConvenzione del prefisso _Visibilità a livello di libreriaApprofondimento su getter e setterNascondimento delle informazioniRiepilogo - Registri degli studenti8Mixin
Introduzione ai MixinCreare MixinUsare più MixinParola chiave on nei MixinMixin vs ereditarietàMixin vs interfacciaRiepilogo - Sistema animale11Metodi speciali
Sovrascrittura di toString()Sovrascrittura di hashCode e ==Interfaccia ComparableMetodo call()Sovrascrittura di noSuchMethodRiepilogo - Collezione personalizzata14Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneSingletonFactoryObserverStrategy3Proprietà delle classi
Membri di istanza e staticiCampi final e constVariabili lateMetodi e campi staticiGetter e setterRiepilogo - Gestore di conti bancari6Ereditarietà
Ereditarietà di baseLa parola chiave superOverride dei metodiL’annotazione @overrideLa parola chiave final per le classiCostruttori ed ereditarietàRiepilogo - Gerarchia dei dipendenti9Polimorfismo
Fondamenti di polimorfismoPolimorfismo tramite interfacceControllo del tipo a runtimeGli operatori is e asParola chiave covariantRiepilogo - Elaboratore di pagamenti12OOP asincrona
Future e async/awaitNozioni di base sugli streamController degli streamCostruttori asincroniAsync nei metodi di classeRiepilogo - Recupero dei dati15Pattern di progettazione Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositePattern RepositoryEsercitati da solo: Compilatore Dart online