15

これが私のコードです:

class Soldier {
public:
   Soldier(const string &name, const Gun &gun);
   string getName();
private:
   Gun gun;
   string name;
};

class Gun {
public:
   void fire();
   void load(int bullets);
   int getBullets();
private:
   int bullets;
}

ソルジャーオブジェクトを介してGunのすべてのメンバー関数を呼び出す必要があります。何かのようなもの:

soldier.gun.fire();

また

soldier.getGun().load(15);

では、どちらがより良いデザインですか?ガンオブジェクトをプライベートメンバーとして非表示にし、getGun()関数を使用してアクセスします。それともパブリックメンバーにしますか?または、これらすべての関数をカプセル化すると、実装が難しくなります。

soldier.loadGun(15); // calls Gun.load()
soldier.fire(); // calls Gun.fire()

では、どれが一番いいと思いますか?

4

9 に答える 9

21

私はあなたの2番目のオプションで行くと言うでしょう:

soldier.loadGun(15); // calls Gun.load()
soldier.fire(); // calls Gun.fire()

最初はもっと手間がかかりますが、システムが複雑になるにつれて、兵士は銃を発射する前後に他のことをしたいと思うかもしれません(おそらく、十分な弾薬があるかどうかを確認してから、発射する前に「Diesuckers!!」と叫んでください。 、その後「痛い」とつぶやき、リロードが必要かどうかを確認します)。また、Soldierクラスのユーザーから、銃がどの程度正確に発射されているかについての不必要な詳細を隠します。

于 2010-04-06T14:56:55.760 に答える
11

まず、クラスの外から にアクセスすることは、デメテルの法則に違反することになります。GunSoldier

代わりに、次のような方法を検討します。

soldier.ArmWeapon(...);
soldier.Attack(...);

このようにして、拳、ナイフ、手榴弾、野球のバット、レーザー キャットなどを実装することもできます。

于 2010-04-06T15:02:26.857 に答える
7

デメテルの法則は、関数をカプセル化すると言うでしょう。

http://en.wikipedia.org/wiki/Law_of_Demeter

このように、兵士と銃の間で何らかの相互作用が必要な場合は、コードを挿入するスペースがあります。

編集:ウィキペディアのリンクから関連記事を見つけました:http: //www.ccs.neu.edu/research/demeter/demeter-method/LawOfDemeter/paper-boy/demeter.pdf ペーパーボーイの例は、あなたが投稿する兵士の例。

于 2010-04-06T14:57:02.080 に答える
5

実際、どれだけコントロールしたいかによって大きく異なります。

現実の世界をモデル化するには、銃のオブジェクトを完全にカプセル化し、soldier.attack() メソッドのみを使用することもできます。次に、soldier.attack() メソッドは、兵士が銃を持っているかどうか、銃の状態を確認し、必要に応じて発砲または再装填します。または、いずれかの操作に十分な弾薬が存在しない場合は、ターゲットに銃を投げて逃げる可能性があります...

于 2010-04-06T15:00:50.887 に答える
3

gun を公開すると、Gun のメンバー関数を超えたものが許可されます。これはおそらく良い考えではありません。

soldier.gun = anotherGun; // where did you drop your old gun?

getGun() を使用すると、呼び出しが少し見苦しくなりますが、Soldier を変更せずに Gun に関数を追加できます。

関数をカプセル化すると (これをお勧めします)、Soldier へのインターフェイスを変更せずに Gun を変更したり、Gun の他の (派生) クラスを導入したりできます。

于 2010-04-06T15:03:42.673 に答える
2

通常、コンテナ クラス (この場合は Soldier) の性質に基づいて決定します。完全に POD であるか、そうでないかのいずれかです。POD でない場合は、すべてのデータ メンバーを非公開にし、アクセサー メソッドを提供します。クラスは、不変条件がない場合にのみ POD です (つまり、外部アクターがそのメンバーを変更することによってその状態を矛盾させることはできません)。あなたの兵士クラスは私には非 POD のように見えるので、アクセサー メソッド オプションに進みます。const 参照または通常の参照を返すかどうかは、fire() およびその他のメソッドの動作 (銃の状態を変更するかどうか) に基づいて、独自の決定です。

ところで、Bjarne Stroustrup は彼のサイトでこの問題について少し語っています: http://www.artima.com/intv/goldilocks3.html

補足: それは正確にはあなたが尋ねたものではないことはわかっていますが、デメテルの法則に対する他の回答で行われた多くの言及も考慮することをお勧めします: 銃オブジェクト全体ではなく、アクション メソッド (銃に作用する) を公開するゲッターメソッド経由。兵士は銃を「持っている」(銃を手に持っており、引き金を引いている)ため、他の俳優が兵士に発砲を「依頼」する方が自然に思えます。銃に作用するメソッドが多数ある場合、これは面倒かもしれませんが、これらは、兵士が公開するより高レベルのアクションにグループ化することもできます。

于 2010-04-06T15:02:50.397 に答える
1

「getGun()」または単に「gun()」を指定します。

ある日、その方法をより複雑にする必要があるかもしれないと想像してみてください。

Gun* getGun() {
  if (!out_of_bullets_) {
    return &gun_;
  } else {
    PullPieceFromAnkle();
    return &secret_gun_;
  }
}

また、constアクセサーを提供して、const兵士にconstガンを使用できるようにすることもできます。

const Gun &getGun() const { return gun_; }
于 2010-04-06T14:56:47.547 に答える
1

100% 適用される黄金律はありません。それは本当にあなたのニーズに応じた判断の呼びかけです.

それは、銃が兵士にアクセスできないように、どの程度の機能を非表示/禁止するかによって異なります。

Gun への読み取り専用アクセスのみが必要な場合は、自分のメンバーへの const 参照を返すことができます。

特定の機能のみを公開したい場合は、ラッパー関数を作成できます。ユーザーが Soldier を介して銃の設定を変更しようとするのを望まない場合は、ラッパー関数を作成します。

ただし、一般的には、Gun は独自のオブジェクトであると考えています。Gun のすべての機能を公開することや、Soldier オブジェクトを介して変更を許可することを気にしない場合は、公開してください。

おそらく銃のコピーは必要ないので、GetGun() メソッドを作成する場合は、銃のコピーを返さないことを確認してください。

コードをシンプルに保ちたい場合は、兵士に銃の取り扱いを任せてください。他のコードは銃で直接動作する必要がありますか? それとも、兵士は常に自分の銃の操作方法やリロード方法を知っていることができますか?

于 2010-04-06T14:53:00.207 に答える
0

関数をカプセル化して、後でロジックを変更した場合でも一貫した UI を提供します。命名規則は自由ですが、私は通常 "getFoo()" を使用せず、"foo()" をアクセサーとして使用し、"setFoo()" をセッターとして使用します。

  • 可能な場合は const への参照を返します (効果的な C++ 項目 #3)。
  • ハードコードされた数値を使用するよりも、定数、列挙型、およびインラインを優先する (項目 #4)
  • プライベート メンバーを引数と区別するための一意の命名規則を提供する
  • エラーをコンパイル時に移動する意味がある場合は、符号なしの値を使用します
  • 最大値のような const 値がクラス全体に適用される場合。それらを静的にします。
  • 継承する予定がある場合は、デストラクタが仮想であることを確認してください
  • すべてのメンバーを正常なデフォルトに初期化する

その後のクラスの様子です。コードパッド

#include <iostream>
#include <string>
#include <stdint.h>

using namespace std;

class Gun 
{
public:
   Gun() : _bullets(0) {}
   virtual ~Gun() {}
   void fire() {cout << "bang bang" << endl; _bullets--;}
   void load(const uint16_t bullets) {_bullets = bullets;}
   const int bullets() const {return _bullets;}

   static const uint16_t MAX_BULLETS = 17;

protected:
   int _bullets;
 };

class Soldier 
{
public:
   Soldier(const string &name, const Gun &gun) : _name(name), _gun(gun) {}
   virtual ~Soldier() {}
   const string& name() const;
   Gun& gun() {return _gun;}

protected:
   string _name;
   Gun _gun;
};


int main (int argc, char const *argv[])
{
   Gun gun; // initialize
   string name("Foo");
   Soldier soldier(name, gun);

   soldier.gun().load(Gun::MAX_BULLETS);

   for(size_t i = 0; i < Gun::MAX_BULLETS; ++i)
   {
     soldier.gun().fire();
     cout << "I have " << soldier.gun().bullets() << " left!" << endl;
   }
  return 0;
}
于 2010-04-06T15:32:11.773 に答える