【プログラミング 基礎】元インフラ系の人と学ぶオブジェクト指向
はじめまして!
Biz Freakで主にBubbleを使った開発の方々とは別の事業で
バックエンド開発をしているエンジニアのTNと申します。
Biz FreakはBubbleとAIのイメージが強い方が多いかと思いますが、
私たちのような従来の開発を行っているエンジニアのメンバーも存在します!
弊社では「コードを書く」際でもAIの使用を推奨しており、
私たちのような従来のイメージのエンジニアでも日常的にAIを使いながら開発を行えている点や、
社内ツールが充実しているおかげでスムーズ且つバクソクで開発できる環境が整っています!
話は変わりますが、私自身のことを軽く自己紹介させていただきますと・・・
元々は広告代理店で働いていていましたが、少し転機がありIT業界へ突入。
最初はインフラ系のエンジニアでした。
興味本位で html やら Python で開発に触れ始め、開発そのものに魅入られてしまいました。
そして、開発を仕事にできるように色々と行動していたら、
バックエンドのエンジニアとなり、現在に至ります!
この記事を読んでいるあなたも何かに対してFreakできるようなものがあれば、
もしかしたら、弊社で最高のパフォーマンスを発揮できるかもしれません!
どの領域のエンジニアの方も問わず、です!
目次
■ はじめに
■ オブジェクト指向の主要な概念
- クラス
- オブジェクト
- 継承
- ポリモーフィズム
- カプセル化
- Pythonでの実装例
- 手続き型と関数型でOOPのコードを比較
■ ここが謎だよ、OOPくん
- インスタンス
- ポリモーフィズム:オーバーライド
- カプセル化
■ おわりのご挨拶
はじめに
さて、記事の冒頭で私の簡単な経歴をお伝えしましたが、
元々はインフラ系のエンジニアということをお伝えしました。
なので正直、まだまだ知識量も足りてないことを実感していますし、
今でもコマンドライン上でコマンドを打ち込んでいる方が得意だったりします。笑
そんな私が開発をするにあたって理解するのに苦労した
オブジェクト指向プログラミングについてお話しようと思います。
そもそも、オブジェクト指向プログラミング(OOP)は、
データとそのデータを操作するメソッド(関数)を「オブジェクト」としてまとめ、
それらのオブジェクト同士の関係性を考慮しながらプログラムを設計する手法です。
私が興味本位でプログラミングを始めた際に触れたPythonは、
マルチパラダイムの言語でオブジェクト指向的な書き方の出来る言語のひとつです。
そのPythonのクラスとオブジェクトを使ってOOPの概念を実装することができます。
今回の記事では簡潔にオブジェクト指向の主要な概念のご説明と
簡単なPythonのコードを用いて解説していこうと思います。
オブジェクト指向の主要な概念
クラス(Class)
クラスは、オブジェクトの設計図のようなものです。
クラスを定義することで、オブジェクトの属性を指定できます。
「属性」はオブジェクトに関連付けられたデータや処理(メソッド)を表します。
オブジェクト(Object)
オブジェクトは、クラスから生成されたインスタンスです。
実際に使われるデータを持ち、クラスで定義されたメソッドを使って操作します。
継承(Inheritance)
あるクラスが他のクラスの属性を引き継ぐことです。
再利用性を高め、新しいクラスを効率的に作ることができます。
ポリモーフィズム(Polymorphism)
同じ名前のメソッドが異なるクラスで異なる動作をすることです。
オーバーライドという概念を含みます。
※ オーバーライドの詳細は後述にて
カプセル化(Encapsulation)
属性を直接アクセスできないようにします。
クラスの外部からはメソッドを通してのみデータにアクセスできます。
※ カプセル化の詳細も後述にて
Pythonでの実装例
# クラスの定義
class Animal:
# コンストラクタ(インスタンス生成時に呼び出されるメソッド)
def __init__(self, name):
self.name = name # インスタンス変数の定義
# メソッドの定義
def speak(self):
return f"{self.name} makes a sound" # 基本的な音を返す
# クラスの継承
class Dog(Animal):
def speak(self):
return super().speak() + " - Woof!"
class Cat(Animal):
def speak(self):
return super().speak() + " - Meow!"
# オブジェクトの生成
animal = Animal("Generic Animal")
dog = Dog("Rex")
cat = Cat("Whiskers")
# メソッドの呼び出し
print(dog.speak()) # Rex makes a sound - Woof!
print(cat.speak()) # Whiskers makes a sound - Meow!
Animal クラスは基底クラスで、speak メソッドを持っていますが、
このメソッドはサブクラスでオーバーライドされることを前提としています。
Dog と Cat は Animal クラスを継承し、それぞれの speak メソッドをオーバーライドしています。
dog と cat はそれぞれ Dog クラスと Cat クラスのオブジェクトで、メソッドを使って動作を示します。
この例では、継承とポリモーフィズムの概念が使用されています。
手続き型と関数型でOOPのコードを比較
先ほどのコードの例を「手続き型」と「関数型」という手法を使って表現しようと思います。
ぜひ、見比べてみてください!
# 手続き型プログラミング
def create_animal(name, animal_type):
return {"name": name, "type": animal_type}
def animal_speak(animal):
if animal["type"] == "dog":
return f"{animal['name']} says Woof!"
elif animal["type"] == "cat":
return f"{animal['name']} says Meow!"
else:
return "Unknown animal"
dog = create_animal("Rex", "dog")
cat = create_animal("Whiskers", "cat")
print(animal_speak(dog)) # Rex says Woof!
print(animal_speak(cat)) # Whiskers says Meow!
# 関数型プログラミング
def dog_speak(name):
return f"{name} says Woof!"
def cat_speak(name):
return f"{name} says Meow!"
def create_animal(name, speak_func):
return lambda: speak_func(name)
dog = create_animal("Rex", dog_speak)
cat = create_animal("Whiskers", cat_speak)
print(dog()) # Rex says Woof!
print(cat()) # Whiskers says Meow!
今回の記事はOOPについてのお話なので、説明については割愛させていただきます。
なんとなく「こんなに違うんだ」というのを感じ取っていただければ、と。
もし、私の記事がシリーズ化するのであれば、次回は上記二つのどれかのご説明をするかもしれません。
乞うご期待!笑
ここが謎だよ、OOPくん
さてさて、ここまでさらっとOOPの説明をしましたが、
私が実際にOOPに触れた際に理解するのに苦労した概念がいくつかあります。
それが主に以下の3つです。
- インスタンス
- ポリモーフィズム
- カプセル化
私自身そんなに賢くないので、座学だけで理解することができませんでした。。。
しかも、関数であれば呼び出すだけで一発で処理を動かすことができるのに、
OOPだと回りくどいことを最初にしなければならないのが、
私にとってはすごくとっつきにくく感じました。
若干偏見ではありますが、今まで関数を使ったプログラミングがメインだった方は、
もしかしたら私と同様の壁に当たった経験があるかもしれません。
これから前述の3つの概念を簡単なコードを用いて解説していきます。
インスタンス(Instance)
インスタンスは、クラス(オブジェクトの設計図)から生成された具体的なオブジェクトのことです。
クラスは設計図にすぎませんが、その設計図に基づいて実体を作ることがインスタンス化と呼ばれます。
class Dog:
def __init__(self, name):
self.name = name
# Dogクラスのインスタンス生成
my_dog = Dog("Rex")
ここで my_dog は Dog クラスのインスタンスです。
Dog という設計図から具体的なオブジェクト(Rex という名前の1匹の犬)を作ったことになります。
ポリモーフィズム:オーバーライド(Override)
オーバーライドとは、親クラス(基底クラス)で定義されたメソッドを、
子クラスで再定義し、異なる動作に置き換えることです。
親クラスの機能を上書きして、子クラス固有の振る舞いを提供します。
class Animal:
def speak(self):
return "Some sound"
class Dog(Animal):
# 親クラスの speak メソッドをオーバーライド
def speak(self):
return "Woof!"
dog = Dog()
print(dog.speak()) # Woof!
この例では、Dog クラスで Animal クラスの speak メソッドをオーバーライドして、Dog クラス独自の動作に変更しています。
カプセル化(Encapsulation)
クラスの属性に対しグルーピングを行い、外部からの直接アクセスを制限することができます。
クラスの外部からは、明示的に公開されたインターフェース(通常はパブリックメソッド)を通してのみ属性にアクセスできます。
これにより、クラスの内部実装を変更しても、外部のコードに影響を与えにくくなります。
Pythonでは、変数名やメソッド名の先頭にアンダースコア( _ )
またはダブルアンダースコア( __ )をつけることでカプセル化を実現できます。
class Person:
def __init__(self, name, age):
self.name = name # パブリック属性
self.__age = age # プライベート属性
self.__id = self.__generate_id() # プライベート属性
# パブリックメソッド
def get_age(self):
return self.__age
def set_age(self, age):
if self.__validate_age(age):
self.__age = age
def get_id(self):
return self.__id
# プライベートメソッド(慣習的に名前の前に__を付ける)
def __validate_age(self, age):
return age > 0 and age < 120
def __generate_id(self):
# 実際のIDジェネレータロジックはここに実装
return "ID-" + str(id(self))
# インスタンスの生成
person = Person("Alice", 30)
# パブリック属性にアクセス
print(person.name) # Alice
# プライベート属性に直接アクセスはできない(ただし、完全に不可能ではない)
# print(person.__age) # AttributeError
# パブリックメソッドを通してアクセス
print(person.get_age()) # 30
person.set_age(35)
print(person.get_age()) # 35
# プライベートメソッドは直接呼び出せない(ただし、完全に不可能ではない)
# print(person.__validate_age(25)) # AttributeError
# IDの取得(プライベートメソッドで生成されたが、パブリックメソッドで取得可能)
print(person.get_id()) # ID-140712837567776 (実際の値は異なる場合があります)
# Pythonの名前修飾(name mangling)を使用して、プライベートメンバにアクセスすることも可能
# これは通常推奨されませんが、場合によっては必要になることがあります
print(person._Person__age) # 35
print(person._Person__validate_age(25)) # True
このコードでは、以下のようにデータとメソッドのカプセル化を実装しています。
- データ(属性)のカプセル化
self.__age
とself.__id
はプライベート属性として定義されています。- これらの属性には直接アクセスできず、メソッド(
get_age()
,get_id()
)を通してアクセスします。 set_age()
メソッドは、値の検証を行ってから__age
を更新します。
- メソッドのカプセル化
__validate_age()
と__generate_id()
はプライベートメソッドとして定義されています。- これらのメソッドはクラス内部でのみ使用され、外部からは直接呼び出せません。
__validate_age()
はset_age()
内で使用され、年齢の妥当性をチェックします。__generate_id()
は__init__()
内で使用され、IDを生成します。
- パブリックインターフェース
name
属性、get_age()
,set_age()
,get_id()
メソッドは公開されており、クラスの外部から安全にアクセスできます。
注意点
Pythonでは、真の意味での「隠蔽」は存在しません。
名前の前に__
を付けることで、名前修飾(name mangling)が行われますが、それでも完全に隠蔽されるわけではありません。
例えば、別の言語だとprivateやprotectedを頭につければ隠蔽を行うことができますが、Pythonにはその機能がないのです。
インスタンス、ポリモーフィズム、カプセル化についてまとめ
- インスタンスはクラスから生成されたオブジェクト
- オーバーライドは親クラスのメソッドを子クラスで上書きすること
- カプセル化はオブジェクト内部の属性に対して外部からのアクセスを制御すること
おわりのご挨拶
いかがでしたでしょうか?
弊社ではBubbleを使用した開発を行っていますが、
中にはBubbleで対応しきれないことが出てきたらコードを書かなければいけなくなる場面も存在します。
そういう場面となった際に、プログラミングの基礎的な知識などを持つと役立つことも多々あると思います。
是非、色々なことに興味を持って吸収し、開発に取り組んで行きましょう!
最後に…
私たちのチームでは、業界最高のスピード感で新規事業開発に携わることができ、
テクノロジーの力で圧倒的な成長を実感することができます。
株式会社Biz Freakで一緒に成長したい方は、ぜひ以下のリンクからご応募ください。
チーム一同、あなたと一緒に未来を創ることを楽しみにしています!