Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I grasp the concept of pure OOD?

Tags:

c++

oop

ooad

I still have a little problem grasping the concept of pure OOD.

Let's say we have a class Human and we live in a world where sometimes the human walks (the brain commands the legs), where sometimes trees disappear (the human notices that) and sometimes humans randomly hit each other.

The first two cases are really a no-brainer:

class Tree {
  private:
    void disappear()
    {
       // call onTreeDisappeared() for all human observers
    }
};

class Human {
  public:
    // The human notices that a tree disappeared
    void onTreeDisappeared();
  private:
    int x, y, z;
    // Human wants to walk forward
    void moveForward();
    // Hit another human, possibly causing him to fall down
    void hit(Human &target);
};

Now I've got a really bad problem with the hit method. Of course it's nice that you can say

anna.hit(bob);

Up until here I think it's nice (please complain if something's bad) and reads like prose (which good OOP code should). But how do you transfer the hitting into OOP? If Anna hits Bob and Bob falls down then the falling down is neither directly caused by Anna nor Bob. It's caused by the hit, a loss of balance and by physics.

I know only 2 options for this case, but somehow I think both suck:

public: void fallDown()
{ z = 0; }

public: void hit(Human &target)
{
  bool targetFallsDown = true; // could be random or any algorithm you like
  if(targetFallsDown)
  { target.fallDown(); }
}

In this case Anna "falls down" Bob. But this totally doesn't make any sense. It's not like Anna grabs Bob's body and moves it towards the ground. But there's another option:

private: void fallDown()
{ z = 0; }

public: void onHitCausesMeToFallDown()
{ fallDown(); }

public: void hit(Human &target)
{
  bool targetFallsDown = true; // could be random or any algorithm you like
  if(targetFallsDown)
  { target.onHitCausesMeToFallDown(); }
}

In this case Bob's body "notices" that the hit is causing him to fall to the ground, he'll then "move himself" to the ground. I think this is better than the first option but this still somehow doesn't feel right.

So please, smart OOP folks, explain to me, how do you handle cases when in the real world A modifies the state of B but in the OOP world only B should modify the state of B.

like image 463
Kira M. Backes Avatar asked Apr 23 '11 14:04

Kira M. Backes


2 Answers

I think your dilemma arises because you are not modelling the passage of time. Everything happens at once when you do anna.hit(bob).

If you modelled your entities as running their own state machines, then things will start looking more like reality:

  1. anna.hit(bob) causes bob's state to change to falling.

  2. For the next few cycles, bob keeps plummeting in his falling state.

  3. Eventually bob's state changes to on_ground.

  4. Then bob's state changes to cries_for_mommy.

Check out the State design pattern for ideas on how to implement start charts in an OO language. The "Gang of Four's" Design Patterns book covers this topic.

like image 21
Emile Cormier Avatar answered Nov 02 '22 23:11

Emile Cormier


I think that you are falling into the trap of trying to model the "real" world in classes without having a purpose to your design.

What is your program supposed to do? Once you've worked that out, then you can start to design which aspects of the real world you want to model and which parts of the real world don't matter and don't need to be modelled. Making a class map onto a concrete real world object type just because it is easy to visualise is often a mistake. You only have to model concepts that matter to your program.

OOD is about using techniques such as abstraction and polymorphism to allow objects to interact with each other without having to know about each other's implementation.

In your implementation you need to decide what behaviours you want to model and what knowledge each object wants. For example, you might want a human to work out whether he wants to fall down based on how hard he is hit.

void Human::receiveHit(Hit hit)
{
    if (hit.IsBigForThisWeight(this->weight))
        this->fallDown();
}

Note that the thing that hits me doesn't need to know or care what effect it will have on me. That's my reaction to the hit. I've also modelled a "hit" object because it makes sense to my program. Anything can hit me by creating a Hit object and making me receive it. In the future I can be hit by a bus or a train without any changes to my class.

like image 124
CB Bailey Avatar answered Nov 03 '22 01:11

CB Bailey