Observerパターンについて軽く
Observer
は観測者という意味だが、どちらかというと観測される側(Subject
)から観測者へ、どう通知するかについてまとめられたパターン。
通知側(Subject)で観測対象の事象が発生したら、観測側(Observer)へ通知(notifyObservers())し、観測側の処理を開始する(update())。
通知側と観測側を分離することによって、事象の発生とその事象に対してどう振る舞うか、が分離されるのがメリット。
サンプルとして何を作ったか
新しくFlutterプロジェクトを作成するときに自動生成されるカウンターアプリを元に、カウントがインクリメントされたらファイル書き込みとDB書き込みするように変更し、その通知をObserverパターンを用いて行う。
(サンプルコードとしてはファイル書き込み、DB書き込みを行った、というていのログのみ記載)
クラス図
コード
GitHub に完全版があります。
observer.dart
abstract class Observer {
void update(int counter);
}
subject.dart
import 'package:flutter_observer/utils/observer.dart';
abstract class Subject {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
file_writer.dart
import 'package:flutter_observer/utils/observer.dart';
class FileWriter implements Observer {
@override
void update(counter) {
// とりあえずログ記載のみ行う
print('$counter をファイルに書き込み');
}
}
db_writer.dart
import 'package:flutter_observer/utils/observer.dart';
class DbWriter implements Observer {
@override
void update(counter) {
// とりあえずログ記載のみ行う
print('$counter をDBに書き込み');
}
}
my_home_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_observer/utils/db_writer.dart';
import 'package:flutter_observer/utils/file_writer.dart';
import 'package:flutter_observer/utils/observer.dart';
import 'package:flutter_observer/utils/subject.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> implements Subject {
int _counter = 0;
final List<Observer> _observers = [];
@override
void addObserver(Observer observer) {
_observers.add(observer);
}
@override
void removeObserver(Observer observer) {
_observers.remove(observer);
}
@override
void notifyObservers() {
for (final observer in _observers) {
observer.update(_counter);
}
}
@override
void initState() {
addObserver(FileWriter());
addObserver(DbWriter());
super.initState();
}
void _incrementCounter() {
setState(() {
_counter++;
notifyObservers();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
以上