今回はオブジェクト指向についてです。
pythonを独学で組んでたときは意識してないことで多くの書き直しを生んだ私にとってはパラダイムシフト的な内容です。実際先生の説明から、あーまさに手続き型指向でずっとpythonを書いて行き詰った私のことを言われたように感じ、ある意味歴史を体感できました。体系的に勉強していないとこういうことになるまさに末路ですね。正直今もあまりよくできていない部分なので、これを機会にしっかり理解したいところです。
オブジェクト指向の定義
- 先生の説明
→オブジェクト指向とはオブジェクトを中心に考える開発や設計、パラダイム(考え方の枠組み)。開発者(お客さんを含む)がプログラム(システム)の構造を理解しやすくするための技術(考え方) - 教科書の説明
→ソフトウェアを開発するときに用いる部品化の考え方
教科書の説明だとオブジェクト指向に対して明確な答えは得られないとのことで、オブジェクト指向の考え方としては人間が内容を把握しやすいプログラム開発を実現するという目的をもった考え方というか目標を持つことなのかなとなんとなく理解。実際独学中に少し勉強したときに具体的にこうだと実感できなかった理由がわかった気がします。
対義語として手続き型指向
注:手続き型指向とは処理(関数)を中心に考える開発パラダイムのこと。こちらの方が歴史は長く、1940年代~より始まっているとのこと。
手続き型指向の問題点
- 間違いがおきやすく、わかりにくい
オブジェクト指向が手続き型指向の問題点から発展したのであれば、これまでの私はオブジェクト指向の言語を使いながら、手続き型の設計を行っていたので当然のごとく問題に当たったということですね。
オブジェクト指向のタイプもsmalltalk系とC++系のものがあるらしい
オブジェクト指向の考え方ではフィールドを外部から直接アクセスさせず、変更はクラスメソッドで行うべき => これをデータ隠蔽またはカプセル化という
オブジェクトはときどきクラスのことを指して使われることもある、かなりあいまいな用語。もし、「金型ではなくその型から生み出された仮想世界で活動する実体」を厳密に示したい場合は、インスタンスという。また、クラスからインスタンスを生成する行為をインスタンス化と表現する。
オブジェクト指向に基づいて作るメソッドには、普通はstaticを付けない
staticがついたメソッドはインスタンス生成しなくても動くメソッドである。
オブジェクト指向と題しておきながら、これを1記事で書ききるのは無理と悟ったので、ここは概要だけにして、いったん切りたいと思います。
以下はここまでの先生の説明と教科書の説明ではさらっと終わってたのに、いきなりhas-aの関係といって、話を進めていくので個人的に少し深堀。
UML
クラス名、属性、操作を上から一覧として並べる書き方をクラス図class diagramと呼ばれる設計図のルールに準じたもの。クラス図は、世界共通の設計図をUML(unified Modeling Language)で定められている図の1つ。
以下geminiの解説
UML(Unified Modeling Language:統一モデリング言語)とは、ソフトウェアの設計やシステムの構造、処理の流れを視覚的に図として表現するための「世界共通のルール(規格)」です。 [1, 2]
主にオブジェクト指向に基づいた開発で利用され、複雑なシステムを誰もがひと目で理解できるようにするために作られました。 [2]UMLを活用するメリット
- 共通言語として機能する:開発メンバー間、あるいはエンジニアとクライアントの間で、言葉のすれ違いを防ぎます。
- 設計ミスを早期発見できる:コードを書く前に図でロジックを整理するため、矛盾や考慮漏れに気づきやすくなります。
- システムの保守性が上がる:ドキュメントとして残すことで、後から参画したメンバーへの引き継ぎや仕様変更がスムーズになります。 [3, 4, 5, 6]
よく使われる代表的なUML図
UMLには10種類以上の図がありますが、実際の現場でよく使われるのは主に以下の4つです。 [3, 7]
分類 図の名前 概要(何を表現するか) 静的な構造 (システムの形) クラス図 システムに登場する「モノ(クラス)」と、それらの関係性(データ構造)を表す最も重要な図。 パッケージ図 フォルダ分けのように、クラスやシステムをグループ化して依存関係を表す図。 動的な振る舞い (システムの流れ) シーケンス図 オブジェクト間のメッセージのやり取りを時系列で表現する図。処理の順序がよくわかる。 アクティビティ図 業務のフローやプログラムの「条件分岐・並行処理」など、一連の流れを表現する図(フローチャートに近い)。 ユースケース図 ユーザー(アクター)の視点から、システムが「何をしてくれるのか(機能)」を表現する図。
初心者は何から始めるべき?
UMLをすべて完璧に覚える必要はありません。まずは以下のステップで進めるのがおすすめです。
なるほど、UMLにいくつかの図があり、クラスの関わりに関するコンセンサスをクラス図ということで、今回軽く言葉が出てきたようですね。とりあえず、出てきた言葉についてだけは学んでいこうと思います。
以下は関連についてgeminiの解説
UMLにおける「関連(Association)」とは、クラス同士が何らかのつながり(関係性)を持っていることを指します。一方のクラスがもう一方のクラスのインスタンスを「知っている」「保持している」という状態を表します。
UMLのクラス図では、関連をさらに細分化した表現(集約やコンポジションなど)があり、線の形状や矢印で関係の強さを表します。関連の種類と表記法## 1. 関連(Association)
最も一般的なつながりです。クラス間を実線で結びます。
- 表記: [クラスA] ────── [クラスB]
- 具体例: [社員] ────── [部署](社員はどこかの部署に所属しており、部署には社員がいる)
2. 誘導可能性(Navigability)
データの参照方向が「片方向」のみであることを表す場合、矢印を付けます。
- 表記: [注文] ──────> [商品](注文はどの商品かを知っているが、商品側は自分が誰に注文されたかを知らない)
3. 集約(Aggregation)
「全体と部分」の関係(〜の一部である)を表しますが、関係性は緩やかです。全体が消滅しても、部分は独立して存在し続けられます。
- 表記: 全体側に白抜きのひし形を付けます。
[車(全体)] ◇────── [カーナビ(部分)] (車を廃車にしても、カーナビは取り外して単>体で存在できる)4. コンポジション(Composition)
「全体と部分」の関係ですが、集約よりも強力な結びつきです。部分が全体と「運命共同体」であり、全体が消滅すると部分も同時に消滅します。
- 表記: 全体側に黒塗りのひし形を付けます。
[注文(全体)] ◆────── [注文明細(部分)] (注文自体がキャンセル・削除されれば、その中の明細データも一緒に消える)5. 依存(Dependency)
あるクラスが、もう一つのクラスを一時的に利用しているだけの関係です(関数の引数やローカル変数として使う場合など)。
- 表記: 破線の矢印で表します。
[車] ・・・・・・> [ガソリンスタンド] (車は給油の時だけスタンドを利用する)
多重度(Multiplicity)の表現
関連線の端には、お互いが「何対何」で結びついているかを示す数字(多重度)を記載するのが一般的です。
表記 意味 1 厳密に1つ 0..1 0個または1つ(あってもなくてもよい) * または 0..* 0個以上の任意の実数 1..* 1個以上の複数
- 例: [顧客] 1 ────── * [注文] (1人の顧客は、0件以上の注文を持つことができる)
以下はhas-aの関係について解説
has-a(ハズ・ア)関係とは、オブジェクト指向の設計(主にクラス図)において、「あるオブジェクトが別のオブジェクトを『所有している』または『持っている』状態」を表す関係性のことです。
英語の「A has a B(AはBを持っている)」が語源で、日本語では「集約(Aggregation)」や「合成(Composition)」、あるいはもっと広く「関連(Association)」と呼ばれます。isa関係との違い(最重要)
オブジェクト指向では、has-a 関係と対になる is-a(イズ・ア)関係 があります。この2つの違いを理解することが最も大切です。
関係性 意味 表現方法 具体例 has-a AはBを持っている クラスのフィールド(変数)に持つ 車(A)は、エンジン(B)を持っている。 is-a AはBの一種である クラスの継承(Inheritance) 犬(A)は、動物(B)の一種である。 has-a関係の2つのレベル(集約と合成)
UMLでは、has-a関係の「結びつきの強さ」によって、図の描き方が2種類に分かれます。
1. 集約(Aggregation) ── 結びつきが「弱い」
- 特徴:全体と部分の関係ですが、「全体が消えても、部分は生き残る」関係です。
- UMLの記号:線の端に白抜きのひし形(◇)を描きます(全体側にひし形)。
- 具体例:会社(全体) ◇── 社員(部分)
- 会社が倒産(消滅)しても、社員(個人)は消滅せず、別の場所で生き残ることができます。
2. 合成/コンポジション(Composition) ── 結びつきが「強い」
- 特徴:全体と部分が運命共同体で、「全体が消えたら、部分も一緒に消える」関係です。
- UMLの記号:線の端に黒塗りのひし形(◆)を描きます(全体側にひし形)。
- 具体例:車(全体) ◆── エンジン(部分)
- 車をスクラップにして完全に廃棄(消滅)すれば、その車の一部として組み込まれていたエンジンも同時に消滅します(再利用などを考慮しない基本設計の考え方)。
コードで見るhas-a関係(Javaの例)
プログラミングコード(Java)に落とし込むと、has-a関係は以下のように「クラスのメンバー変数として他のクラスを持つ」という形で表現されます。
// 部分となるクラスclass Engine {
void start() { System.out.println("エンジン始動"); }
}
// 全体となるクラス(Car "has a" Engine)class Car {
// Carクラスの中に、Engineクラスの変数を「持っている」
private Engine engine;public Car() {
this.engine = new Engine(); // 合成(Carと一緒に作られる)
}public void drive() {
engine.start();
System.out.println("車が走ります");
}
}