この章では、オブジェクト指向プログラミング (OOP) の応用として、クラスの継承や抽象クラス、インターフェース、ポリモーフィズムなどについて詳しく解説します。これらの概念を理解することで、より柔軟で再利用可能なコードを設計できるようになります。
7.1 継承と派生クラスの作成
継承は、既存のクラス(親クラスまたは基底クラス)を基にして、新しいクラス(子クラスまたは派生クラス)を作成する機能です。子クラスは親クラスのすべての機能を引き継ぎ、新たな機能を追加したり、既存の機能をオーバーライドすることができます。
継承の基本構文
class ParentClass:
def __init__(self, name):
self.name = name
def greet(self):
print(f"Hello, I am {self.name}.")
class ChildClass(ParentClass):
def greet(self):
print(f"Hi, I am {self.name}, and I am a child.")
# 子クラスのインスタンス作成
child = ChildClass("Alice")
child.greet() # "Hi, I am Alice, and I am a child."
この例では、ChildClass
がParentClass
を継承し、greet
メソッドをオーバーライドしています。ChildClass
はParentClass
の機能を引き継ぎつつ、自分の振る舞いを上書きしています。
7.2 抽象クラスとインターフェースの違いと使用法
抽象クラスは、他のクラスが継承するためのクラスであり、直接インスタンス化されることはありません。抽象クラスには、具体的なメソッドのほかに、サブクラスで実装しなければならない抽象メソッドを含むことができます。Pythonでは、abc
モジュールを使って抽象クラスを定義します。
インターフェースに近い概念として、Pythonでは抽象クラスを使って特定のメソッドを強制的に実装させることができます。
抽象クラスの例
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def make_sound(self):
pass
class Dog(Animal):
def make_sound(self):
return "Woof!"
class Cat(Animal):
def make_sound(self):
return "Meow!"
# インスタンスの作成と使用
dog = Dog()
cat = Cat()
print(dog.make_sound()) # "Woof!"
print(cat.make_sound()) # "Meow!"
この例では、Animal
クラスは抽象クラスであり、make_sound
メソッドをサブクラスで必ず実装しなければなりません。Dog
やCat
クラスは、それぞれ独自の実装を提供しています。
7.3 ポリモーフィズムと仮想メソッド (virtual, override, new)
ポリモーフィズムは、異なるクラスのオブジェクトが同じインターフェースを共有し、同じメソッドを呼び出せる仕組みを指します。ポリモーフィズムにより、異なるオブジェクトが同じメソッド呼び出しに対して異なる挙動を示すことができます。
ポリモーフィズムの例
class Animal:
def make_sound(self):
return "Some sound"
class Dog(Animal):
def make_sound(self):
return "Woof!"
class Cat(Animal):
def make_sound(self):
return "Meow!"
def animal_sound(animal):
print(animal.make_sound())
# ポリモーフィズムの適用
dog = Dog()
cat = Cat()
animal_sound(dog) # "Woof!"
animal_sound(cat) # "Meow!"
この例では、animal_sound
関数はAnimal
型のオブジェクトを受け取りますが、実際に渡されるオブジェクトに応じて異なるmake_sound
メソッドが呼び出されます。これがポリモーフィズムの一例です。
7.4 カプセル化の原則とプロパティの使用
カプセル化は、クラス内部のデータ(属性)を隠蔽し、クラス外から直接アクセスできないようにする原則です。これにより、データの安全性が向上し、データの不正な変更を防ぐことができます。Pythonでは、カプセル化を実現するために、属性をプライベートにし、プロパティを使ってアクセス制御を行います。
プロパティの例
class Person:
def __init__(self, name, age):
self.__name = name # プライベート属性
self.__age = age # プライベート属性
# getterメソッド
@property
def age(self):
return self.__age
# setterメソッド
@age.setter
def age(self, new_age):
if new_age > 0:
self.__age = new_age
else:
print("Invalid age!")
person = Person("Alice", 25)
print(person.age) # 25
person.age = 30 # ageを更新
print(person.age) # 30
person.age = -5 # "Invalid age!"と表示
この例では、__age
はプライベート属性であり、外部から直接アクセスできません。プロパティage
を通じて、間接的にアクセスおよび更新が行われます。
7.5 デザインパターンの基本 (Singleton, Factoryなど)
デザインパターンは、ソフトウェア設計におけるよくある問題に対する再利用可能な解決策です。いくつかの代表的なデザインパターンを紹介します。
シングルトンパターン (Singleton)
シングルトンパターンは、クラスのインスタンスが常に1つしか生成されないことを保証するパターンです。システム全体で1つのインスタンスを共有したい場合に使用します。
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(Singleton, cls).__new__(cls)
return cls._instance
# シングルトンのテスト
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # True (同じインスタンス)
ファクトリーパターン (Factory)
ファクトリーパターンは、オブジェクトの生成をクラスの外部に隠蔽し、オブジェクトの生成を統一するためのパターンです。これにより、オブジェクトの生成ロジックをクラス外に出すことができます。
class Dog:
def speak(self):
return "Woof!"
class Cat:
def speak(self):
return "Meow!"
class AnimalFactory:
@staticmethod
def create_animal(animal_type):
if animal_type == "dog":
return Dog()
elif animal_type == "cat":
return Cat()
else:
return None
# ファクトリーによるオブジェクト生成
animal = AnimalFactory.create_animal("dog")
print(animal.speak()) # "Woof!"
この例では、AnimalFactory
がDog
やCat
オブジェクトを生成する役割を担い、クライアントコードから生成ロジックを隠蔽しています。
まとめ
この章では、オブジェクト指向の応用として、継承、抽象クラス、ポリモーフィズム、カプセル化、そしてデザインパターンについて学びました。これらの概念を使うことで、柔軟で再利用可能なプログラム設計が可能になります。次の章では、コレクションと配列の操作について詳しく見ていきます。