今、話題の人工知能(AI)などで人気のPython。初心者に優しいとか言われていますが、全然優しくない! という事を、つらつら、愚痴っていきます

460.継承は人類には早すぎた

»

初回:2026/3/4

 久しぶりに言語系のお話をしたいと思います。

P子「AIネタばかりじゃ飽きるものね」※1

 とはいうものの、AIネタは本当に変化が激しい領域なので、ネタが次々出てくるので取り上げたいのは山々なんですが、ちょっと面白い記事を見つけたもので色々と考えたいと思いました。

1.オブジェクト指向とは?

 今更ですが、オブジェクト指向というと『継承』が最初に出てくるキーワードかもしれませんが、実は継承は使えない...というお話です。

 ≪参考資料1≫
  https://zenn.dev/03da/articles/3a40c5b24d10dc
  「継承は人類には早すぎた」-- シニアエンジニアに学ぶオブジェクト指向の弱み
  2026/02/27に公開
  おさぴー

 実際、私も継承は嫌いで、基本的にはインターフェースを定義しての委譲を主に使っています。

P子「もしかして、staticおじさんの再来?」

 ≪参考資料2≫
  https://el.jibun.atmarkit.co.jp/pythonlove/2020/02/061.html
  061.オブジェクト指向をディスってみる
  初回:2020/02/17
  ちゃとらん By @IT > 自分戦略研究所 >エンジニアライフ > Python なんて、大っ嫌い!

 先の≪参考資料1≫では、Javaでいう static だけでプログラムを組もう...なんてことは言ってなくて、オブジェクト指向言語に継承なんていらない...というお話です。

P子「あなたも、いらない派?」

 実際、継承なしでも委譲だけで何とかなりますし、そもそも「~である(Is-a)」からといって、実装まで共通なのかと言われるとなかなか難しいものがあると思います。それに、Javaのように多重継承禁止の場合は、実装の共通化を目的とした継承は、すぐに破綻してしまいます。

P子「それは、実装の共通化のために、継承するのが悪いんじゃないの?」

 それ以外には継承に使い道が見つからないんです。

2.構成(Composition)と委譲(deligation)の関係

 さて、私は継承ではなく委譲を使おうと言ってるのに対して、先の記事では継承ではなく構成(コンポジション)を使おうと言っています。では、この2つはどう違うのか?まずはそこから見てみたいと思います。

 こんな時に頼りになるのは、AIですね。早速聞いてみましょう。

Q:オブジェクト指向言語における 構成(Composition)と委譲(deligation)の違いを説明して

A:オブジェクト指向設計において、構成(Composition)と委譲(Delegation)は非常に関連が深く、しばしばセットで語られます。しかし、その「目的」と「役割」には明確な違いがあります。
簡単に言うと、「持っている(Has-a)」のが構成であり、「任せる(Do-for-me)」のが委譲です。

1. 構成 (Composition) とは?
 構成は、あるクラスが別のクラスのインスタンスを自身のフィールド(属性)として保持することを指します。
 概念: 「AはBを(部品として)持っている」という "Has-a" 関係。
 目的: 既存のクラスを組み合わせて、より複雑な機能を持つ新しいクラスを作ること。
 特徴: 包含される側(部品)の寿命は、包含する側(全体)に管理されることが多い。
 継承(Is-a)に代わる柔軟な再利用手法。

java
class Car {
 private Engine engine = new Engine(); // これが構成
}

2. 委譲 (Delegation) とは?
 委譲は、あるオブジェクトが自分で行うべき処理を、別のオブジェクト(主に構成されたオブジェクト)に丸投げすることを指します。
 概念: 「この仕事はBさんに任せた」という "Do-for-me" 関係。
 目的: 責務を分散させ、コードの重複を避けること。
 特徴: 外側からは「自分のメソッド」のように見えるが、中身は別クラスのメソッドを呼んでいるだけ。

java
class Car {
 private Engine engine = new Engine();

 void start() {
  engine.ignite(); // 処理をエンジンに「委譲」している
 }
}

3. 違いのまとめ
構成と委譲は、「構造(データ)」と「振る舞い(実行)」という側面で対比すると分かりやすくなります。

特徴 構成 (Composition) 委譲 (Delegation)
焦点 静的な構造(何を持っているか) 動的な振る舞い(誰がやるか)
役割 部品を組み立てて全体を作る 責任を別のオブジェクトへ渡す
関係性 Has-a(持っている) Pass-to(渡す)
イメージ スマホはバッテリーを内蔵している 撮影ボタンを押すとカメラユニットに撮らせる

 こうやって並べてみると、内部に持つという構成的要素ですが、その実オブジェクトは動的に差し替える使い方をしていますね。持っているのはインターフェースで定義して、実オブジェクトは動的に差し替える、または、その必要なオブジェクトに合ったオブジェクトを与えるという感じでしょうか?

// 1. インターフェースの定義
interface Engine {
 void ignite();
}

// 2. 具体的なエンジンの実装(ガソリンエンジン)
class GasEngine implements Engine {
 public void ignite() {
  System.out.println("ガソリンエンジンが力強く始動します。");
 }
}

// 3. 具体的なエンジンの実装(電気モーター)
class ElectricMotor implements Engine {
 public void ignite() {
  System.out.println("モーターが静かに起動します。");
 }
}

// 4. Carクラス(構成と委譲)
class Car {
 private final Engine engine; // インターフェースを「構成」

 // コンストラクタで受け取る(依存性の注入)
 public Car(Engine engine) {
  this.engine = engine;
 }

 public void start() {
  System.out.println("車のスタートボタンを押します...");
  engine.ignite(); // 実際の処理をengineに「委譲」
 }
}

// 実行例
public class Main {
 public static void main(String[] args) {
  // ガソリン車の作成
  Car gasCar = new Car(new GasEngine());
  gasCar.start();

  // 電気自動車の作成
  Car ev = new Car(new ElectricMotor());
  ev.start();
 }
}


 AI君にコーディングを手伝ってもらいました。

 普段は、GasEngine やElectricMotor を、コーディングで渡すのではなく、リフレクションを使って生成しています。そうすれば各オブジェクトの定義をリソースとして管理できるのでコードを書き換えることなくリソースを指定することで色々な組み合わせのクラスを適用できるようになります。

3.まとめ

 久しぶりにオブジェクト指向のお話になりましたが、よくよく考えると、継承...abstractクラスの継承は、「~である(Is-a)」ではなく、単に実装の継承...つまり同じコーディングを書きたくないだけにしか使っていない気がします。

P子「設計的にはダメね」

 JSP(JavaServer Page)でカスタムタグを作ってデータベースと連携させる際に、同じようなコーディングが現れます。これをフレームワーク的に共通処理しているので、共通実装部分は、継承を使っています。タグライブラリという意味では「~である(Is-a)」ですが、Javaは言語である...みたいにすべてに適用しているので、本当に同じコーディングを再利用しているだけです。

 これらを構成や委譲を使って作るよりも『楽ちん』だっただけで、そのあたりがいまいちポリシーにかけるところだと思います。

P子「破綻はしてないので、アリってことでしょ」

 個人的には必要悪だと割り切って使っています。

P子「お主もワルよのう。はっはっはっはっはぁ」

 ほな、さいなら

======= <<注釈>>=======

※1 P子「AIネタばかりじゃ飽きるものね」
 P子とは、私があこがれているツンデレPythonの仮想女性の心の声です。

Comment(0)

コメント

コメントを投稿する