シコウノキロク

アラサー男が自由を手に入れるまでの思考と試行の記録。Python・お金・考察・学んだことが中心

クラスとインスタンスの関係やオブジェクト指向をまとめた[Pythonの学習をjavaの知識で]

この記事は初心者エンジニアである私の現時点での知識を元にしているため間違っている可能性があります。
参考にしていただければ嬉しいですが、鵜呑みにはせずに自分で納得するまで学習してください。
また、上級者の方は間違っている箇所を指摘していただければ幸いです。

自分が書いたコードのなんと読みにくいこと。
そして、処理を変える時に修正箇所が複数あったり、そもそも修正箇所が見つけづらいコード。
つまり、可読性と保守性が低いわけですね。

shikouno.hatenablog.com

その理由というのがオブジェクト指向を意識していないベタ打ちコードだからだよ、ということを師匠に指摘してもらいました。
そうするとPythonのクラスの項目を読んでも使い方のイメージが浮かばずに理解が追いついていなかった、クラスやインスタンスを理解する必要があるなと。
色々質問するのですが、私の知識がなさすぎて全く話が噛み合わない。
そんな時でも嫌な顔せず付き合ってくれる師匠に感謝しかないわけですが。

と、まぁ話が脱線しました。
どうしてもPythonに関する学びだけだとぼんやりとした理解のみだったクラス・インスタンスオブジェクト指向
javaの入門サイトをさらっと読むとなんとなく輪郭が定まってきたのでアウトプットによる知識整理と備忘録を兼ねて記事していきます。

javaの知識

javaへ入門するにあたって参考にしたのがテックスコアさんのサイトになります。
30分くらいでサラーっと読めます。
こんなわかりやすい解説ができるようになりたいものです。

Java 入門 | TECHSCORE(テックスコア)

Pythonではクラスを作成しなくても書き始めることができますが、javaでは必ずクラスを作成する必要があるようです。
この点がオブジェクト指向の強弱に繋がってくるのかと思っています。
クラスを作成するとインスタンスの生成が必須になってくるため、必然とオブジェクト指向っぽくなる。
もちろん一つのクラスにベタ打ちするとオブジェクト指向っぽさは皆無なダメコードになるわけですが。

まず、オブジェクト指向を語る上で避けては通れないクラスとインスタンスについて。

クラスとインスタンス

クラスとインスタンスの説明についてよくあるのが、設計書とたい焼き

項目 例1 例2
クラス 設計書 たい焼きの型
インスタンス 設計書を元にした実物 型を元に作られたたい焼き

なるほど、わからん!
初心者プログラマーはこの説明でわかるのでしょうか。僕の理解力が乏しいという可能性は多いになりますが。

私は別の例を提唱したい。
クラス:霊
インスタンス:人

例えば、りんごを握りつぶすという処理を実装したいとします。
クラス設計では、実体がない霊の状態で握力を受け取ってりんごを握りつぶすという処理を教えることができます。
しかし、実体がないため処理を実行しても実世界に影響を与えられません。
インスタンス化することで、実体にのある肉体に霊を憑依させることでりんごを握りつぶすという処理を実世界で実行できるようになるわけです。

Pythonのコードを例として クラスを設計する。霊に動作を記述する。 握力の値を受け取ってりんごを握りつぶす、という動作です。

class Soul:

    def __init__(self, grip):
        self._grip = grip

    def crush_apple(self):
        required_grip = 100
        if self._grip > required_grip:
            return "生搾りりんごジュース"
        else:
            return "りんご"

インスタンス化。人に霊を憑依させる。 シュワちゃん (握力2000kg)と一般市民(握力30kg)に憑依してりんごを握りつぶします。

Schwarzenegger = Soul(2000)
"シュワちゃん:" +Schwarzenegger.crush_apple()

general_citizen = Soul(30) 
"一般市民:"general_citizen.crush_apple()

結果

'シュワちゃん:生搾りりんごジュース'
'一般市民:りんご'

まとめると
クラスを作成した段階では、処理や持つべき変数については決定することができる。実行はできない。
→霊に、握力を受け取ってりんごを絞るという動作を教える。
変数に値を代入したり処理をするには、インスタンス化する必要がある。この段階で初めて実行できる。
→人に憑依し、握力を霊に教えてあげてりんごを絞るという動作を行う。

クラスとインスタンスのメリットは何か

私が調べていると特にメリットに感じたのは2点です。

カプセル化・隠蔽

クラスでデータと処理を一括にまとめることがカプセル化
カプセル化のメリットは、外部からアクセスできるクラスやメソッドの名前を見るだけで、内部の詳しい処理内容や変数を知る必要がない点。 今回の例では、 握力判定が行われていること・握力100kgが必要というところ。
変数や処理が外部から見えないようすることができるのが隠蔽。
隠蔽のメリットは、内部の変数を外部から変更することができないようになる点。
今回の例ですと、人に憑依した後に握力のところ。

私は上記のようなイメージを持っていますが、用語の定義については曖昧なようです。
詳しく考察してくれている記事がありますので気になる方はご参照ください。

カプセル化、情報隠蔽、データ隠蔽 - ぐるぐる~

そして、javaの場合は変数やメソッドを宣言する時に隠蔽度合いを設定します。
隠蔽度合いとは、どのクラスからでも参照可能・同一クラスからのみ参照可能、といったことです。 また、クラスを必ず作成するためクラスをオブジェクトとして意識しやすく、外部からどう参照されるかを意識した設計がしやすい。
つまりカプセル化を常に念頭においてクラス設計をする言語仕様になっている。

Pythonで隠蔽度合いを設定する場合、変数やメソッド名の頭にアンダースコアを一つないし二つつけることで達成します。
javaと比べるとあまり重視していないと感じました。
完全に隠蔽されないけどもお作法としてそういうルールですよ、といった程度。 また、クラスは必ず作成する必要がないので、カプセル化を意識しない設計をすることも可能です。
そのため、javaよりも自由な言語仕様だという印象を受けました。

かなり脱線してしまいました、話を戻してメリットの2点目にいきます。

クラスの使い回し

これは理解しやすいと思います。
クラスを設計してしまえば、引数を変更したインスタンスをいくつも作成することができます。
そのため、同じメソッドや変数宣言を何回も記述する必要がありません。
また、バグが発生した時に(うまく設計していれば)発生箇所の当たりをつけやすく、修正箇所が最小限になります。
今回の例であれば、一度霊に教えてしまえば、シュワちゃん、一般市民、ジャッキー、セガールにも憑依するだけでりんごを絞るという処理を簡単に使い回すことができます。
さらに、りんごを絞るのではなく皮を剥きたいだけだった。という場合も大元の霊の動作を修正するだけで対応できます。

クラスの使い方

クラスの使い回しが理解しやすいと言いつつ実装の段になると、クラス連携の方法の区別がつかずにごっちゃになっていました。
Pythonの言語仕様が理解しづらいのか、参考情報の収集法がまずかったのか、頭が追いついていなかったのか。
モヤモヤしてましたが、テックスコアさんのjava入門にある継承とインスタンスのページを読むと少し区別がつくようになりました。
別に回し者じゃありませんよ!

7.継承 | TECHSCORE(テックスコア)

8.インスタンスの生成 | TECHSCORE(テックスコア)

結果、クラス使い回し初心者の私が意識すれば良さそうなのは継承とインスタンスの生成だと結論づけました。
他にも色々あるみたいですが、複雑なものに手を広げすぎる前にシンプルなもので実装!
物足りなかったり痒いところに手が届かなくなってきたらまた勉強すればいい。

クラス使い回しの二つの方法について書いていきますが、クラスは二種類に分けた方が良いと考えて区別しています。

  1. モジュールを使用する際に隠蔽される・処理を実装するクラス
  2. モジュールを使用する際に使用・上記のクラスをまとめるクラス

継承

1.の処理を実装するクラスで使用し、2つ以上のクラスで同じ処理が現れた時に使うものだと考えています。
同じ処理部分を抽出して親クラスとして記述し、子クラスに継承させるという使い方です。
1.のクラス内にインスタンスを生成すると実数が含まれてしまうので、避けるべきだと思います。

インスタンスを生成

2.のクラスをまとめるクラスにおいては必須で、1.のクラスのインスタンスを記述しメソッドを実行するという流れ。
Pythonでは継承できるクラスが無制限なので、クラスの使い回しをするなら継承だけでいい気がします。
しかし、javaでは継承できるクラスは1つだけ。何か理由がありそう。
クラスやメソッドの名前を明示するためなのか。隠蔽されないクラスに実数があることが良いのか。
クラス内でインスタンス化する時、クラスやメソッドの名前から処理内容や扱うデータを類推できると見通しがつきやすいです。
そのため、名前はその辺りを意識してつけていこうと思います。

さいごに

いかがだったでしょうか。
他の言語を学ぶとわかることもあると言われており、オブジェクト指向が強いjavaを学ぶことで少し学べたような気がします。
ただ、大いに間違っている可能性もありますので、そのような点を発見した場合は指摘していただければ嬉しいです。
最後までお読み頂きありがとうございました。みなさんの理解のきっかけになれば幸いです。