質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

Q&A

解決済

4回答

1636閲覧

サブクラスにおいてpublicメソッドを定義することについて

tsuyo_244

総合スコア219

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

0グッド

1クリップ

投稿2020/07/10 02:43

編集2020/07/10 05:21

同僚と議論していたのですが、有識者の方にもご意見を伺いたく質問させていただきました。

個人的には「サブクラスにpublicメソッドを定義すること」はNGと考えています。
理由はポリモーフィズムが破壊される(?)からです。

ただし、スーパークラスのpublicメソッドをオーバーライドするためにサブクラスで同名のpublicメソッドを定義するのは問題ないと思いますので、正しくは「サブクラスにてスーパークラスには存在しないpublicメソッドを定義すること」がNGになります。

私自身オブジェクト志向をしっかりと理解している自信はないので、認識の間違いや他にも「こういう時はサブクラスにpublicメソッドを定義したほうがよい」といったケースがございましたらご教授いただけると嬉しいです。

どうぞよろしくお願いします。

追記

みなさまご回答ありがとうございました。
色々なご意見をいただき自信のプログラムの書き方を見直すきっかけにもなりました。

「トンカチをもつとなんでもクギに見える」とはまさにこのことですね。。

ベストアンサー悩ましかったのですが、maisumakunさんにさせていただきました。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

swordone

2020/07/10 02:54

「ポリモーフィズムが破壊される」の根拠をお願いします。
tsuyo_244

2020/07/10 03:18

サブクラスを他のサブクラスと入れ替えて使う想定のコードがあるが、 あるサブクラスではそれが上手くいく一方で他のサブクラスに差し替えると動作しないような状況を想定していました。 サブクラスAにしかないhogeメソッドを定義し、それと代替する想定のサブクラスBにhogeメソッドがない(かつスーパークラスにもない)場合にサブクラスAのインスタンスでしか動作しなくなってしまうため「ポリモーフィズムが破壊される」のかなと考えていました
tsuyo_244

2020/07/10 05:22

shiracamusさん、ありがとうございます ポリモーフィズムひとつとってもそれにまつわる色々な機能や実装方法があるのですね。 勉強しなくては...
guest

回答4

0

ベストアンサー

理由はポリモーフィズムが破壊される(?)からです。

されません。親クラスやインターフェースで参照する場合、サブクラス独自に作成したメソッドは見えませんので、ポリモーフィズムに何ら影響しません。

投稿2020/07/10 02:45

maisumakun

総合スコア145121

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tsuyo_244

2020/07/10 03:05

さっそくご回答ありがとうございます! よくよく考えるとおっしゃるように「サブクラスに独自のpublicメソッドを定義する」だけではポリモーフィズムは破壊されませんね。。 このあたりが同僚とも認識にずれがあった部分かもしれないです、ありがとうございます。 あらためて整理すると下記の場合にポリモーフィズムが破壊されると考えてよいのでしょうか? 「サブクラスにてスーパークラスには存在しないpublicメソッドを定義し、サブクラスから作成したインスタンスから独自メソッドを読ぶ」 もちろん他の方がおっしゃるようにあくまで「サブクラスを他のサブクラスと交換して使う用途」が想定されていた場合になるとは思うのですが...
maisumakun

2020/07/10 03:07

> 下記の場合にポリモーフィズムが破壊されると考えてよいのでしょうか? そもそも、それはやることがポリモーフィズムから外れています。
BluOxy

2020/07/10 03:08

破壊されるとはどういうことでしょうか。swordoneさんがコメントに書かれているように破壊されることの根拠があると良いと思います。
tsuyo_244

2020/07/10 03:21

maisumakunさん BluOxyさん ありがとうございます。「ポリモーフィズムが破壊されると考える根拠」と考えている内容を質問欄に記載させていただきました。
Zuishin

2020/07/10 03:23 編集

ポリモーフィズムの使い方を完全に勘違いしています。差し替えるなら別に派生しなくても元のクラスを拡張すればいいだけです。
maisumakun

2020/07/10 03:25

> サブクラスを他のサブクラスと入れ替えて使う想定のコードがあるが、 あるサブクラスではそれが上手くいく一方で他のサブクラスに差し替えると動作しないような状況を想定していました。 やりたいこと自体がポリモーフィズムから外れていますね。hogeメソッドを定義したインターフェースを用意するのが筋です。
tsuyo_244

2020/07/10 03:35

maisumakunさん Zuishinさん ありがとうございます。そもそもの私の「ポリモーフィズム」に対する理解が間違っているようですね。 Zuishinさんにいただいたご説明とコード例を読み返しつつもう一回勉強しなおしてみます。 hogeメソッドを定義したインターフェースを用意すれば、サブクラスすべてにhogeメソッドが存在することが保証されるのでサブクラスを入れ替えても動作する = ポリモーフィズムと考えていたのですが、その理解が間違っていたのかなと思います(インターフェースを定義してサブクラスにメソッドの存在が保証されることと、ポリモーフィズムは別の概念) オブジェクト志向って難しいですね...
maisumakun

2020/07/10 03:37

> hogeメソッドを定義したインターフェースを用意すれば、サブクラスすべてにhogeメソッドが存在することが保証されるのでサブクラスを入れ替えても動作する = ポリモーフィズムと考えていたのですが、その理解が間違っていたのかなと思います いえ、それは正しいです。そのようなインターフェースを「使っていない」から、メソッドがない事態に陥ったのではないのですか?
tsuyo_244

2020/07/10 04:57

maisumakunさん、ありがとうございます。 インターフェースを使うことでサブクラスが特定のメソッドを持っていない事態は避けることができると認識しています。 皆さんの回答などをふまえて考え直すと、「親クラスにないpublicメソッドをサブクラスに定義すること」がポリモーフィズムを破壊してしまうのではなく「ポリモーフィズムで代替を想定したサブクラスのメソッドがサブクラスによって実装されていたりされていなかったりすること」になるのかなと思いました。 そしてその状況を避けるための仕組みとして親クラスにインターフェースを実装してサブクラスすべてに必要なメソッドの実装を保証するということですかね?
guest

0

難しすぎたようなので、簡単に書き直します。

サブクラスをスーパークラスにキャストし、スーパークラスとして使うのがポリモーフィズムです。

サブクラスにあり、スーパークラスに無いメソッドは、ポリモーフィズムを使う以外の場所、つまりキャストしない場所で使います。

投稿2020/07/10 02:52

編集2020/07/10 03:46
Zuishin

総合スコア28656

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tsuyo_244

2020/07/10 03:49

ありがとうございます!コード例までつけていただきわかりやすいです。 ReadHelloWorldにてreader.ReadLine();としている箇所について readerに入っている中身がFileStreamのインスタンスであろうとNetworkStreamのインスタンスであろうとStreamから派生したクラスであればちゃんと"Hello World!"を取得してきてくれるということですよね(どうやって取得するかはそれぞれ異なるとしても) この状況がつまりはFileStreamとNetworkStreamを状況に応じて差し替えても動作する(どちらにもReadLineメソッドが実装されているから)という理解でした。
tsuyo_244

2020/07/10 05:08

書き直しありがとうございます。 >サブクラスにあり、スーパークラスに無いメソッドは、ポリモーフィズムを使う以外の場所、つまりキャストしない場所で使います。 これをソースコード内に存在しないように無理な実装を普段やっていた気がします。。 (デザインパターンでいうテンプレートメソッドのように大枠の処理の流れを親クラスで定義して、 サブクラスでその中身を実装していく方法をよく使っています)
guest

0

単純に考えて、ポリモーフィズムが必要ないような振る舞いはサブクラスのメソッドとして定義するかと思いますが、いかがでしょう。

理由はポリモーフィズムが破壊される(?)から

「ポリモーフィズムを使う」という手段自体が目的と化していると感じました。

何を目的にポリモーフィズムを使うのかを洗い出して、それに準ずるものはスーパークラスからオーバーライドするなりインターフェースから実装するなりすると良いと思います。

ポリモーフィズムを使う目的(とメリット)がなければ、「サブクラスにpublicメソッドを定義することはNG」ではありません。

投稿2020/07/10 02:50

BluOxy

総合スコア2663

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tsuyo_244

2020/07/10 03:10

なるほど...「ポリモーフィズムという手段自体が目的と化している」というのは納得しました! 私が普段サブクラスを入れ替える想定がない箇所も後々差し替えできるように無意識にやってしまっていて、ポリモーフィズムを乱用しているような気がしてきました。(おそらく親クラスがでかくなってしまいがちなのもそれが原因かも...) 自分のコーディングの仕方を見直すきっかけにもなりました。 ありがとうございます。
BluOxy

2020/07/10 03:13

> 私が普段サブクラスを入れ替える想定がない箇所も後々差し替えできるように無意識にやってしまっていて、 差し替えすることに明確なメリットがあればポリモーフィズムするのは正しいと思います。 > ポリモーフィズムを乱用しているような気がしてきました。(おそらく親クラスがでかくなってしまいがちなのもそれが原因かも...) ポリモーフィズムを乱用しているから親クラスがでかくなっている訳ではないと思います。ここは単純にクラス設計の話と思います。
tsuyo_244

2020/07/10 05:02

ありがとうございます。 >ポリモーフィズムを乱用しているから親クラスがでかくなっている訳ではないと思います。ここは単純にクラス設計の話と思います。 親クラスに複数の責任をもたせてしまうことが原因というのはもちろん大きいと思っています。 一方で、本来サブクラスにPublicメソッドとして定義するだけにしておけば単純になるところを、過剰に親クラスにも同名のメソッドをつくったりすることが心当たりがありまして、それが親クラスの肥大化にもつながっているのかなと感じました。
BluOxy

2020/07/15 02:19 編集

> サブクラスにPublicメソッドとして定義するだけにしておけば単純になるところ 恐らくそれはスーパークラスに対して振る舞いを持たせるのではなく、インターフェースを通して振る舞いだけ持たせておくのが良いと思います。 例えば、Takashi is Human, Masaru is Humanであり、Human can jump.だとします。 ならば、 ① TakashiとMasaruはHumanを継承し、HumanにはIJumpableを実装する ② Humanには実装を持たないためIJumpableが持つjumpメソッドを抽象メソッドとして定義する ③ TakashiとMasaruはjumpメソッドの実装を強制されるため、そこで振る舞いを定義する。 といったノリです。 こうするとHumanクラスは実装を持たないため、Humanクラス自体が肥大化することはありません。 するとスーパークラスの具体的な実装が減っていくはずで、仮にそれが0となるようならHumanクラスが不要となります。つまり、Takashi can jump, Masaru can jumpとコードで表現するだけで良くなります。
tsuyo_244

2020/07/10 05:50

インターフェースを用いてスーパークラスには振る舞いだけをもたせておくのがスマートですね。 具体的なご説明でわかりやすかったです、ありがとうございます。
guest

0

ポリモーフィズムを使いたいときに、サブクラスで拡張されたメソッドをつかわなきゃいけないのであれば、問題だろうと思います。(ポリモーフィズムが破壊されるの意味はこれであってますか?)

しかし、継承を使うのは何もポリモーフィズムのためだけではありません。もう一つ差分プログラミングという目的があります。

たとえば、

クラスAとクラスBとクラスCはそれぞれ、メソッドがコピペになっていたとします。
この場合、共通部分をクラスDにまとめ、クラスA〜CはクラスDの派生クラスとするとコードの重複を減らせます。

ところで、クラスA〜Cは全く違うコンテキストでつかわれるためクラスDにキャストされる必要はありません。

上記のような場合もあり、一概に継承を使うからポリモーフィズムがあるとは言えないこともあります。
(この場合、意味上でクラスDにまとめられるかとか、移譲でやったほうが良くないかなどの検討課題が個別に存在します、)

他にも、インスタンス化した直後は派生クラスで定義したメソッドは使うけど、実際の処理をするときにはポリモーフィズムを使って処理するなどのパターンがあります。

オブジェクト指向で可読性が高く書くことは重要ですが、どうすればよいかは常にコードを見ながら個別に判断したほうが良いと思います。

投稿2020/07/10 03:44

iwamoto_takaaki

総合スコア2883

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tsuyo_244

2020/07/10 05:11

ありがとうございます! よく考えるとおっしゃるように共通処理をまとめる目的で継承させますね。 状況に応じて最適な実装を考えることが大事というのもすごく参考になりました。 ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問