Els patrons de disseny són solucions provades i documentades per a problemes comuns en el desenvolupament de programari. Aquests patrons ajuden a crear codi més organitzat, reutilitzable i mantenible. En aquest tema, explorarem alguns dels patrons de disseny més comuns i com implementar-los en Dart.
Continguts
Introducció als Patrons de Disseny
Els patrons de disseny es classifiquen en tres categories principals:
- Patrons Creacionals: Ajuden a crear objectes de manera que es controlin millor les seves creacions.
- Patrons Estructurals: Ajuden a compondre objectes i classes en estructures més grans.
- Patrons de Comportament: Ajuden a definir com els objectes interactuen i es comuniquen entre ells.
Patrons Creacionals
Singleton
El patró Singleton assegura que una classe només tingui una instància i proporciona un punt d'accés global a aquesta instància.
Exemple de Singleton en Dart
class Singleton { static final Singleton _instance = Singleton._internal(); factory Singleton() { return _instance; } Singleton._internal(); void someMethod() { print('Singleton method called'); } } void main() { var singleton1 = Singleton(); var singleton2 = Singleton(); print(singleton1 == singleton2); // true singleton1.someMethod(); }
Explicació
- _instance: Una instància estàtica de la classe.
- factory Singleton(): Un constructor de fàbrica que retorna la mateixa instància cada vegada.
- Singleton._internal(): Un constructor privat utilitzat per crear la instància única.
Factory Method
El patró Factory Method defineix una interfície per crear un objecte, però permet que les subclasses decideixin quina classe instanciar.
Exemple de Factory Method en Dart
abstract class Product { void operation(); } class ConcreteProductA implements Product { @override void operation() { print('Operation of ConcreteProductA'); } } class ConcreteProductB implements Product { @override void operation() { print('Operation of ConcreteProductB'); } } abstract class Creator { Product factoryMethod(); void someOperation() { var product = factoryMethod(); product.operation(); } } class ConcreteCreatorA extends Creator { @override Product factoryMethod() { return ConcreteProductA(); } } class ConcreteCreatorB extends Creator { @override Product factoryMethod() { return ConcreteProductB(); } } void main() { var creatorA = ConcreteCreatorA(); creatorA.someOperation(); var creatorB = ConcreteCreatorB(); creatorB.someOperation(); }
Explicació
- Product: Defineix la interfície dels objectes que el mètode de fàbrica crearà.
- ConcreteProductA i ConcreteProductB: Implementen la interfície Product.
- Creator: Declara el mètode de fàbrica que retorna un objecte de tipus Product.
- ConcreteCreatorA i ConcreteCreatorB: Implementen el mètode de fàbrica per crear instàncies de ConcreteProductA i ConcreteProductB respectivament.
Patrons Estructurals
Adapter
El patró Adapter permet que classes amb interfícies incompatibles treballin juntes.
Exemple d'Adapter en Dart
class Target { void request() { print('Target request'); } } class Adaptee { void specificRequest() { print('Adaptee specific request'); } } class Adapter extends Target { final Adaptee _adaptee; Adapter(this._adaptee); @override void request() { _adaptee.specificRequest(); } } void main() { var adaptee = Adaptee(); var adapter = Adapter(adaptee); adapter.request(); // Adaptee specific request }
Explicació
- Target: Defineix l'interfície que el client utilitza.
- Adaptee: Té una interfície incompatible amb Target.
- Adapter: Converteix la interfície d'Adaptee en una interfície compatible amb Target.
Decorator
El patró Decorator permet afegir comportament a objectes de manera dinàmica.
Exemple de Decorator en Dart
abstract class Component { void operation(); } class ConcreteComponent implements Component { @override void operation() { print('ConcreteComponent operation'); } } class Decorator implements Component { final Component _component; Decorator(this._component); @override void operation() { _component.operation(); } } class ConcreteDecoratorA extends Decorator { ConcreteDecoratorA(Component component) : super(component); @override void operation() { super.operation(); print('ConcreteDecoratorA operation'); } } class ConcreteDecoratorB extends Decorator { ConcreteDecoratorB(Component component) : super(component); @override void operation() { super.operation(); print('ConcreteDecoratorB operation'); } } void main() { var component = ConcreteComponent(); var decoratorA = ConcreteDecoratorA(component); var decoratorB = ConcreteDecoratorB(decoratorA); decoratorB.operation(); }
Explicació
- Component: Defineix la interfície per als objectes que poden tenir responsabilitats afegides dinàmicament.
- ConcreteComponent: Implementa la interfície Component.
- Decorator: Manté una referència a un objecte Component i implementa la interfície Component.
- ConcreteDecoratorA i ConcreteDecoratorB: Afegixen comportament addicional abans o després de cridar l'operació del component.
Patrons de Comportament
Observer
El patró Observer defineix una dependència un-a-molts entre objectes, de manera que quan un objecte canvia d'estat, tots els seus dependents són notificats i actualitzats automàticament.
Exemple d'Observer en Dart
abstract class Observer { void update(String state); } class ConcreteObserver implements Observer { final String _name; ConcreteObserver(this._name); @override void update(String state) { print('$_name received update: $state'); } } class Subject { final List<Observer> _observers = []; String _state; void attach(Observer observer) { _observers.add(observer); } void detach(Observer observer) { _observers.remove(observer); } void notify() { for (var observer in _observers) { observer.update(_state); } } void setState(String state) { _state = state; notify(); } } void main() { var subject = Subject(); var observer1 = ConcreteObserver('Observer 1'); var observer2 = ConcreteObserver('Observer 2'); subject.attach(observer1); subject.attach(observer2); subject.setState('State 1'); subject.setState('State 2'); }
Explicació
- Observer: Defineix una interfície per a objectes que han de ser notificats de canvis en un Subject.
- ConcreteObserver: Implementa la interfície Observer i actualitza el seu estat en resposta a notificacions.
- Subject: Manté una llista d'observers i els notifica quan el seu estat canvia.
Strategy
El patró Strategy defineix una família d'algoritmes, encapsula cada un d'ells i els fa intercanviables. Permet que l'algoritme variï independentment dels clients que l'utilitzen.
Exemple de Strategy en Dart
abstract class Strategy { void execute(); } class ConcreteStrategyA implements Strategy { @override void execute() { print('Executing strategy A'); } } class ConcreteStrategyB implements Strategy { @override void execute() { print('Executing strategy B'); } } class Context { Strategy _strategy; Context(this._strategy); void setStrategy(Strategy strategy) { _strategy = strategy; } void executeStrategy() { _strategy.execute(); } } void main() { var context = Context(ConcreteStrategyA()); context.executeStrategy(); context.setStrategy(ConcreteStrategyB()); context.executeStrategy(); }
Explicació
- Strategy: Defineix una interfície comuna per a tots els algoritmes.
- ConcreteStrategyA i ConcreteStrategyB: Implementen la interfície Strategy amb diferents algoritmes.
- Context: Manté una referència a un objecte Strategy i delega l'execució de l'algoritme a aquest objecte.
Exercicis Pràctics
- Implementa un Singleton: Crea una classe Logger que només permeti una instància i tingui un mètode per registrar missatges.
- Crea un Adapter: Implementa un adaptador per a una classe que té una interfície incompatible amb una interfície esperada.
- Utilitza el patró Observer: Crea un sistema de notificacions on diversos observadors reben actualitzacions quan l'estat d'un subjecte canvia.
Conclusió
Els patrons de disseny són eines poderoses que ajuden a resoldre problemes comuns en el desenvolupament de programari. En aquest tema, hem explorat alguns dels patrons més utilitzats i com implementar-los en Dart. Practicar aquests patrons t'ajudarà a escriure codi més net, mantenible i escalable.
Curs de Programació en Dart
Mòdul 1: Introducció a Dart
- Introducció a Dart
- Configuració de l'Entorn de Desenvolupament
- El Teu Primer Programa en Dart
- Sintaxi i Estructura Bàsica
Mòdul 2: Conceptes Bàsics de Dart
Mòdul 3: Col·leccions
Mòdul 4: Programació Orientada a Objectes en Dart
Mòdul 5: Funcionalitats Avançades de Dart
Mòdul 6: Gestió d'Errors i Depuració
Mòdul 7: Paquets i Biblioteques de Dart
Mòdul 8: Dart per a Web i Mòbil
- Introducció a Flutter
- Construcció d'una Aplicació Simple amb Flutter
- Dart per al Desenvolupament Web