Pages

Wednesday, October 27, 2010

Design a class

     There is a golden rule in designing a class: keep to real-world thinking. Although that rule sounds simple, it's quite nebulous - what is real-world thinking? There are a number of rules we can follow to keep our code particularly readable:

• We can start or end local variables with a special character so that we are always clear about what variable is being set. The most common method is to start local variables with an underscore, e.g. _Name, _Age, etc.

• To strictly follow OOP guidelines, nearly all variables should be either private or protected - they should not be accessible from outside of an object.

• We can put accessor functions to set and get private variables. To get a variable called _Age, we can define a function Age(). To set a variable called _Age, we can define a function SetAge().

• We can put variables and functions as low down in our inheritance as they can go without repetition - if we find one object has variables and functions it is not supposed to have, we have gone wrong somewhere. For example, while dolphins can swim, gorillas cannot, so we should not put a swim() function into a mammal class "just to save time".

• Muir's law is important in this regard: "When we try to separate anything out by itself, we find it hitched to everything else in the universe". We should keep our classes distinct and separate from each other to begin with rather than try to hack them apart later on.

     If we are wondering why it is that accessor functions should be used to read and write variables, it is because OOP practice dictates that objects should be self-contained - other parts of our program should be able to work with them using simple function calls. For example, imagine we are programming a strategy game where players can control multiple cities. Each city brings in various amount of food depending on how many workers are there. Now, if the player changes the number of workers in a city to 800, we could just effect the change with code like this:

$City->_Workers = 800;

     However, how would that make any change to the amount of food coming in? It wouldn't, so we would need to make your code this:

$City->_Workers = 800;
$City->_FoodSurplus = WORKER_SPEED * 800;

     Now, what if a few weeks later we decide that people can upgrade their cities to build farms and processing facilities that increase the amount of food being made? We'd have to search through our code for all the times we change the _FoodSurplus variable. Later, if we want to make more changes, we need to repeat the procedure again and again and again - the calling code needs to have explicit knowledge of how to handle changes to the number of workers in a city.

     This is not how OOP works. In the OOP world, a city would be supposed to handle all these calculations itself, leaving the calling code looking like this:

City->SetWorkers(800);

    The SetWorkers() function would contain all the other changes such as altering the _FoodSurplus level, but the key is that the calling code wouldn't need to know anything about that - any changes to the SetWorkers() calculation only needs to be made in one place.