Flutter初心者の学習記録 第7回:オブジェクト指向の基本原則を理解する

オブジェクト指向プログラミングの三大柱:継承、多態性、カプセル化

こんにちは、ジミーです!今回のブログでは、オブジェクト指向プログラミング(OOP)の基本的な概念である継承多態性、そしてカプセル化について解説します。これらの概念はDartを含む多くのプログラミング言語の基盤となっており、Flutterアプリ開発においても非常に重要です。

継承(Inheritance)

継承は既存のクラス(親クラスまたはスーパークラス)から特性を受け継いで新しいクラス(子クラスまたはサブクラス)を作成する機能です。これにより、コードの再利用性が高まり、階層構造を作ることができます。

Dartでの継承の例を見てみましょう:

// 親クラス
class Animal {
  String name;
  
  Animal(this.name);
  
  void makeSound() {
    print('動物が鳴きます');
  }
  
  void eat() {
    print('$nameは食べています');
  }
}

// 子クラス
class Dog extends Animal {
  Dog(String name) : super(name);
  
  @override
  void makeSound() {
    print('$nameはワンワン!');
  }
  
  void fetch() {
    print('$nameは物を取ってきます');
  }
}

void main() {
  var dog = Dog('ポチ');
  dog.eat();      // 親クラスから継承したメソッド
  dog.makeSound(); // オーバーライドしたメソッド
  dog.fetch();    // 子クラス固有のメソッド
}

Flutterでは、StatelessWidgetStatefulWidgetを継承してカスタムウィジェットを作成することがよくあります:

class CustomButton extends StatelessWidget {
  final String text;
  final VoidCallback onPressed;
  
  const CustomButton({required this.text, required this.onPressed});
  
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: onPressed,
      child: Text(text),
    );
  }
}

多態性(Polymorphism)

多態性は「多くの形態を持つ」という意味で、同じインターフェースが異なるクラスで異なる動作をすることを可能にします。簡単に言えば、同じメソッド名が異なる動作をする機能です。

多態性の例:

abstract class Shape {
  double calculateArea();
}

class Circle extends Shape {
  double radius;
  
  Circle(this.radius);
  
  @override
  double calculateArea() {
    return 3.14 * radius * radius;
  }
}

class Rectangle extends Shape {
  double width;
  double height;
  
  Rectangle(this.width, this.height);
  
  @override
  double calculateArea() {
    return width * height;
  }
}

void printArea(Shape shape) {
  print('面積は${shape.calculateArea()}です');
}

void main() {
  var circle = Circle(5);
  var rectangle = Rectangle(4, 6);
  
  printArea(circle);    // 出力: 面積は78.5です
  printArea(rectangle); // 出力: 面積は24です
}

Flutterでの多態性の応用例:

abstract class DataProvider {
  Future<List<String>> fetchData();
}

class NetworkDataProvider extends DataProvider {
  @override
  Future<List<String>> fetchData() async {
    // ネットワークからデータを取得するロジック
    return ['ネットワークデータ1', 'ネットワークデータ2'];
  }
}

class LocalDataProvider extends DataProvider {
  @override
  Future<List<String>> fetchData() async {
    // ローカルストレージからデータを取得するロジック
    return ['ローカルデータ1', 'ローカルデータ2'];
  }
}

class DataScreen extends StatelessWidget {
  final DataProvider dataProvider;
  
  DataScreen({required this.dataProvider});
  
  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<String>>(
      future: dataProvider.fetchData(),
      builder: (context, snapshot) {
        // データ表示のUI構築ロジック
        // ...
      },
    );
  }
}

カプセル化(Encapsulation)

カプセル化はクラスのデータ(属性)と、そのデータを操作するメソッドをひとまとめにする概念です。また、クラスの内部データへのアクセスを制限し、データの整合性を保護します。

Dartでのカプセル化の例:

class BankAccount {
  // プライベート変数(アンダースコアで始まる)
  double _balance = 0;
  
  // コンストラクタ
  BankAccount([double initialBalance = 0]) {
    if (initialBalance >= 0) {
      _balance = initialBalance;
    }
  }
  
  // ゲッターメソッド
  double get balance => _balance;
  
  // 入金メソッド
  void deposit(double amount) {
    if (amount > 0) {
      _balance += amount;
      print('入金完了:現在の残高は $_balance 円です');
    } else {
      print('不正な入金額です');
    }
  }
  
  // 出金メソッド
  bool withdraw(double amount) {
    if (amount > 0 && _balance >= amount) {
      _balance -= amount;
      print('出金完了:現在の残高は $_balance 円です');
      return true;
    } else {
      print('出金できません');
      return false;
    }
  }
}

void main() {
  var account = BankAccount(1000);
  print('初期残高: ${account.balance}');
  
  account.deposit(500);
  account.withdraw(200);
  
  // 直接_balanceにアクセスすることはできない
  // account._balance = -1000; // エラー
}

Flutterでのカプセル化の応用例:

class UserModel {
  String _name;
  String _email;
  int _age;
  
  UserModel(this._name, this._email, this._age);
  
  // ゲッター
  String get name => _name;
  String get email => _email;
  int get age => _age;
  
  // セッター(バリデーション付き)
  set name(String value) {
    if (value.isNotEmpty) {
      _name = value;
    }
  }
  
  set email(String value) {
    if (value.contains('@')) {
      _email = value;
    }
  }
  
  set age(int value) {
    if (value >= 0) {
      _age = value;
    }
  }
  
  // データを整形して返すメソッド
  Map<String, dynamic> toJson() {
    return {
      'name': _name,
      'email': _email,
      'age': _age,
    };
  }
  
  // JSONからインスタンスを作成するファクトリメソッド
  factory UserModel.fromJson(Map<String, dynamic> json) {
    return UserModel(
      json['name'] as String,
      json['email'] as String,
      json['age'] as int,
    );
  }
}

まとめ

オブジェクト指向プログラミングの三大原則である継承多態性カプセル化は、Flutterアプリケーション開発において非常に重要な概念です。これらを理解し適切に活用することで、保守性が高く、拡張しやすい、堅牢なアプリケーションを開発することができます。

  • 継承は親クラスの特性を子クラスに引き継ぎ、コードの再利用性を高めます
  • 多態性は同じインターフェースで異なる動作を実現し、柔軟なコードを可能にします
  • カプセル化はデータとその操作をひとまとめにし、データの整合性を保護します

次回のブログでは、これらの概念をさらに発展させ、実際のFlutterアプリケーション開発でどのように活用するかについて詳しく解説します。ご質問やコメントがありましたら、ぜひ下のコメント欄にお寄せください!

良いFlutter開発ライフを!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です