BETA

サブクラス一覧を配列にして取得(.subclasses禁止縛り)

投稿日:2020-04-15
最終更新:2020-04-15

※この記事はQiitaから移行した物です。

#やりたいこと

class LandSuper  
end  

class RepublicSub < LandSuper  
end  

class MonarchySub < LandSuper  
end  
# SuperとかSubはスーパークラス、サブクラスの意味  
# この状況で[RepublicSub,MonarchySub]という配列が欲しい  

#縛り

###その1:.subclasses禁止
Class.subclassesはActiveSupportというライブラリにしか入っていません。(Ruby on Railsに付属しているらしい)
現在作っているプログラムが既に相当重いので、おそらくここでしか使わない機能のためにライブラリをrequireする案は却下。

###その2:サブクラスに追記禁止
「仕様変更でサブクラスを増やした時に書き忘れてバグ発生」みたいな未来が見えるので、サブクラス側は一行も実装しません。
RepublicSubとMonarchySubは空クラスで行きます。

#ダメだった方針……ObjectSpace.each_object
ActiveSupportの中ではObjectSpace.each_objectというメソッドを使っているらしく、
これを実装すればいいという情報がネットには多かったです。
https://www.xmisao.com/2014/02/11/ruby-how-to-get-subclasses.html
http://nomnel.net/blog/ruby-get-subclasses/
しかしObjectSpaceというのが何なのか調べてもよくわからず、コピペして改変してみても(良い子のみんなは真似しないでね)
動かなかったため断念。

#筆者の結論……inherited (Class)
https://ref.xaio.jp/ruby/classes/class/inherited
自動で動くメソッドと言えば、インスタンスが作成された時に自動で回るinitializeメソッドが有名ですね。
あれの「newされた時」ではなく、「継承された時」バージョンがinheritedメソッドです。
というわけで筆者の結論は、
「まず空の配列をスーパークラスのクラス変数として用意し、継承されるたびにサブクラスを追加する」です。

class LandSuper  
    @@polity=[]  

    def self.inherited(subclass)  
        @@polity.push(subclass)  
    end  

    def self.polity  
        @@polity  
    end  
end  

class RepublicSub < LandSuper  
end  

class MonarchySub < LandSuper  
end  

p LandSuper.polity # => [RepublicSub, MonarchySub]  

クラスオブジェクトではなく文字列オブジェクトで取得するなら、
配列に入れるsubclassに.nameを付けてください。

class LandSuper  
    @@polity=[]  

    def self.inherited(subclass)  
        @@polity.push(subclass.name)  
    end  

    def self.polity  
        @@polity  
    end  
end  

class RepublicSub < LandSuper  
end  

class MonarchySub < LandSuper  
end  

p LandSuper.polity # => ["RepublicSub", "MonarchySub"]  

100回継承したら100回、1000回継承したら1000回クラス変数の値が変わるので、
コメントで説明するなり.freezeするメソッドを作って継承し終わったら凍結させるなりした方がいいかもしれません。

#未検証項目
挙動を確認したのはスーパー、サブ共に自作クラスの場合なので、
「既存クラスをオープンしてinheritedメソッドを使ったらどうなるのか」はわかりません。
また「継承が一段ではなく、孫クラスひ孫クラスまで継承したらどうなるのか」もわかりません。
誰か教えてください。(悪質なコメ稼ぎ)

※その後Qiitaのコメントにて

ObjectSpace.each_object(Class).select{ |c| c.superclass == Numeric }  
# => [Complex, Rational, Float, Integer]  

というやり方を教えてもらいました。

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

例の件でQiitaから移行します。

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう