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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

オブジェクト指向

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

継承

継承(インヘリタンス)はオブジェクト指向プログラミングに存在するシステムです。継承はオブジェクトが各自定義する必要をなくし、継承元のオブジェクトで定義されている内容を引き継ぎます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

3回答

6964閲覧

単純なものでも継承より委譲を選んだほうが良いのか?

退会済みユーザー

退会済みユーザー

総合スコア0

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

オブジェクト指向

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

継承

継承(インヘリタンス)はオブジェクト指向プログラミングに存在するシステムです。継承はオブジェクトが各自定義する必要をなくし、継承元のオブジェクトで定義されている内容を引き継ぎます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

1グッド

1クリップ

投稿2019/12/08 10:20

編集2019/12/08 10:27

この質問のあと、他のトランプゲームにも応用が利くようにブラックジャックのコードを書き換えました。
(以下概略)

Python

1# game_components.py 概略 2class Card 3class Deck 4class PlayerBase: 5 self.hands: List[Card] 6 self.bankroll: int 7 8 self.draw() 9 10 @abstractmethod 11 self.score() # @propertyとして実装 12 13class GameBase: 14 self.deck: Deck 15 16 @abstractmethod 17 self.play() 18 19 @abstractmethod 20 self.reset()

Python

1# blackjack.py 概略 2class BlackjackPlayer(PlayerBase): 3 self.score() 4class BlackjackDealer(BlackjackPlayer): 5 self.score_based_strategy() # draw()でディーラーのみ必要 6 7class SimpleBlackjack(GameBase): # ベットなしのブラックジャック 8 self.__init__(): 9 super() 10 self.player: BlackjackPlayer 11 self.dealer: BlackjackDealer 12 self.play() 13 self.reset() 14 15class Blackjack(SimpleBlackjack): # ベットあり 16 self.__init__(): 17 super() 18 self.bet: int 19 self.default_bet: int 20 self.is_double_down: bool

コード全文は長さの制約により乗り切らなかったため、Gistに置いてあります。
blackjack.py | GitHub Gist

いくつかのトランプゲームのルールを調べたうえで、プレイヤーにはそれぞれの手札と種銭が、ゲームの場にはトランプ1組が共通すると判断してPlayerBase, GameBaseを抽出しました。
今後これらを継承する際もブラックジャックのコードと同様、1つのゲームXxxxにつきclass XxxxPlayer(PlayerBase), class XxxxDealer(PlayerBase or XxxxPlayer), 賭けなしのclass SimpleXxxx(GameBase), 賭けに伴う処理を含めたclass Xxxx(SimpleXxxx)の高々4つ程度しか作らず、何重にも入れ子になった継承はしないだろうと考えています。

ですが、このコードを書いたあとネットでクラス設計について調べていると、継承よりも委譲を強く推奨する意見が多く見られました。
1重, 2重程度の継承すらも悪のように書かれているものもあるので、初心者としてはどうすればいいのか困惑しています。
趣味で書いているものとはいえ、できれば良いコードにしたいです。
これくらいの規模(継承の深さ?)でも継承は徹底して避けたほうが良いのでしょうか?
よろしくお願いします。

LouiS0616👍を押しています

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

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

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

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

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

guest

回答3

0

むしろこの規模だと、10行に満たないBaseをちまちま作るご利益がないなと思ってしまいました。

Pythonはダックタイピングできるのでという事情で余計にそう見えているという面はあるかもしれないけど、にしても

「ブラックジャックのプレイヤーとポーカーのプレイヤーで異種試合」

とかやらないだろうから、インターフェースを統一したところでなにも嬉しくないし。逆に、その程度の統一しか求められてないし問題からしてもできないから、Baseが要らない子に。。。

どうせコンフリクトしないでしょということで実際的に問題にならないとしたら、理念の問題なのでどっちでもいいです。is-a関係を満たすので継承でおkというのも一理あるでしょう。委譲で書くと。。。あ、そもそも抽象基底しかなかったりすると委譲の議論に乗らない気がします。

投稿2019/12/08 12:18

編集2019/12/08 12:34
hayataka2049

総合スコア30933

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

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

0

ベストアンサー

「継承より合成」は、おそらく「継承か合成、どっちでもいいなら合成を使いましょう」という 経験則 なのかなと思っています。

経験則なので、これが何か論理的に正しいというわけではなく、ただ、継承か合成、どっちか迷ったら、とりあえず合成にしておいた方が良いということではないかなと思っています。

KISS の原則

継承の欠点は、本来使わないはずの機能も使える様になってしまうことです。使わない機能が使える様になって何が問題なのでしょうか?継承は「KISS の原則」と相反するものなのかなと思っています。

KISS の原則 (英: KISS principle) とは、「Keep it simple stupid.」(シンプルで愚鈍にする)、もしくは「Keep it simple, stupid.」(シンプルにしておけ!この間抜け)、もしくは「Keep it short and simple.」(簡潔に単純にしておけ)という内容の、1960年代の米国海軍において言われた、経験的な原理・原則[1]の略語。その意味するところは、設計の単純性(簡潔性)は成功への鍵だということと、不必要な複雑性は避けるべきだ、ということである。

「KISS の原則」も原則というよりも、おそらく 経験則 かなと感じますが。そのため「じゃあ、なんで設計の単純性(簡潔性)は成功への鍵なのか?」、あるいは「KISS の原則は正しいのか?」と聞かれると答えに窮してしまうのですが...

依存性逆転の原則

「継承より合成」は、言い換えれば「使うメソッドだけ渡して、使わないメソッドは使わせない」とも言えるかなと思います。これと同じことが「依存性逆転の原則」 に見られます。

「依存性逆転の原則」も過度に単純化している可能性があるのですが、「関数あるいはメソッドの引数にはオブジェクトをそのまま渡さず、必要な属性だけ渡せ」ということだと思っています。

python

1# player をそのまま渡すのではなく 2monster.damaged(player)

python

1# 必要な属性だけ渡す 2# Player.attack -> (damage, type) 3monster.damaged(player.attack())

「依存性逆転の原則」も論理的に正しいわけではなく、そういう経験則、どっちか迷ったらそうやっておくと良いとということなのかなと思っています。

今回のケースは?

今回のケースは BlackjackPlayer, BlackjackDealer と PlayerBase は、ほぼ完全に is-a 関係 を満たしている様に思えます。なので、個人的には継承でもいいんじゃないかなと思っています。今回のコードは、継承の方がわかりやすいコードなのではないかなと個人的に思っています(わかりやすさ、というのは個々人で議論の分かれるところではあるとは思うのですが)。

継承が一律に悪いわけではないのかなと思っています。継承の方が、即ち全部一括で渡してしまう方が、コードを書くのは楽です。依存性逆転の原則にしても、わざわざ必要な属性に分割したりして渡すのは面倒です。

これはちょうどグローバル変数を使うのと似ています。ウェブアプリケーションフレームワークの Flask は、グローバル変数を多用するというアンチパターンをあえて踏みながら、カジュアルに書けることで、とても人気を博しました(と個人的に思っています)。

is-a 関係 をちゃんと満たす、あるいはコードに変更があったとしても is-a 関係 満たし続けてくれることは、なかなか無いのかなという中で Go, Rust といった言語では、継承という機能が切られてしまったのかなと思っています。

それでも今回のケースでは、継承の方がわかりやすいんじゃないのかなと思ったりもしています。

まとめ

「継承より合成」あるいは「依存性逆転の原則」にしても、過度に単純化している可能性はあるのですが「必要なものだけ渡そう」ということなのかなと思います。

その背景としては「KISS の原則」がある様に感じます。原則というよりも経験則で、どっちか迷ったら「単純な方を」あるいは「必要なものだけ渡す方を」選択しておいた方が、無難なのかなと 思っています

投稿2019/12/08 11:30

編集2019/12/08 11:42
nico25

総合スコア830

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

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

0

こんにちは。

継承と移譲では、移譲の方が使い方は簡単です。使い方が難しいものは使わない方が良い場面もあります。
言語によっては学習難易度が低いことを重視しますので、そのような言語では使い方が難しいものは「悪」というケースも存在するようです。それは、そのプロジェクトの性質によると思います。

技術者としては、使い方が難しい機能を「使うことができない」のと「使うことはできるけど場面に応じて使わない」のでは明らかに後者の方が優れていると思います。

投稿2019/12/08 10:35

Chironian

総合スコア23272

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問