📝

親クラスから子クラスを操作しようと頑張ってみた

2023/08/29に公開

https://qiita.com/items/c67f4fec5ec95c46058e


お詫び

Qiitaの元記事にて、区切り線を「---」で書いている場所があり、これがZennの記法に干渉して一部うまく表示できない記事がある事を認識しています。
全ての記事を精査しきれていないため、お手数ですがお見かけの際は教えていただけると大変喜びます。


:::note warn
この記事はQiitaの下書きに放置していた記事の一つですが、久しぶりにタイトルを読むと何がしたいのか分からなかったため、残っている内容から頑張って読み解いて意訳しています。
:::

▶️オブジェクト指向を教える時にタイトルのような相談を受けるので、本気で考えてみようと思います。
▶️結論から言うと、is aとhas aをきちんと理解してないとこういう発想になってしまうんだろうな、と反省しきりです。

やりたいこと

  • Baseクラスで宣言したものをそれぞれのクラス間で使えるようにしたい
  • 親クラスで子クラスを管理したい
    • ▶️そもそもこのアプローチが既に設計レベルで問題です。呼び出し元で管理すべきです。
    • 【未検証】Javaでいうstaticな配列フィールドをBaseクラスに作ってBaseクラス型で宣言しておき、子クラスのコンストラクタで参照先アドレスを渡すとか、運用対処でインスタンス化した後に必ず実行するメソッド(セッター)を決めてそちらから格納するとか?

ちょっと考えてみました。

クラスの中にクラスを作ってみる方法

コードが古い上に意味があると思えないので、興味があればどうぞ。

<details>
<summary>展開</summary>

今回はPython2系で。

class Base:
  def __init__(self, arg):
    self.arg = arg
    self.hoge = self.Hoge(arg)
    self.fuga = self.Fuga(arg)

  def print_classname(self, string)
    print self.arg, string

  class Hoge:
    __hoge_var = 'hoge'
    def __init__(self, arg):
      self.arg = arg
    def print_classname(self)
      print self.arg + __hoge_var

  class Fuga:
    __fuga_tuple = ['fuga']
    def __init__(self, arg):
      self.arg = arg
    def print_classname(self)
      print self.arg + __fuga_tuple[0]

print Base('test').hoge.print_classname()

### クラスを継承する方法

``` inheritance.py
class Base:
  def __init__(self, arg):
    self.arg = arg
    self.hoge = self.Hoge(arg)
    self.fuga = self.Fuga(arg)

class Hoge(Base):
  __hoge_var = 'hoge'
  def __init__(self, arg):
    self.arg = arg
  def print_classname(self)
    super().print_classname(self.arg + __hoge_var)

class Fuga(Base):
  __fuga_tuple = ['fuga']
  def __init__(self, arg):
    super().__init__(arg)
  def print_classname(self):
    super().print_classname(self.arg + __fuga_tuple[0])

print Base('test').hoge.print_classname()

課題

なんとなくやりたい事が伝わればいいんですが、要は同じ要素をそれぞれの子クラスに入れるんじゃなくて、親クラスに入れて子クラスからもそれぞれに呼べるようにしたい、と。
そういう話なんですが、こういう事をやる場合はdefだと上位の階層に変数を定義するとか、classだとselfとかが使えますね。

とはいうものの、上記コードはエラーになります。
正直、この辺り(やりたい事は上記、ただし実装をどうすれば良いか、よく分からなかっていない)でいつもdefをネストして作っています。
defのネストも好きじゃないのでやりたくないんですが、こちらの方が直感的というか、とりあえず動くものが出来るんで扱いやすいんですよね。
このコードもclassをdefに変えてメソッドのselfを消したら期待した通りに動くものになります。
defでネストしまくればやりたい事は実現できていますが、あまりにもスマートからかけ離れていることと、見やすくするためだけにdefを使っている面があるので、あんまり良い使い方・作り方ではありません。

</details>

伝えたいこと(当時を振り返っての反省)

そもそも親クラスを子クラスで管理する、というアプローチの目的が分からないのですが、当時superクラス・subクラスという表現の関係性をparentクラス、childクラスという名前で作成していたからこのような混乱が発生してしまったのかな、という印象です。
人間的な発想だと親が子供を知らないのはおかしいでしょ、というのでなんか質疑応答していた記憶がぼんやりとありますが、結局のところクラスを継承して使う目的が管理ではなくて拡張なので、クラスを人ではなく設計図として認識できるように指導していればこんな話にはならなかったのかもしれません。

GitHubで編集を提案

Discussion