views:

368

answers:

9

Here is my code:

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;
}

I need to call all the member functions of Gun over a Soldier object. Something like:

soldier.gun.fire();

or

soldier.getGun().load(15);

So which one is a better design? Hiding the gun object as a private member and access it with getGun() function. Or making it a public member? Or I can encapsulate all these functions would make the implementation harder:

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

So which one do you think is the best?

+1  A: 

There's no golden rule that applies 100% of the time. It's really a judgement call depending on your needs.

It depends on how much functionality you want to hide/disallow for the gun from access to the Solider.

If you want to have only read only access to the Gun you could return a const reference to your own member.

If you want to expose only certain functionality you could make wrapper functions. If you don't want the user to try to change Gun settings through the Soldier then make wrapper functions.

Generally though, I see the Gun as it's own object and if you don't mind exposing all of Gun's functionality, and don't mind allow things to be changed through the Soldier object, just make it public.

You probably don't want a copy the gun so if you make a GetGun() method make sure that you aren't returning a copy of the gun.

If you want to keep your code simple then have the soldier responsible for dealing with the gun. Does your other code need to work with the gun directly? Or can a soldier always know how to work/reload his own gun?

Brian R. Bondy
gun is private in the above example, it would need to be made either public or an accessor method written like getGun()
Fermin
@Brian: I need to access all the public members of the Gun object from the Soldier.
pocoa
@pocoa: Then return a reference to it via GetGun() or just make the gun public. That is probably easiest. When you want to make changes to your Gun interface later on you won't need to change your soldier class' interface too.
Brian R. Bondy
+1  A: 

Provide a "getGun()" or simply "gun()".

Imagine one day you may need to make that method more complex:

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

Also, you may want to provide a const accessor so people can use a const gun on a const soldier:

const Gun &getGun() const { return gun_; }
Stephen
+20  A: 

I would say go with your second option:

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

Initially it's more work, but as the system gets more complex, you may find that a soldier will want to do other things before and after firing their gun (maybe check if they have enough ammo and then scream "Die suckers!!" before firing, and mutter "that's gotta hurt" after, and check to see if they need a reload). It also hides from the users of the Soldier class the unnecessary details of how exactly the gun is being fired.

FrustratedWithFormsDesigner
+1 for your description of the Soldier class :P
Billy ONeal
I love your examples :)
Dinah
Also, don't say loadGun, say prepareWeapon. This way when your soldier is in a tank, he isn't fumbling with his pistol when he should be rotating the tank cannon.
jmucchiello
+7  A: 

The Law of Demeter would say to encapsulate the functions.

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

This way, if you want some type of interaction between the soldier and the gun, you have a space to insert the code.

Edit: Found the relevant article from the Wikipedia link: http://www.ccs.neu.edu/research/demeter/demeter-method/LawOfDemeter/paper-boy/demeter.pdf The paperboy example is very, very similar to the soldier example you post.

bryanjonker
And keep in mind to avoid falling into the "Law of Low Dot Counts" (http://haacked.com/archive/2009/07/14/law-of-demeter-dot-counting.aspx)
Martinho Fernandes
Great link Martinho! Perfectly sums up my feelings about LOD.
Matthieu M.
In this case, given "Gun" is a constructor parameter, I don't think this applies. It doesn't break encapsulation since the client class needs to instantiate the gun to begin with. That's not to say that a Soldier::Attack(Target *t) method is not a better approach.
Stephen
I think a lot of it depends on how much the soldier acts -- if the soldier is a container containing a gun and some stats, then the you're just counting dots. But if the soldier is an actual object that needs to be manipulated, the the I see the LOD being useful.
bryanjonker
@bryanjonker: Soldier is not just a container object.
pocoa
@bryanjonker: Thank you for the links!
pocoa
@pocoa: Yeah, in that case, I'd go with the "soldier.fire()" // calls Gun.fire(). If you have the controlling class say "soldier.getGun().fire()", you're basically telling the object to take the gun from the soldier, fire, and give the gun back. The soldier object doesn't really have any control over the gun, which may not be your intention.
bryanjonker
@Martinho: great link. I'm going to start calling it the "occasionally useful suggestion of demeter".
rmeador
+5  A: 

Indeed, it depends a lot about how much control you want to have.

To model the real world, you might even want to completely encapsulate the gun object, and just have a soldier.attack() method. The soldier.attack() method would then see whether the soldier was carrying a gun, and what the state of the gun was, and fire or reload it as necessary. Or possibly throw the gun at the target and run away, if insufficient ammunition were present for either operation...

Matt Gibson
+10  A: 

First off, you'd be violating the Law of Demeter by accessing the Gun from outside the Soldier class.

I would consider methods like these instead:

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

This way you could also implement your fist, knife, grenade, baseball bat, laser cat, etc.

Austin Salonen
+1 for the laser cat!
FrustratedWithFormsDesigner
+2  A: 

Usually my decision is based on the nature of the container class (in this case, Soldier). Either it is entirely a POD or is not. If it's not a POD, I make all data members private and provide accessor methods. The class is a POD only if it has no invariants (i.e. there is no way an external actor can make its state inconsistent by modifying its members). Your soldier class looks more like a non-POD to me, so I would go to the accessor method option. If it would return a const reference or a regular reference is your own decision, based on the behaviour of fire() and the other methods (if they modify gun's state or not).

BTW, Bjarne Stroustrup talks a little about this issue in his site: http://www.artima.com/intv/goldilocks3.html

A sidenote: I know that's not precisely what you asked, but I'd advice you to also consider the many mentions made in other answers to the law of Demeter: to expose action methods (that act on gun) instead of the entire gun object via a getter method. Since the soldier "has" the gun (it is in his hand and he pulls the trigger), it seems more natural to me that the other actors "ask" the soldier to fire. I know this may be tedious if gun has many methods to act on, but maybe also these could be grouped in more high-level actions that the soldier exposes.

Fabio Ceconello
No, Soldier is not just a container class. Thanks for the link.
pocoa
+3  A: 

If you expose gun, you allow things beyond the member functions of the Gun, which is probably not a good idea:

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

If you use getGun(), the calls look a little ugly, but you can add functions to Gun without modifying Soldier.

If you encapsulate the functions (which I recommend) you can modify the Gun or introduce other (derived) classes of Gun without changing the interface to Soldier.

Beta
A: 

Encapsulate the functions to provide a consistent UI even if you later change the logic. Naming conventions are up to you, but I normally don't use "getFoo()", but just "foo()" as accessors and "setFoo()" as setters.

  • return reference-to-const when you can (Effective C++ Item #3).
  • Prefer consts, enums, and inlines to using hard coded numbers (Item #4)
  • provide unique naming conventions for your private members to distinguish them from arguments
  • Use unsigned values where they make sense to move errors to compile time
  • When const values, like maximums, apply to an entire class. Make them static.
  • If you plan to inherit, make sure your destructors are virtual
  • initialize all members to sane defaults

This is how the classes look after that. CodePad

#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;
}
jkyle