【Flutter/Dart】排他制御 synchronizedパッケージ
asoacasio
プログラマーの裏舞台
Dart言語でStrategyパターンを説明します。
ある目的を達成するための戦略がいくつかあり、それらをいつでも交換できるように設計するのがStrategyパターンです。
Strategy(ストラテジー)は日本語に翻訳すると、戦略になります。
データのサイズや特性によって、最適なソートアルゴリズムが異なる場合があります。
例えば、クイックソート、マージソート、バブルソートなどをそれぞれストラテジーとして実装し、実行時の状況に応じて最適なソートストラテジーを選択することができます。
通信環境や要件によって、最適な通信プロトコルが異なる場合があります。
例えば、HTTP、FTP、SMTPなどをそれぞれのストラテジーとして実装し、実行時に状況に応じて最適な通信方式に変更することができます。
Strategyパターンのイメージが付きやすいように、ウサギが川を渡る例でサンプルコードを書いてみます。
ウサギが川を渡らなければいけない問題に直面しました。
川を渡る方法はいくつかあります。
どの戦略が最適かは、体調・時間・天候などに依ります。
状況に応じて最適な戦略を選ぶことがStrategyパターンの考え方です。
abstract class Strategy {
String execute();
}
// 泳いで渡る戦略
class SwimStrategy implements Strategy {
@override
String execute() {
return 'ウサギは泳ぎました';
}
}
// 橋を渡る戦略
class BridgeStrategy implements Strategy {
@override
String execute() {
return 'ウサギは橋を渡りました';
}
}
// 浅瀬を渡る戦略
class FordStrategy implements Strategy {
@override
String execute() {
return 'ウサギは浅瀬を渡りました';
}
}
class Rabbit {
final Strategy strategy;
Rabbit(this.strategy);
String crossRiver() {
return strategy.execute();
}
}
void main() {
final rabbit1 = Rabbit(SwimStrategy());
log(rabbit1.crossRiver()); // [log] ウサギは泳ぎました
final rabbit2 = Rabbit(BridgeStrategy());
log(rabbit2.crossRiver()); // [log] ウサギは橋を渡りました
final rabbit3 = Rabbit(FordStrategy());
log(rabbit3.crossRiver()); // [log] ウサギは浅瀬を渡りました
}
ウサギが川を渡る戦略を動的に切り替えてみます。
これらの判断で戦略を選びます。
状況クラスを作成しました。
// 状況
class Situation {
final bool isHealthy; // 体調が良いか
final bool hasTime; // 時間があるか
final bool isGoodWeather; // 天気が良いか
Situation(
{this.isHealthy = false,
this.hasTime = false,
this.isGoodWeather = false});
Rabbit selectStrategy() {
// 体調が良く、時間がある場合は橋を渡る
if (isHealthy && hasTime) {
return Rabbit(BridgeStrategy());
// 時間がないけど体調が良ければ泳ぐ
} else if (isHealthy && !hasTime) {
return Rabbit(SwimStrategy());
// 天気が良い場合に限り、浅瀬を探す
} else if (isGoodWeather) {
return Rabbit(FordStrategy());
} else {
return Rabbit(SwimStrategy());
}
}
}
クライアントコードで状況に応じて戦略を変えるようにコードを書いてみます。
void main() {
// 体調が良い・時間がある・天気が悪い
Situation situation1 =
Situation(isHealthy: true, hasTime: true, isGoodWeather: false);
Rabbit rabbit1 = situation1.selectStrategy();
log(rabbit1.crossRiver()); // [log] ウサギは橋を渡りました
// 体調が悪い・時間がない・天気が良い
Situation situation2 =
Situation(isHealthy: false, hasTime: false, isGoodWeather: true);
Rabbit rabbit2 = situation2.selectStrategy();
log(rabbit2.crossRiver()); // [log] ウサギは浅瀬を渡りました
// 体調が良い・時間がない・天気が悪い
Situation situation3 =
Situation(isHealthy: true, hasTime: false, isGoodWeather: false);
Rabbit rabbit3 = situation3.selectStrategy();
log(rabbit3.crossRiver()); // [log] ウサギは泳ぎました
}
状況に応じて柔軟に戦略を変えれることが分かります。
またStrategyクラスとRabbitクラスは疎結合になります。
つまり、片方を変更してももう一方への影響が低減します。特に新たな戦略を追加する場合、Rabbitクラスを修正する必要なく、新たなStrategyインターフェースの実装クラスを追加するだけで良いです。