0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

抽象クラスの必要性が腑に落ちなかった話

Posted at

プログラミングを学ぶ中で、関数やクラスの作り方は理解できてきたけど、「抽象クラスってなんで必要なんだろう?」と長らくモヤモヤしていた。

関数との違いに着目してみた

関数って、同じ処理が複数箇所に出てきた時に「共通化しよう」と思って使う。コード量も減るし、実際にその関数を呼び出すから恩恵がすぐに見える。だからこそ「関数化しよう」という意識は自然に持てる。

でも抽象クラスは、定義してもインスタンス化して直接使うことはできない。しかも、クラスの構造を縛るだけ。だから「これ、今作って意味ある?」と感じてしまう。

視点の変換がカギだった

じゃあ、どうやったら「抽象クラスを作ろう」と自然に思えるようになるのか。

例:ArrayListとLinkedList

配列ベースのリスト(ArrayList)と、ポインタベースのリスト(LinkedList)を考えてみると、それぞれにメリット・デメリットがある。

  • ArrayList:ランダムアクセスが速い
  • LinkedList:挿入・削除が速い

状況によって使い分けたいが、使うときのインターフェース(add, get など)は統一されていた方が使いやすい。

ここで次のような関数を考えてみる。

void processList(MyList list) {
    list.add(1);
    list.add(2);
    System.out.println(list.get(0));
}

この processList 関数は、MyList 型のオブジェクトに対して要素を追加・取得している。重要なのは、この関数が「ArrayListなのかLinkedListなのかを気にせず」使えている点。

これは、MyList抽象クラス(もしくはインターフェース) として、add()get() という共通のメソッドを定義しているから実現できること。

抽象クラスがなかったら?

もし抽象クラス(またはインターフェース)がなかったとしたら、次のようなことが起こります:

  • ArrayListaddTo()getByIndex() を使う
  • LinkedListappend()fetch() を使う

すると、processList のような汎用的な関数は書けない。以下のように、リストの種類ごとに処理を分ける必要が出てくる。

void processArrayList(ArrayList arrayList) {
    arrayList.addTo(1);
    arrayList.addTo(2);
    System.out.println(arrayList.getByIndex(0));
}

void processLinkedList(LinkedList linkedList) {
    linkedList.append(1);
    linkedList.append(2);
    System.out.println(linkedList.fetch(0));
}

クラスの数が増えるほど、使う側のコードも複雑になってしまう。

抽象クラスで解決!

共通のメソッド名・引数・戻り値の型などを抽象クラスで統一しておけば、実装の中身が違っても使う側は気にせずコードを書ける。

abstract class MyList {
    abstract void add(int value);
    abstract int get(int index);
}

class ArrayList extends MyList {
    // 内部は配列で実装
    void add(int value) { /* 実装 */ }
    int get(int index) { /* 実装 */ }
}

class LinkedList extends MyList {
    // 内部はノードで実装
    void add(int value) { /* 実装 */ }
    int get(int index) { /* 実装 */ }
}

使う側はこう書ける:

void run() {
    MyList list = new ArrayList();
    list.add(10);
    list.add(20);
    System.out.println(list.get(1));
}

このように「使う側」が内部実装を意識せずに済むようになるのが、抽象クラスの大きな恩恵。

利用者視点に立った設計

この経験を通じて感じたのは、「抽象クラスの価値は、クラスを使う側の視点に立つことで初めて実感できる」ということだった。

普段は「作る側」になりがちだけど、実際には「使いやすさ」こそが良い設計を分けるポイント。

この気づきは、今後の設計力を高める上で非常に大事な視点だと感じた。

おわりに

「抽象クラスって意味あるの?」と思っていた自分が、「めちゃくちゃ助かるじゃん!」と実感できたのは、「ちょっとした視点の変換」だった。

物事の見方が変わると、理解速度もぐっと上がる。そう実感した出来事だった。

同じようにモヤモヤしてる人の参考になれば嬉しい。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?