単一責任パターンはクラスに対してどの程度具体的である必要がありますか?


14

たとえば、コンソールとの間のあらゆる種類の入出力メソッドを備えたコンソールゲームプログラムがあるとします。でしょうが、すべてのシングルでそれらを保つためにスマートにinputOutputクラスなど、より具体的なクラスにそれらを打破startMenuIOinGameIOplayerIOgameBoardIO各クラスが1-5程度のメソッドを持っていることなど、?

また、同じメモで、それらを分解する方が良い場合は、IO名前空間に配置して、呼び出しをもう少し冗長にするのが賢明でしょうIO.inGameか?



関連:入力と出力を組み合わせる正当な理由を見たことがない。そして、意図的にそれらを分離すると、私のコードはずっときれいになります。
Mooingダック

1
とにかく、lowerCamelCaseのクラスはどの言語で命名しますか?
user253751

回答:


7

更新(要約)

私はかなり冗長な回答を書いたので、ここにすべてを要約します:

  • 名前空間は良いです、それが理にかなっているときはいつでも使用してください
  • クラスを使用するinGameIOplayerIO、SRPの違反となる可能性があります。IOを処理する方法とアプリケーションロジックを結合している可能性があります。
  • ハンドラークラスによって使用される(または共有される)汎用IOクラスがいくつかあります。これらのハンドラクラスは、生の入力をアプリケーションロジックが意味のある形式に変換します。
  • 出力についても同じことが言えます。これはかなり汎用的なクラスで実行できますが、ゲームの状態をハンドラー/マッパーオブジェクトに渡し、内部のゲームの状態を汎用IOクラスが処理できるものに変換します。

あなたはこれを間違った方法で見ていると思います。アプリケーションのコンポーネントの機能でIOを分離していますが、私にとっては、ソースとIOの「タイプ」に基づいてIOクラスを分離する方が理にかなっています。

開始するベース/ジェネリックKeyboardIOクラスをいくつか用意MouseIOし、それらをいつどこで必要とするかに基づいて、上記のIOを異なる方法で処理するサブクラスを用意します。
たとえば、テキスト入力はおそらくゲーム内のコントロールとは異なる方法で処理したいものです。各ユースケースに応じて特定のキーを異なる方法でマップしたいことがありますが、そのマッピングはIO自体の一部ではなく、IOの処理方法です。

SRPにこだわり、キーボードIOに使用できるクラスがいくつかあります。状況に応じて、おそらくこれらのクラスとは異なる方法でやり取りしたいと思うでしょうが、唯一の仕事はユーザーが何をしているかを教えてくれることです。

次に、これらのオブジェクトをハンドラオブジェクトに挿入します。ハンドラオブジェクトは、生のIOをアプリケーションロジックで使用できるものにマップします(たとえば、ユーザーが"w"を押すと、ハンドラはそれをにマップしますMOVE_FORWARD)。

これらのハンドラーは、キャラクターを動かし、それに応じて画面を描画するために使用されます。かなり単純化しすぎていますが、その要点は次のような構造です:

[ IO.Keyboard.InGame ] // generic, if SoC and SRP are strongly adhered to, changing this component should be fairly easy to do
   ||
   ==> [ Controls.Keyboard.InGameMapper ]

[ Game.Engine ] <- Controls.Keyboard.InGameMapper
                <- IO.Screen
                <- ... all sorts of stuff here
    InGameMapper.move() //returns MOVE_FORWARD or something
      ||
      ==> 1. Game.updateStuff();//do all the things you need to do to move the character in the given direction
          2. Game.Screen.SetState(GameState); //translate the game state (inverse handler)
          3. IO.Screen.draw();//generate actual output

私たちが今持っているのは、生の形式でキーボードIOを担当するクラスです。このデータをゲームエンジンが実際に意味のあるものに変換する別のクラスは、このデータを使用して関連するすべてのコンポーネントの状態を更新し、最後に別のクラスが画面への出力を処理します。

すべてのクラスには1つの仕事があります。キーボード入力の処理は、処理する入力の意味を知らない/気にする/知る必要があるクラスによって行われます。入力を取得する方法を知っているだけです(バッファ付き、バッファなし、...)。

ハンドラは、これをアプリケーションの残りの内部表現に変換して、この情報を理解します。

ゲームエンジンは、翻訳されたデータを取得し、それを使用して、関連するすべてのコンポーネントに何かが起こっていることを通知します。これらの各コンポーネントは、衝突チェックであろうと、キャラクターアニメーションの変更であろうと、1つのことだけを行います。それは重要ではなく、個々のオブジェクトにまで及びます。

その後、これらのオブジェクトは状態をリレーし、このデータはに渡されますGame.Screen。これは本質的に逆IOハンドラーです。内部表現を、IO.Screenコンポーネントが実際の出力を生成するために使用できるものにマッピングします。


コンソールアプリケーションであるため、マウスはなく、メッセージやボードの印刷は入力と密接にリンクされています。あなたの例では、あるIOgameサブクラスを持つ名前空間またはクラス?
慎三

@kuhaku:それらは名前空間です。私が言っていることの要点は、アプリケーションのどの部分に基づいているかに基づいてサブクラスを作成することを選択した場合、基本的なIO機能をアプリケーションロジックと効果的に密接に結合しているということです。最終的には、アプリケーションの機能で IO 担当するクラスになります。それは、私にとって、SRPの違反のように聞こえます。名前と名前空間のクラスについては、私は95%の時間の名前空間を好む傾向があります
エリアスヴァンOotegem

私の答えを要約するために答えを更新しました
エリアスヴァンOotegem

ええ、それは実際に私が抱えている別の問題です(IOとロジックを結合する)ので、実際には入力を出力から分離することをお勧めしますか?
慎三

@kuhaku:それは本当にあなたが何をしているのか、そして入力/出力がどれほど複雑なのかによって異なります。(生の入力/出力対ゲームの状態を翻訳)ハンドラはあまり異なる場合でない場合、あなたはおそらく、入力と出力のクラスを分離することをお勧めします、単一のIOクラスは結構です
エリアスヴァンOotegem

23

単一の責任原則を理解するのは難しい場合があります。私が便利だと思ったのは、あなたが文章を書く方法のように考えることです。多くのアイデアを1つの文に詰め込もうとはしません。各文は、1つのアイデアを明確に述べ、詳細を延期する必要があります。たとえば、車を定義する場合、次のように言います。

通常は4つの車輪を備え、内燃エンジンを動力とする道路車両。

次に、「乗り物」、「道路」、「車輪」などを個別に定義します。あなたは言ってみません:

2つの場所の間の大通り、ルート、または道で人を輸送するための車両。舗装されているか、または車両の下に固定された車軸を中心に回転する4つの円形オブジェクトを持ち、それを生成するエンジンを搭載して移動できるように改良されているガソリン、オイル、またはその他の燃料を空気で燃焼させることによる動力。

同様に、クラス、メソッドなどを作成し、可能な限り簡単に中心概念を述べ、詳細を他のメソッドやクラスに委ねる必要があります。文章を書くのと同じように、文章の大きさについて厳しい規則はありません。


それで、多くの単語ではなく少数の単語で車を定義するのと同じように、小さな特定の、しかし類似のクラスを定義するのは問題ありませんか?
慎三

2
@kuhaku:小さいのは良いことです。特定は良いです。あなたがそれを乾いた状態に保つ限り、似たようなものは問題ありません。デザインを簡単に変更しようとしています。物事を小さく具体的にすることで、どこで変更を加えるかを知ることができます。乾いた状態に保つと、多くの場所を変更する必要がなくなります。
ヴォーン

6
2番目の文は、「くそ、先生はこれを4ページにする必要があると言いました...「原動力を生成します」です!!」
corsiKa

2

最善の方法は、それらを別々のクラスに保持することだと思います。少人数のクラスは悪くありません。実際、ほとんどの場合、良いクラスです。

あなたの特定のケースに関しては、分離することで他のハンドラに影響を与えることなく特定のハンドラのロジックを変更するのに役立ち、必要に応じて新しい入出力メソッドを追加する方が簡単になると思います。


0

単一責任プリンシパルは、クラスが変更する理由は1つだけあるべきだと述べています。クラスに複数の変更理由がある場合は、他のクラスに分割し、構成を利用してこの問題を排除できます。

あなたの質問に答えるために、私はあなたに質問しなければなりません:あなたのクラスは、変更する1つの理由しかありませんか?そうでない場合は、変更する理由が1つだけになるまで、より特殊なクラスを追加し続けることを恐れないでください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.