Flutter オブジェクト指向プログラミング(Flutter Roadmap OOP)

## はじめに
Flutterを網羅的に学習するにあたって[Roadmap](https://roadmap.sh/flutter)を使って学習を進めることにしました。

この記事では、Flutter初学者やこれからFlutterを学習し始める方に向けて、**オブジェクト指向プログラミング**についてまとめています。

RoadmapはFlutterだけでなく、他の言語やスキルのロードマップも提供されており、何から学習して良いか分からないと悩んでいる方にとって有用なサイトになっています。
ぜひ[Roadmap](https://roadmap.sh/)を利用して学習してみてください。

## Roadmapとは

簡潔に言えば、Roadmap.shは学習者にとっての**ガイドブック**であり、**学習の方向性を提供する学習ロードマップサイト**です。

初心者から上級者まで、ステップバイステップでスキルを習得するための情報が提供されています。

学習の進め方が分かりやすく示されているだけでなく、個々の項目に参考資料やリソースへのリンクも提供されているので、学習者は目標を設定し、自分自身のペースで学習を進めることができます。

## OOP
FlutterロードマップOOPでは以下の2つのサイトが紹介されています。興味のある方はぜひお読みください。

- Discover Object Oriented Programming: https://blog.hubspot.com/website/object-oriented-programming
- Software Development Tutorial: https://www.youtube.com/watch?app=desktop&v=SS-9y0H3Si8

## 設計原則とは
**設計原則**(Design Principles)とは、設計や開発の際に遵守すべき**基本的なガイドラインやルールのこと**を指します。

**設計原則を遵守する**ことで、システムや製品を設計する際に**品質**、**効率**、**信頼性**、**保守性**を向上させることができます。

一般的に**設計原則**は設計段階から、開発、保守、運用の**各段階にわたって適用される**ため、プロジェクト全体の品質を上げることにつながるのです。

## オブジェクト指向プログラミング(Object-Oriented Programming / OOP)
**Dart**は、**オブジェクト指向プログラミング**（Object-Oriented Programming / OOP）の原則に基づいて設計されたプログラミング言語です。

オブジェクト指向（OO / Object-Oriented）とは簡単に言うと**モノ**に注目した考え方のことを言います。**役割ごとに分割されたモノ(オブジェクト)を一つ一つ組み合わせることで、複雑で大きな処理ができるモノを作り上げる**という考えのことを指しています。

現実世界でのことを例にするならば、**車の組み立て**についてイメージするとわかりやすいです。車は**エンジン**、**ブレーキ**、**タイヤ**、**ハンドル**、**シート**など様々なパーツが組み合わさって構成されています。この一つ一つの部品が**オブジェクト**(モノ)なのです。部品を**一箇所で作るのではなく**、役割ごとに**分割して作成し、最終的に組み合わせる**という手法がオブジェクト指向の考え方になります。

**データ**と**処理**をまとめて一つの**オブジェクト**(モノ)としてとらえることによって、**コード量を抑え、保守性を高める**ことができるのです。**オブジェクト指向**についてもっと詳しく知りたい方は[こちら](https://wa3.i-3-i.info/word1895.html
)にわかりやすい記事があったので、是非読んでみてください。

### 1. クラスとオブジェクト
**クラス**とは**オブジェクトを生成する**ために**データと処理**を定義したものです。**データ**のことを**フィールド**といい、**処理**のことを**メソッド**と言います。

```dart
class Car {
  // フィールド: 車のデータ
  String brand;
  double price;

  // メソッド: 走る
  void drive() {
    print('drive');
  }
}
```

Flutterで使用する、**ウィジェット**（Widget）もクラスとして実装され、UI要素や機能を提供しています。例えば、Textウィジェットはテキストを表示するためのクラスであり、Imageウィジェットは画像を表示するためのクラスです。これらのクラスからオブジェクトを作成し、アプリケーションのUIを構築しています。

そして、**クラス**を実際に**オブジェクトにして使う**ことを**インスタンス化**と言い、生成されたものを**インスタンス**と呼びます。

また、**インスタンス化**するときに呼ばれるメソッドを**コンストラクタ**と言います。**コンストラクタ**を呼び出すことで**オブジェクトの初期化が行える**のです。クラス内に定義しない場合、自動的に作成された**デフォルトコンストラクタ**が呼ばれます。

```dart: コンストラクタ例
// Carクラスの定義
class Car {
  // フィールド: 車のデータ
  String brand;
  double price;

  // パラメータ付きコンストラクタ
  Car(this.brand, this.price);

  // メソッド: 走る
  void drive() {
    print('$brandの車がdriveする');
  }
}

void main() {
  // コンストラクタを使用してCarオブジェクトを作成
  Car car1 = Car('Toyota', 25000.0);
  Car car2 = Car('Honda', 22000.0);

  // オブジェクトのメソッドを呼び出して情報を表示
  print('Car 1 Information:'); // Car 1 Information: Toyotaの車がdriveする
  car1.drive();

  print('Car 2 Information:'); // Car 2 Information: Hondaの車がdriveする
  car2.drive();
}
```

上記のコードでは`Car`クラスに**パラメータ付きコンストラクタ**が使用されています。コンストラクタは`this`を使用して、クラス(Carクラス)のフィールド(ブランド、価格)を初期化することができるのです。

また、**コンストラクタを呼び出す際に、引数を渡すことで、オブジェクトを初期化**できます。

上記の例でいくと`Car`クラスのコンストラクタを使用して2つの車のオブジェクト`car1`と`car2`を作成しています。それぞれのオブジェクトは異なる車の情報を持っており、**コンストラクタによって初期化**されています。

つまり、**オブジェクトを作成する際に、コンストラクタが呼び出されて、オブジェクトの状態を設定できるのです**。

### 2. カプセル化
**データ**と**データを操作するメソッド**をクラス内で**カプセル化する概念**です。Flutterでは内部でデータとロジックをカプセル化し、外部からアクセスするために**プロパティ**や**メソッド**を指定することができます。これにより、**内部の実装を隠し、簡潔なコード**を書くことができるようになります。

**カプセル化**とは簡単にいうと、**干渉しないこと**を言います。例えば、車の内部には様々な部品がありますが、運転手はその部品について**詳細を知らなくても**運転することができます。

運転手が知るべきものは
・アクセルを踏む＝速度を上げる
・ブレーキを踏む＝速度を下げる
・ハンドルを操作する => 左・右に曲がる
などの「運転方法」だけです。

つまり**中の複雑な動作は内部に隠し、簡単な使い方さえ知れば良い**ことになります。

ここで重要なことは**各部品がそれぞれの役割に徹底する**ということです。役割が不明瞭だと、極端な話、ドアでハンドルを操作したり、ブレーキがワイパーを動かしたりができるようになり、車として成り立たなくなるのです。

これをプログラミングに置き換えると**クラス**(部品)は**一つの独立した機能**を持ち、その**中身を理解しなくても簡単に使える**べきということになります。

例えば、Textウィジェットの**中身がどういう実装になっているか知らなくても**、`Text('ウィジェット')`のように**簡単に使える**ことができることがカプセル化の利点なのです。

### 3. 継承
クラス間で**コードの再利用**を可能にする概念です。Flutterでは、継承[^1]を利用して、**既存の機能を拡張し、カスタムした機能を作成**することができます。例えば、Containerウィジェットを継承してカスタムしたコンテナウィジェットを作成したりすることができるのです。

継承元のクラスを**スーパークラス**、継承してできたクラスを**サブクラス**といいます。

以下のように**継承**を使用することで、**親クラスの機能を活用**しつつ、独自の機能や**カスタマイズを追加**できます。

```dart
import 'package:flutter/material.dart';

// Containerを継承してカスタムコンテナウィジェットを作成 CustomContainerWidget(サブクラス) Container(スーパークラス)
class CustomContainerWidget extends Container {
  // カスタムコンテナウィジェットのコンストラクタ
  CustomContainerWidget({
    Key? key,
    double width = 100.0,
    double height = 100.0,
    Color color = Colors.blue,
    Widget? child,
  }) : super(
          key: key,
          width: width,
          height: height,
          color: color,
          child: child,
        );
}

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(
        title: Text('Custom Container Example'),
      ),
      body: Center(
        child: CustomContainerWidget(
          width: 150.0,
          height: 150.0,
          color: Colors.green,
          child: Text('Custom Container'),
        ),
      ),
    ),
  ));
}
```

**継承とは、特定の要素だけを受け継いで利用していく**ことを言います。例えば、車にはトラックや、軽自動車、セダンなど様々な種類が存在します。これらは車というクラスの**特定の要素**(人を乗せる、四輪で走るなど)を受け継いでいるのです。また、乗り物には車や飛行機、船など様々な種類が存在します。これらも乗り物というクラスの**特定の要素**(人を乗せる、走るなど)を受け継いでいます。

一から作成するよりも**元々ある機能(クラス)を一部使いながら改変していく**ことで、**機能や改修の追加が少なくなる**のです。

### 4. ポリモーフィズム (多態性)
**ポリモーフィズム**とは、クラスを継承して、メソッドをオーバライド[^2]した際に、関数（メソッド）の呼び出しに対し、オブジェクト毎に異なる動作をすることを言います。

```dart
// 乗り物クラス
class Vehicle {
  void moveOn() {
    print('Moving...');
  }
}

// 飛行機クラス
class Airplane extends Vehicle {
  @override
  void moveOn() {
    print('Flying');
  }
}

// 船クラス
class Ship extends Vehicle {
  @override
  void moveOn() {
    print('Sailing');
  }
}

// 車クラス
class Car extends Vehicle {
  @override
  void moveOn() {
    print('Driving');
  }
}

void main() {
  final airplane = Airplane();
  final ship = Ship();
  final car = Car();

  airplane.moveOn(); // Flying
  ship.moveOn(); // Sailing
  car.moveOn(); // Driving
}
```

上記のコードでは**乗り物クラスに「moveOn」（進む）という関数**（メソッド）があります。そして、乗り物クラスのサブクラスには 飛行機クラス、船クラス、車クラスがあり、それぞれ「moveOn」（進む）という関数（メソッド）の中身を**独自で実装**しています。

乗り物クラスの「moveOn」（進む）を実行すると、飛行機クラスのオブジェクトは「Flying」、船クラスのオブジェクトは「Sailing」、車クラスのオブジェクトは「Driving」と出力します。

このように、**呼び出し側は同じ「moveOn」（進む）という処理を実行していますが、命令を受け取ったオブジェクト側でそれぞれ異なる動作をする**のが**ポリモーフィズム**です。

### 5. 抽象化
**抽象化**は、簡単にいうと**役割分担**のことを言います。重要な概念や動作を**抽出**し、それを簡潔に表現することが抽象化の考え方になります。

車の部品をイメージしてください。アクセル、ブレーキ、ハンドル、ワイパーなど様々な部品がありますが、これらは**個々がそれぞれの役割を全うする**ことで、車として機能するようになります。また、**役割分担をしているおかげで、車が故障した際、どの部品を直せばいいのか原因がどこにあるのかを特定しやすくなるのです。**

この時、**それぞれの部品はお互いの機能について細かく理解する必要はありません**。例えば、「ブレーキ」は「ハンドルで進行方向を変えることができる」ということ理解していれば、ハンドル内部の複雑な処理について知らなくても良いのです。

つまり、**抽象化を取り入れることで、それぞれの部品が自身の役割に専念する**ことができます。これをプログラミングに当てはめると、**一つのクラスに多くの複雑な処理を入れるのではなく、機能ごとに別々のクラス切り出して、特定の機能を使用する際にその機能(クラス)のメソッドや変数を呼び出せば良い**のです。

Flutterには**抽象クラス**という**継承されることを前提**としているクラスがあります。抽象クラスは`class`キーワードの前に`abstract`というキーワードを付与したクラスのことで、**処理の再利用をしたい**場合に使用します。

```dart
// カーパーツの抽象クラス
abstract class CarPart {
  // カーパーツの動作を表す抽象メソッド
  void operate();
}

// アクセルクラス
class Accelerator extends CarPart {
  @override
  void operate() {
    print('アクセルを踏んで加速します。');
  }
}

// ブレーキクラス
class Brake extends CarPart {
  @override
  void operate() {
    print('ブレーキを踏んで停止します。');
  }
}

// ワイパークラス
class Wiper extends CarPart {
  @override
  void operate() {
    print('ワイパーを動かして窓を拭きます。');
  }
}
```

上記のコードのように**抽象クラス**を使用することで、パーツごとに**独立して操作**できるようになります。またそのおかげで、パーツごとの役割を全うでき、責務が明確になるのです。


## 参考資料

https://qiita.com/tokkun5552/items/4649478803be47cd488c

https://zenn.dev/hiddy0329/articles/7790eaab94ff2a

https://atmarkit.itmedia.co.jp/fjava/devs_bk060201/object03/object03.html

https://qiita.com/gksdyd88/items/13b9f71861f540c8b471

https://medium-company.com/%E3%83%9D%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%95%E3%82%A3%E3%82%BA%E3%83%A0/#:~:text=%E3%83%9D%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%95%E3%82%A3%E3%82%BA%E3%83%A0%EF%BC%88%E8%8B%B1%EF%BC%9APolymorphism%EF%BC%89%E3%81%A8,%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%82%92%E3%81%84%E3%81%84%E3%81%BE%E3%81%99%E3%80%82&text=%E7%95%B0%E3%81%AA%E3%82%8B%E5%8B%95%E4%BD%9C%E3%82%92%E5%90%8C%E3%81%98%E5%87%A6%E7%90%86%E3%81%A7%E5%AE%9F%E7%8F%BE%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8C%E3%83%9D%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%95%E3%82%A3%E3%82%BA%E3%83%A0%E3%81%A7%E3%81%99%E3%80%82

[^1]: **継承**
すでに存在しているクラスに、メンバを追加したり元々のメソッドを上書きしたりして新しいクラスを作ることです。

[^2]: **オーバーライド**
オブジェクト指向における用語のひとつであり、親クラスにあるメソッドを子クラスで再定義することによって、子クラス上で親クラスのメソッドを上書きすることを言います。
[こちら](https://wa3.i-3-i.info/word138.html)にわかりやすい記事があったので、是非読んでみてください。