はじめに
こんな誤解はないでしょうか。
- 継承
- ポリモーフィズム
- カプセル化
「この3つがオブジェクト指向です」
…本当にそうでしょうか?
3要素は「目的」ではなく「手段」
オブジェクト指向の3要素はあくまでも手段であり、オブジェクト指向の目的を果たすためのものであると私は考えています。
そのため、オブジェクト指向の目的や3要素が何のためにあるのかを整理することでオブジェクト指向の理解が深まるのではないかと思います。
オブジェクト指向はなぜ生まれたのか?
オブジェクト指向が生まれる前の問題点
- データと処理がバラバラ
- 変更すると他も壊れる
- 影響範囲が読めない
問題点の具体例
例えば、下記のように「値段を扱う処理」がバラバラに存在しているとします。
- 税金をかける処理
- 表示する処理
- 割引する処理
税率を変更する等の仕様変更が入るとどうなるでしょうか?
関係ありそうな場所を全部探して修正する必要があります。
また、データと処理がバラバラに存在いているため、影響範囲もわかりにくいです。
オブジェクト指向の目的
こうした問題を解決するために生まれたのがオブジェクト指向です。
やっていることは、データと処理をひとまとまりにすることです。
これによって何が起きるか?
- 関係するものがまとまる
- 変更の影響が外に漏れにくくなる
👉 変更に強い構造になる
重要なのは?
重要なのは「何をどこが担当するか(責務)」を分けることです。
- 値を管理する責務
- 計算する責務
- 表示する責務
これらが整理され、しっかりと閉じることで変更の影響が広がりにくくなります。
オブジェクト指向は「閉じる」と「開く」のバランス
オブジェクト指向で最初にやるべきことは、カプセル化によって内部を閉じることです。
なぜなら、内部が自由に触れてしまう状態ではどんな設計もすぐに壊れてしまうからです。
ただし、閉じるだけでは拡張できません。
そこで重要になるのが、必要な部分だけを開くという考え方です。
これを実現するのが、ポリモーフィズムと継承です。
カプセル化(Encapsulation)
何をするもの?
内部の状態を壊させないために行います。
ここでよくある誤解はカプセル化=アクセサ(getter/setter)を作ることというものです。
大事なのは、値の変更ルールを1箇所に集めることであり、アクセサを作成することではありません。
悪い例(直接触れてしまう)
class BankAccount:
def __init__(self, balance):
self.balance = balance
account = BankAccount(100)
# どこからでも自由に変更できてしまう
account.balance = -1000
良い例(ルールを通す)
class BankAccount:
def __init__(self, balance):
self._balance = balance
def deposit(self, amount):
if amount <= 0:
raise ValueError("正の値のみ")
self._balance += amount
def withdraw(self, amount):
if amount > self._balance:
raise ValueError("残高不足")
self._balance -= amount
def get_balance(self):
return self._balance
何が違うのか?
下記を守っているため、「どう変更していいか」をクラスが管理している点です。
- 勝手に値を変えられない
- 必ずルールを通る
重要なのは
- 状態とそのルールを一緒にすること
- 壊れないように変更の入口を制御すること
ポリモーフィズム(Polymorphism)
何をするもの?
種類ごとの分岐を書かなくてよくする仕組みです。
下記のようにspeekのメソッドがあるクラスをの処理をともに呼び出すことで同じ処理を分岐せずに行うことができます。
class Dog:
def speak(self):
print("ワン")
class Cat:
def speak(self):
print("ニャー")
animals = [Dog(), Cat()]
for a in animals:
a.speak()
実務では、継承と組み合わせて使われることが多いですが継承を使わずに説明しています。
何が嬉しいのか?
- if文が減る
- 追加に強くなる
if type == "dog":
print("ワン")
elif type == "cat":
print("ニャー")
ここで重要なのは、 「同じ呼び方ができる」という約束があることです。
DogとCatはともにspeak() が呼べます。
この「共通の呼び方(約束)」があることで、中身を知らなくても扱えるようになります。
継承(Inheritance)
何をするもの?
共通部分をまとめる仕組みです。
class Animal:
def eat(self):
print("食べる")
class Dog(Animal):
pass
何が嬉しいのか?
共通部分をまとめることで再利用しやすくなります。
注意点
- 親の変更が全部に影響する
- 結合が強くなりやすい
カプセル化の重要性
3要素はもともと処理をまとめて変更に強い構造を作ることが目的でした。その視点から3要素をまとめると以下の通りです。
- カプセル化 → 内側に閉じる(変更を閉じ込める)
- ポリモーフィズム → 外側に開く(変更を受け入れる)
- 継承 → 共有する(ただし影響が広がりやすい)
個人的には一番重要なのはカプセル化だと思っています。
理由は、閉じることを前提としてポリモーフィズムと継承があり、この二つは開き方の工夫だと考えているからです。
また、現実問題ポリモーフィズムと継承はうまく適用できないことが多く、鳴かない動物がうまれたり、最初からある程度前提を知っていないと間違った作りになることも多いです。
まとめ
オブジェクト指向で設計を行うにあたり重要な点は以下です。
👉 まず閉じる(カプセル化)
👉 そのあと必要な分だけ開く
また、オブジェクト指向は変更に強い構造を作ることが目的であり、3要素を使うことはあくまでも手段です。
手段が先行して当初の目的を見失わないような設計を心がけましょう!







