😊
Flutter: 抽象クラスとミックスインについて簡単にまとめる
はじめに
Flutterでコードを効率的に書くための二つの重要な概念「抽象クラス」と「ミックスイン」について説明します。
抽象クラスは「こういう機能を持つべき」というルールを定義する設計図のようなもので、ミックスインは既存のクラスに「機能を混ぜ込む」ための仕組みです。
この二つを適切に使い分けることで、コードの重複を減らしたり、メンテナンスしやすいアプリを作る事が出来ます。
特に複数の画面で共通の機能を実装する場合(mixin)や、関連するクラスに一貫したルールを設定したい場合(Abstract Class)に役立ちます。
本題
抽象クラス(Abstract Class)
そもそも Abstract(アブストラクト)は「抽象的な、観念的な,理論的な」という意味です。
通常の class を設計図のようなものだとすると、Abstract は完全ではない設計図のようなものです。
抽象クラスは「こういう機能を持つべき」というルールを定義し、具体的な実装は子クラスに任せるクラスです。
特徴
- インスタンス化できない(
new Abstract()
のように直接使えない) - 子クラスが実装すべきメソッドを定義できる
- 共通の機能も提供できる
- 定数をまとめる場所としても使える
例(タスク管理アプリ)
// タスク種別の抽象クラス
abstract class TaskType {
String get name; // 子クラスで実装必須
Color get color; // 子クラスで実装必須
// 共通の機能
Widget buildIcon() {
return Container(
color: color,
child: Text(name[0]), // 最初の文字をアイコンとして表示
);
}
}
// 具体的な実装クラス
class WorkTask extends TaskType {
String get name => '仕事';
Color get color => Colors.blue;
}
class PersonalTask extends TaskType {
String get name => 'プライベート';
Color get color => Colors.green;
}
使用場面
- 関連するクラスに共通のルールを設定したいとき
- 複数の実装方法があるけど、基本的な構造は同じにしたいとき
- (実際に個人開発でよく使ってます)アプリで使うカラーやテキストスタイルを定数としてグループ化したいとき
// アプリ全体で使う色の定数
abstract class AppColors {
// プライベートコンストラクタでインスタンス化を防ぐ
AppColors._();
// 基本色
static const Color primary = Color(0xFF4CAF50);
static const Color secondary = Color(0xFF2196F3);
static const Color error = Color(0xFFE53935);
// テキスト色
static const Color textPrimary = Color(0xFF212121);
static const Color textSecondary = Color(0xFF757575);
// 背景色
static const Color background = Color(0xFFFFFFFF);
static const Color cardBackground = Color(0xFFF5F5F5);
}
// 使用例
Container(
color: AppColors.primary,
child: Text('タスク', style: TextStyle(color: AppColors.textPrimary)),
)
抽象クラスと通常クラスの違い
- 抽象クラスは abstract キーワードを使用
- 抽象メソッドは本体を持たない(実装は子クラスで行う)
- 抽象クラスは直接インスタンス化できない
ミックスイン(mixin)
mixin は「混ぜ込む」という意味で、既存のクラスに機能を追加するための仕組みです。
Dart は単一継承(一つの親クラスしか持てない)ですが、ミックスインを使うと複数の場所から機能を取り込めます。
その際、with
キーワードを使います。
下記、Mermaid 記法を使ったイメージ図。
図の説明
- Screen: すべての画面の基本となる
抽象クラス
です - CartFeature: カートに商品を追加する機能を提供する
mixin
です - FavoriteFeature: お気に入りに商品を追加する機能を提供する
mixin
です - ProductListScreen: 商品一覧を表示する画面で、
Screen
を継承しFavoriteFeature
をmixin
しています - ProductDetailScreen: 商品詳細を表示する画面で、
Screen
を継承し、CartFeature
とFavoriteFeature
の両方をmixin
しています - 継承関係: 矢印「<|--」は継承を表しています
- with関係: 点線矢印「<|..」は
mixin
の使用を表しています
特徴
- 複数のクラスで同じ機能を共有できる
- クラスに「混ぜ込む」ことで機能を追加する
- 状態(変数)とメソッド(関数)の両方を含められる
- 継承とは異なり、複数のミックスインを使える
例(タスク管理アプリ)
// ローディング表示機能のミックスイン
mixin LoadingMixin {
bool _isLoading = false;
bool get isLoading => _isLoading;
void showLoading() {
_isLoading = true;
// ローディング表示処理
}
void hideLoading() {
_isLoading = false;
// ローディング非表示処理
}
}
// タスク操作機能のミックスイン
mixin TaskOperationMixin {
void saveTask(Task task) {
// タスク保存処理
}
void deleteTask(String taskId) {
// タスク削除処理
}
}
// タスク一覧画面
class TaskListScreen extends StatelessWidget with LoadingMixin, TaskOperationMixin {
void loadTasks() {
showLoading();
// タスク読み込み処理
hideLoading();
}
}
// タスク詳細画面
class TaskDetailScreen extends StatelessWidget with LoadingMixin, TaskOperationMixin {
void updateTask(Task task) {
showLoading();
saveTask(task);
hideLoading();
}
}
こういう場面で使う
- 複数の画面で同じ機能を使いたいとき
- 例:ローディング表示、エラーハンドリング
- 継承関係がないクラス間で機能を共有したいとき
- 例:異なる種類の画面でも同じアニメーション効果を使いたい
- コードの重複を避けたいとき
- 例:複数の画面でフォーム検証ロジックを共有する
抽象クラスとミックスインの使い分け
特徴 | 抽象クラス | ミックスイン |
---|---|---|
目的 | 共通の型と基本実装の提供 | 機能の追加・拡張 |
継承 | 単一継承のみ | 複数適用可能 |
インスタンス化 | 不可 | 単体では不可 |
コンストラクタ | 持てる | 持てない |
使用場面 | 「〜である」関係 | 「〜できる」関係 |
Reference Site
Discussion