Exploring Reactive Design Patterns in Dart Programming

20 Jul 2023 Balmiki Mandal 0 Dart Programming

Mastering Reactivity: Exploring Reactive Design Patterns in Dart Programming

Reactive programming has become a popular paradigm for building dynamic and responsive applications, and Dart, with its robust asynchronous capabilities and rich ecosystem, offers an excellent platform to leverage this approach. This guide explores some key reactive design patterns and their implementation in Dart:

1. The Observer Pattern:

  • Concept: Allows objects to subscribe to changes in another object (observable) and be notified automatically when the observable's state changes.
  • Implementation:
Dart
class Subject {
  final _listeners = <Function>[];

  void addListener(Function listener) {
    _listeners.add(listener);
  }

  void notifyListeners() {
    for (var listener in _listeners) {
      listener();
    }
  }
}

class MyObservable extends Subject {
  int _value = 0;

  int get value => _value;

  void increment() {
    _value++;
    notifyListeners();
  }
}

void main() {
  final observable = MyObservable();
  observable.addListener(() {
    print('Value changed: ${observable.value}');
  });

  observable.increment(); // Prints: Value changed: 1
}
 

2. The Pub/Sub Pattern:

  • Concept: Enables communication between loosely coupled components using topics (channels) for publishing and subscribing to messages.
  • Implementation (using dart:isolate):
Dart
final channel = Isolate.spawn((port) {
  port.receive((message) {
    print('Received message: $message');
  });
});

channel.send('Hello from main isolate!');

3. The Circuit Breaker Pattern:

  • Concept: Protects systems from cascading failures by automatically stopping requests to a failing service and retrying after a defined period.
  • Implementation (using package:rxdart):
Dart
import 'package:rxdart/rxdart.dart';

Stream<String> fetchData() async* {
  // Simulate potential network error
  if (Random().nextBool()) {
    throw Exception('Network error');
  }
  yield 'Data from server';
}

Stream<String> fetchDataWithCircuitBreaker() {
  return RetryWhenStream(
    fetchData(),
    (errorEvents, retryCount) => retryCount < 3
        ? TimerStream(Duration(seconds: 1), retryCount + 1)
        : throw Exception('Service unavailable'),
  );
}

void main() async {
  fetchDataWithCircuitBreaker().listen((data) {
    print(data);
  }, onError: (error) {
    print(error);
  });
}

4. The Subject on Next Pattern:

  • Concept: Filters values emitted by an observable and only allows subsequent emissions if a specific condition is met.
  • Implementation (using package:rxdart):
Dart
import 'package:rxdart/rxdart.dart';

Stream<int> generateNumbers() async* {
  for (var i = 1; i <= 10; i++) {
    await Future.delayed(Duration(milliseconds: 500));
    yield i;
  }
}

Stream<int> onlyEvenNumbers() {
  return generateNumbers().where((number) => number % 2 == 0);
}

void main() async {
  onlyEvenNumbers().listen((number) {
    print(number); // Prints: 2, 4, 6, 8, 10
  });
}

These are just a few examples, and several other reactive design patterns exist, each with its own purpose and benefits. By understanding these patterns and exploring available libraries like rxdart, you can effectively build reactive applications in Dart that are more responsive, resilient, and easier to maintain.

 

Remember: Choosing the right pattern depends on your specific needs and the complexity of your application. Always consider the trade-offs between different approaches and prioritize code clarity and maintainability when implementing these patterns.

BY: Balmiki Mandal

Related Blogs

Post Comments.

Login to Post a Comment

No comments yet, Be the first to comment.