My struggles in understanding and learning about Object Oriented design, and the tools and knowledge I've taken from them.

Friday, October 10, 2008

OOP Architecture

OOP is necessary. If you have any aspirations of doing programming as a career, or in any sort of non-amateur way, especially in a way that interacts with other programmers, you must know OOP.

One of my struggles in OOP has been getting past the technical concepts, and figuring out how to apply OOP to make an application OOP.

One of the conclusions I came to after years (literally) of working to figure this stuff out, is that an application is not OOP until it has multiple classes/objects that interact with one another.

So, consider an example of a human who has a dog. Suppose one of the things that the dog does is get the paper for its human. There are things to consider, as far as what makes the human inclined to have a paper, and what makes the dog inclined to fetch the paper. Then there's the question of how those 2 things interact to create a situation where the dog fetches the paper for the human.

So, I'll throw a few things out there, as far as what I would expect could influence a dog's willingness to get a paper for its human:

1. The human wants to read a paper
2. The dog's life contentment is high (it gets fed, it gets petted, etc)
3. There is some sort of incentive to the dog (for instance, the dog gets a treat if it gets the paper)

There are really a lot of other things that could influence these things, but I won't get into them, because I don't want to create a 10,000 line application. But just to kind of keep them in mind, we may have also considered things like how far the dog had to travel to get the paper, whether or not there are distractions the dog has that would limit its willingness to get the paper, the thickness of the paper, and the current mood of the dog that may trump the dog's overall life contentment, or even immediate incentives for the dog.

So, let's build the classes. As I see it, there are 2 nouns (dog and human), and therefore, a need for 2 classes. There are the above properties we need to consider (human's want to read paper, dog's life contentment, dog's incentive). Then there are a few actions that would need to occur in order to mechanize the dog getting the human a paper. Let's iterate them:

1. Human asks dog for the paper
2. Dog fetches the paper
3. Dog brings paper to human
4. Human gives dog a treat

Each of the above 4 things are actions, and therefore would constitute our methods. The methods interact with the above properties to affect the likelihood that each of these methods would fire off another method, or influence the value of some property.

So, let's write dog, first:

class Dog
{
    //Notice that _contentmentInLife is private, and it's
    //accessed via the public property ContentmentInLife
    private int _contentmentInLife;
    public int ContentmentInLife
    {
       get { return _contentmentInLife; }
       set { _contentmentInLife = value; }
    }

    //This is the incentive the dog has to fetch the paper
    //It is true or false (boolean), so if it is true,
    //the dog has incentive
    //to get the human a paper
    private bool _currentIncentive;
    public bool CurrentIncentive
    {
       get { return _currentIncentive; }
       set { _currentIncentive = value; }
    }

    ///This is the publicly exposed method that
    ///allows outside classes to get an object of Dog
    ///To bring a paper

    public void FetchPaper()
    {
       if(this.CurrentIncentive && this.ContentmentInLife > 3)
       {
           Console.WriteLine("The dog fetches the paper");
           bringPaperToHuman();
       }
    }

    ///bringPaperToHuman() is not accessible from outside of
    ///the Dog class - this is an example of encapsulation
    ///This is called by the publicly exposed
    ///method FetchPaper(),
    ///FetchPaper() determines whether or not to call
    ///bringPaperToHuman()
    ///based on the values of CurrentIncentive and     ///ContentmentInLife
    private void bringPaperToHuman()
    {
       Console.WriteLine("The dog brings the paper to the human");
    }

    //When the dog receives a treat, its contentment in life increases
    public void ReceiveTreat()
    {
         this.ContentmentInLife++;
    }
}

Notice that the above example is quite simple, but I think it gives a good example of what classes end up looking like when they're done.

On to human:

class Human
{
    private bool _wantsToReadPaper;
    public bool WantsToReadPaper
    {
       get { return _wantsToReadPaper; }
       set
       {
       _wantsToReadPaper = value;

       }
    }

    private bool _hasPaper;
    public bool HasPaper
    {
       get { return _hasPaper; }
       set { _hasPaper = value; }
    }

    //Note that this returns true if the human wants to read the paper
    //and does not already have the paper
    public bool IncentivizeDog()
    {
       ///If the human wants a paper, and
       ///doesn't already have a paper
       ///then human will proceed to ask the
       ///dog for a paper
       if(this.WantsToReadPaper && !this.HasPaper)
       {
           Console.WriteLine("Human says: Dog, give me a paper, and I'll give you a treat");
           return true;
       }
       //If the human either already has a paper, or they don't want
       //the paper, return false;
       else
       {
         Console.WriteLine("Human says: I don't want a paper, dog");
         return false;
       }
    }

    //Another boolean, because they're a good tool to manage
    //Interaction between classes - as demonstrated below
    //Only give dog a treat if human has paper
    public bool GiveDogATreat()
    {
       if(this.HasPaper)
       {
         Console.WriteLine("Here you go, dog. Here's a treat");
         return true;
       }
       else
       {
         Console.WriteLine("No treat for you!");
         return false;
       }
    }
}

The next step in the process, once we have the "architecture" is to instantiate the objects and have them interact:

class RealLife
{
    //StartLife simply starts the process of instantiation and usage
    public void StartLife()
    {
       //Instantiate our dog and human
       Dog Fido = new Dog();
       Human Tim = new Human();

       //Set base properties
       Fido.ContentmentInLife = 4;
       Tim.HasPaper = false;
       Tim.WantsToReadPaper = true;

       if(Tim.IncentivizeDog())
       {
         Fido.CurrentIncentive = true;
         Fido.FetchPaper();
         Tim.GiveDogATreat();
         Fido.ReceiveTreat();    
       }    
    }
}

The RealLife class makes this application come to life. It sets the preconditions of how happy the dog is, whether the human wants a paper, and then it proceeds to have the human incentivize Fido, and then tells Fido to fetch the paper, has Tim give Fido a treat, and has Fido receive a treat.

One thing you may notice about the RealLife class is that it is facilitating, and actually controlling the interaction between Human and Dog. In fact, Human and Dog have absolutely no knowlege of one another, and at present, have no capacity to know anything about one another. This is one strategy of getting classes to communicate with each other.

One of my struggles in learning OOP is figuring out the best way to get classes to communicate with one another, and in my quest to write the best code possible, I've come across all kinds of strategies to solve this problem, and it speaks to concepts like COUPLING, COHESION, and is often solved by implementing strategies like DESIGN PATTERNS.

Digg It

No comments:

Followers

Search This Blog

Powered by Blogger.