導入
今回は、roadmap.shのFlutterのDesign Principles
を進めていきます。
Flutter開発において重要なデザイン原則、依存性注入、デザインパターン、SOLID原則、そしてオブジェクト指向プログラミング(OOP)などの基本的な概念を学習していきます。これらの概念を深く理解し、適切に活用することで、より良いアプリケーションを開発し、保守性とパフォーマンスを向上させることができます。
デザイン原則
以下は、Flutter開発においてよく従われる一般的なデザイン原則です。
- マテリアルデザイン: Googleが開発したデザインシステムで、すべてのプラットフォームおよびデバイスで一貫した外観と操作感を提供します。
- レスポンシブデザイン: 異なる画面サイズやアスペクト比でシームレスに動作するユーザーインターフェースを設計すること。
- ユーザー中心設計: ユーザーのニーズや目標を考慮してアプリケーションを設計すること。
- シンプルさ: タスクを完了するための手順を最小限に抑え、シンプルで直感的なユーザーインターフェースを設計すること。
- 一貫性: 一貫した色、タイポグラフィ、レイアウトを使用して、アプリケーション全体で一貫した外観と操作感を維持すること。
- アクセシビリティ: スクリーンリーダーのサポート、キーボードアクセシビリティ、高コントラストモードなど、障害を持つ人々が利用できるようにアプリケーションを設計すること。
- パフォーマンス: 低スペックのデバイスでも高速で応答性のあるアプリケーションを設計すること。
これらのデザイン原則に従うことで、ユーザーフレンドリーで視覚的に魅力的なアプリケーションを作成し、優れたユーザーエクスペリエンスを提供することができます。
Dependency Injection
参考:https://medium.com/flutter-community/dependency-injection-in-flutter-f19fb66a0740
Dependency Injection(依存性注入, DI) は、デザインパターンの一つです。クラスの外部で依存オブジェクトを作成し、それらのオブジェクトをクラスに提供することが可能になります。
DI を使用すると、依存オブジェクトの作成とバインドを、それらに依存するクラスの外部に移すことができるのです。
これにより、柔軟性、分離、およびテストの容易さが向上します。
FlutterにおけるDI
ライブラリを用いて実装することが多いようです。
ただ、ライブラリの多くはリフレクションという機構を利用しています。これはDartではミラーと言いますが、以下の2点の問題点を持っています。
- パフォーマンスの都合上、ミラーはデフォルトでDisableになっている
- ネストが深いWidgetに対して、依存関係を渡していくのは現実的ではない
では、どうやって解消するか。
Inject
ライブラリを使用する方法があります。
Inject
ライブラリのアノテーション
Inject
ライブラリには以下のアノテーションが存在します。
-
@Injector
- ビルダーまたはモジュールのセットから構成された制御の反転コンテナ
-
@Module
&@Provides
- 依存関係を提供するクラスとメソッドを定義するもの
-
@Component
- 選択したモジュールを有効にし、依存性注入を実行するもの
Inject
ライブラリの使用例
クラスの注入
import 'package:inject/inject.dart';
@provide
class StepService {
// implementation
}
StepServiceを利用するクラス
@provide
class SomeWidget extends StatelessWidget {
final StepService _service;
SomeWidget(this._service);
}
これで、SomeWidgetを実装する際にStepServiceを引数で渡すことでインジェクションが可能になります。
IF注入
以下のような抽象クラスに対して、注入を行う例になります。
abstract class UserRepository {
Future<List<User>> allUsers();
}
// 抽象クラスの実装
class FirestoreUserRepository implements UserRepository {
@override
Future<List<User>> allUsers() {
// implementation
}
}
以下のように注入ができます。
import 'package:inject/inject.dart';
@module
class UsersServices {
@provide
UserRepository userRepository() => FirestoreUserRepository();
}
Design Patterns
デザインパターンは、ソフトウェア開発における一般的な問題に対する解決策であり、コードの品質と保守性を向上させるために使用できます。
デザインパターンができたからといって、確実に良い開発かは言い切れませんが、開発する上で、理解しておくことは重要です。
以下は、Flutter開発でよく使用されるデザインパターンのいくつかです。
- Model-View-Controller (MVC)
- Model-View-ViewModel (MVVM)
- Providerパターン
- Blocパターン
- Singletonパターン
- Factoryパターン
- Builderパターン
- Compositeパターン
Flutterアプリケーションにデザインパターンを選択する際は、特定の要件や使用ケースを考慮する必要があります。
また、異なるパターンの間のトレードオフ(保守性、スケーラビリティ、使いやすさなど)についても考慮する必要があります。
SOLID Principles
変更しやすく、理解しやすい開発を行うための、原則です。
ルールではありません。
- Single Responsibility Principle
- 一つのクラスに多くの責務を与えないこと
- クラスと責務は1対1にすること
- OPEN-CLOSED Principle
- クラスの拡張に関してはオープンで、変更に対してはクローズドであること
- Liskov Substitution Principle
- 継承した親の処理を子も実装されていること
- Interface Segregation Principle
- IF利用側で、利用したくないメソッドが内容にIFを分離させること
- Dependency Inversion Principle
- クラスAがクラスBから呼び出される時、AはBに依存してはならない
Object-oriented programming (OOP)
オブジェクト指向プログラミング(OOP)は、「オブジェクト」という概念に基づいたプログラミングパラダイムです。
オブジェクトはクラスのインスタンスのことです。
クラスはオブジェクトを作成するための設計図ということになります。
クラスはデータ(属性)と動作(メソッド)を持つ塊です。
OOPは、実世界のオブジェクトとその相互作用をモデル化することに着目しています。
これにより、複雑で大規模なソフトウェアシステムの作成に適していると言われています。
終わりに
Flutter開発において、デザイン原則、依存性注入、デザインパターン、SOLID原則、そしてオブジェクト指向プログラミング(OOP)などの基本概念を理解し活用することは、アプリケーションの品質向上と保守性の向上に大いに寄与します。
常に最良のプラクティスを模索し、学び続ける姿勢を持っていきたいと思います。