views:

204

answers:

10

Let's say I want to make a HumanBody class. And I want to store the length of each limb.

HumanBody.LeftArmLength = 14;
HumanBody.RightArmLength = 14.1;
HumanBody.LeftLegLength = 32;
HumanBody.RightLegLength = 31.9;

That's all well and good, but it seems like it would be better to do something like:

HumanBody.Arm.Left.Length = 14;
HumanBody.Arm.Right.Length = 14.1;
HumanBody.Leg.Left.Length = 32;
Humanbody.Leg.Right.Length = 31.9;

So this would involve making sub classes. Is what I'm describing something that is considered a "Good Practice"? It seems like this is a more organized way to store data.

Edit: this example is quite simple, but if there are 100 different pieces of data to store, this seems like a much better method to use.

+8  A: 

It depends on the real situation. Here there doesn't seem to be very much point in grouping the two arms together and the two legs together... the concept of "a pair of arms" isn't generally a useful one.

On the other hand, if you were talking about grouping "DeliveryAddress1", "DeliveryAddress2", "DeliveryTown", "DeliveryZipCode", "BillingAddress1", "BillingAddress2", "BillingTown", and "BillingZipCode" into two instances of an Address class, that's a different matter.

Basically, do the individual parts belong together as part of a natural grouping? Is this group something you might want to handle in a composite manner? Could you see yourself passing the group to some other piece of code? If so, that sounds like a good unit of encapsulation. If not, maybe you're just complicating things unnecessarily...

Jon Skeet
+2  A: 

What is your primary object? It appears to me that it's the HumanBody and that appendage lengths are simply attributes of that.

By all means have an Arms collection if you think you're going to find a lot of humans with more than two arms :-)

But I think you'll be able to get away with your first solution for your given example. Once an object starts getting hundreds of attributes then, yes, you should think about organising it in a more hierarchical fashion. As to the best way to do that, we'd have to see your actual requirement.

paxdiablo
+10  A: 

Left and right Arms are instances of Arm so perhaps:

HumanBody.LeftArm.Length = 14; 
HumanBody.RightArm.Length = 14.1; 
HumanBody.LeftLeg.Length = 32; 
Humanbody.RightLeg.Length = 31.9; 

Presumably, you need to consider cases where someone might not have both arms or legs.

[Note: As was noted in the comments, these should ideally be set in a constructor, rather than using properties. This is a general principle: Construct objects in a coherent, usable state.]

Mitch Wheat
I was gonna post an answer but let me simply add an addendum to yours. *I hope you cannot assign my arm length on the fly. You are better off using a constructor of some sort.*
ChaosPandion
Well, you have this: `public void Torture(HumanBody person) { person.Arm.Left.Length++; person.Arm.Right.Length++; ... }` for a stretching bench (I don't know the right english word for that sort of thing)
Lasse V. Karlsen
I agree, my arm's are best spent when they don't have dynamic lengths. I vote the underlying Length property has a protected setter
Jimmy Hoffa
@Lasse - Absolute genius!
ChaosPandion
lol, in all seriousness though, make them immutable. And if they are missing an arm or leg then `length` is 0. As Brian Gideon mentioned, this version doesn't imply that the person has one arm and one leg each with a left and right side, as would `HumanBody.Arm.Left.Length`
jasongetsdown
+3  A: 

You might have

HumanBody.LeftArm.Length

where your classes would be something like:

public class HumanBody
{
    public Arm LeftArm { get; private set; }
}

public class Arm
{
    public double Length { get; private set; }
}

But I don't see the advantage in your arm property

public class HumanBody
{
    public Arms Arm { get; private set; }
}

public class Arms
{
    public Arm Left { get; private set; }
    public Arm Right { get; private set; }
}

public class Arm
{
    public double Length { get; private set; }
}

I don't see how that model matches a useful design of a human body (are you ever going to say to another object "here's the two arms of this humanbody"?). Basically there is no reason to compose objects from smaller parts unless that parts are a sensible units in and off themselves. There is an argument for an arm being a separate class, a pair of arms less so.

I think it also depends on the depth and width of your hierarchy. In the example I've given above there is no real gain. You've replaced one double property with a class that contains a single double, maybe slightly more legible, but certainly no more functional. On the other hand, a situation like:

HumanBody.LeftArm.Forefinger.Length = 3.2

Lends itself to composition over the much less legible (and less functional)

HumanBody.LeftArmsForefingerLength = 3.2
Martin Harris
A: 

Setting up your object hierarchy depends on what you're needs are. Sometimes it makes sense, sometimes it doesn't.

For example:

//This make sense
public class Person {

    public String FirstName { get; set; }
    public String LastName { get; set; }
    public String FullName
    {
        get { return String.Format("{0} {1}", FirstName, LastName); }
    }

    public Address Address { get; set; }
    public PhoneNumber[] ContactNumbers { get; set; }
}

//This does not make as much sense, but it's still possible.
public class Person {
    public Name Name { get; set; }
    public Address Address { get; set; }
    public PhoneNumber[] ContactNumbers { get; set; }
}

The second example doesn't make as much sense because the Name object just doesn't seem to be they type of object that will get reused, but it's just a container within a container. So it's ok to do that, but personally to me I'd do the first method.

myermian
When management says they need a `MiddleName`, then you'll be glad to have the `Name` object!
Jeffrey L Whitledge
@Jeffrey Why wouldn't you be able to just the MiddleName field to the Person class? Why would it _have_ to go into a Name object?
myermian
@myermian - You could add the MiddleName field to the Person class. It doesn't have to go into a Name object. I never said it did. But I think a case could be made that a Name class might simplify things in some cases. One might also argue that each of the address fields could be defined in the Person object. I wouldn't say that, though. I would argue that Address should be a separate class *even if the Address object is never reused anywhere else*. Name might be the same way, depending on the system.
Jeffrey L Whitledge
@myermian - Also I meant the exclaimation in my first comment to convey that I was kidding. Next time I'll stick with the industry standard marking. :-D
Jeffrey L Whitledge
+1 for using the _industry standard marking_
myermian
+1  A: 

There is no broad sweeping rule for choosing either one. The decision really boils down to whether the attributes really are associated with the main class or whether you think you will need to divide attributes into separate classes so that those classes can be passed around and manipulated on their own.

By the way, I know your examples are rather contrived, but the second one has the implication that the HumanBody has only one arm and only one leg both with a left side and a right side. In this particular case it might be better to have RightArm, LeftArm, RightLeg, and LeftLeg properties that return their respective class instances. Of course that brings you back to the first example if the only attribute you want associated with limbs is their length...just food for thought.

Brian Gideon
A: 

It depends on how you use the laterality. If you had functions that needed to operate on a whole side of the body at once, you could have something like this:

class Bilateral
{
    Arm arm;
    Leg leg;
    Eye eye;
    Ear ear;
    ...
}

class HumanBody
{
    Bilateral left, right;
    Liver liver;
}

So you would do something like HumanBody.liver.weight = 3.14; or HumanBody.left.arm.length = 12.34;.

Gabe
A: 

Just to put my two cents in I vote we acquiesce to the natural bisymmetry of a human:

public class Digit { public double Length { get; set; } }

public class HumanSide
{
    public Digit Arm { get; set; }
    public Digit Leg { get; set; }
    public Digit MiddleFinger { get; set; }
    public Digit Foot { get; set; }
    public Digit Nose { get; set; }
}

public class Human
{
    public HumanSide Right { get; set; }
    public HumanSide Left { get; set; }
}
Jimmy Hoffa
But if you're trying to make an accurate model of a real human, the lengths will **not** be equal, except in weirdo mutants. In most humans, the lengths will be close, but not exactly equal. (Hey, it's a floating point precision problem!)
Brian S
A: 

I think it is shortsighted to limit the human body to a left and right arm. Some people have no arms. Some people have only a right arm. What if a person's arm comes more from the front?

Of course we can apply these principles to legs as well.

With regard to limiting lengths to the constructor, are prostheses taken into account?

MCain
A: 

I think that composition makes the most sense here, however I'd recommend dynamic composition over static composition in order to account for anatomical anomalies (missing limbs, etc)

public abstract class Body
{
   public Dictionary<string, BodyPart> BodyParts { get; set; }
   //other properties, methods, etc.
}

Your BodyPart class might be purely virtual...

public abstract class BodyPart
{
}

You can add specific types of body parts:

public abstract class Limb : BodyPart
{
   public float Length { get; set; }
   public abstract void Move();
}

If you need to, create subclasses for arms, legs, etc

public class Arm : Limb
{
   public override void Move()
   {
      Debug.WriteLine("Arm is moving");
   }
}

Finally, you can subclass your body to be human-specific:

public class HumanBody : Body
{
   //properties and methods specific to the human body
}

Putting it all together, you can create a robust HumanBody dynamically:

HumanBody body = new HumanBody();
Arm leftArm = new Arm(){ Length=24F };
Arm rightArm = new Arm(){ Length=24F };
//create other parts
body.BodyParts.Add(new KeyValuePair<string, BodyPart>("LeftArm",leftArm));
body.BodyParts.Add(new KeyValuePair<string, BodyPart>("RightArm",rightArm));
//add other parts

While this is very flexible, the obvious disadvantage is that you'll have to do a fair amount of casting if you actually want to use any of the body parts:

Arm leftArm = body.BodyParts["LeftArm"] as Arm;
leftArm.Move();

Not bad for 2 lines of code, but should you need to coordinate movements the added overhead of casting could be too onerous.

That's my 2c.

Brian Driscoll
Arm.Broken = True, otherwise, why would you be CASTing it??? ;-)
FastAl
Instead of 'Arm leftArm = body.BodyParts["LeftArm"] as Arm;' I would think it would be reasonable to do 'Arm leftArm = (Arm) body.BodyParts["LeftArm"]'. I would most certainly want an exception to be thrown if I discovered that my left arm was actually a leg.Although I guess in that case, it would have to be somebody else doing the "throwing."
jloubert