Click here to Skip to main content
Click here to Skip to main content

OOP Manifesto on Best Practices, Basic Rules & General Ideas

, 16 Jun 2005
Rate this:
Please Sign up or sign in to vote.
Object Oriented Programming has been around for a long while, so I know there are a thousand articles, books and various writings on the subject. My goal in writing this one is just to clarify ONE MORE TIME some very basic aspects of OOP that I think are sometimes missed or overlooked.

Introduction

Object Oriented Programming has been around for a long while, so I know there are a thousand articles, books and various writings on the subject. You probably have written one when attending the university you graduated from for one of your computer science classes. My goal in writing this one is just to clarify one more time some very basic aspects of OOP that I think are sometimes missed or overlooked. I feel that the things I will discuss in this article, and the recognized aspects we know and love, if followed, can make you a better designer. We will talk about the various basic concepts of OOP design, and even how to branch off to more advanced methodologies like GOF Design Patterns. I hope with this article to help us all to refresh what we know, and to better understand the concepts that make up professional programming.

BackGround

There are four major aspects or discussions associated with modern OOP. I say four major ones with respect to all the minor ones which apply. Let's start with the most common:

  1. Encapsulation.
  2. Inheritance.
  3. Abstraction.
  4. Polymorphism.
  5. I have also heard of Modularity, Interface, Type, Class, etc...but let's keep it simple.

Ok, I grouped this article using only aspects 1-4, since all the other aspects kind of fit in here, so be patient, hopefully I won't leave anything out. This is not meant to be the mother-of-all OOP manifestos, so don't expect that in a 5-10 paragraph article. There is much more depth than I can write here and now.

Encapsulation

What is Encapsulation? Most descriptions I have read describe Class, which is the basic description for most of the objects you use in OOP. Class is a way to describe an object that has some public, protected, and private attributes, parameters or methods. Think about that. There are several levels of access to things associated with class. Why do we need that? Well public is easy, it allows every other thing out there to do something to it. Protected talks about inherited members accessing it, but private...why do we need ANYTHING private? Private access within a class is a very important aspect. Encapsulation is a rule of OOP because of private members. Private members give us a way of controlling how the insides of a class works, in fact, it is the reason we can have those insides. But why do we need to limit access in a class?

Everything in OOP is a rule, a contract or some other way to limit and therefore define something. So the aspect of encapsulation is the rule of: If You Don't Need To Know, Then You Don't! or I Let You See Only What You Need To See. Take this case as an example. You have a class on a Strategy (a pattern in which a class encapsulates an algorithm), and this strategy class has a method DoSomething(Person person), and as you can see its parameter is Person, which is another class. Inside Person, there are a number of variables, including one that sets the Person object's state. Valid states are Working, Tired, and Sleeping.

                
        public class StrategyClass
        {                         
            public virtual void DoSomething(Person person)
            {
                if(person is Employee)
                    person.Status = Status.Working;
                   else if(person is HouseWife)
                    person.Status = Status.Tired;
                   else if(person is Loafer)
                    person.Status = Status.Sleeping;
            }
        }
           
        enum State{Working, Tired, Sleeping}

        public class Person
        { 
                private State _state;
                private string _name;
                
                public string Name
                {
                    get{return _name;}
                    set{_name = value;}
                }
                public State State
                {
                    get{return _state;}
                    set{_state = value;}
                }
        }

We pass person into the strategy class method DoSomething because the Person relies on that method to change its state. This is a valid reason to pass an object like Person into the method. This is also part of the StateFul Design Pattern. Let's say though that Person no longer needs its state changed, and only needs to pass its parameter Name into the strategy. Well a good encapsulation rule is, only pass in what is needed. So why would you pass in the whole object Person? This could lead to other functionality on person being passed that has no business going into the DoSomething method, which as the code in that method got more complex, could confuse someone who was not familiar with it. Severe coding mistakes are made like this. If instead, we only passed into the DoSomething method the parameter Name, then we could be sure that the method was only working on the one particular and expected parameter we passed and not doing something unexpected. This is really important when dealing with Bridge, Adapter or Facade patterns, which hide functionality from outside access. If we passed a complex object into a method which required only simple data, we might be passing in or introducing unexpected bugs and behavior. Also, looking back at the Strategy class method DoSomething, if we didn't need to modify the object, we might only pass in the parameters we did need.

Another thing to consider when thinking about encapsulation in design is try to only pass in the least complex data you need to. In other words, as in the example above, since we no longer need to manage the state of Person and instead use only its name as an input parameter, we would only pass in the most primitive parameter, in this case a string parameter Name. This helps to decouple your different classes, keeping changes across codebases from rendering unexpected results. With tightly coupled codebases, you get spaghetti code and changing one thing can break seven others. Encapsulation rules if followed properly can prevent this from happening, by providing more modular code.

Another aspect of encapsulation deals directly with Bridge, Adapter or Facade patterns as well as interfaces. All these things use a form of encapsulation to limit and control code access. We use encapsulation inside a class to let the class handle its special units of work. We use encapsulation with one or more classes to define many units of work as one larger one. So we get groups and sub-systems by allowing each piece to only do what it needs to do.

Inheritance & Abstraction

Inheritance is the ability of one object to be derived by overloading the constructor(s), methods and accessors of a parent object. In java and c++ this is known as subclassing. Inheritance is important because many times an object contains some base functionality that another one also needs. If this occurs then the base or parent object should be defined in such a way that several common child objects can use the same common functionality from the parent. The parent should not contain or hide appropriately any functionality not common to all its children. Abstraction is the derived method from inheritance. Abstraction is the ability to abstract or define common functionality for a class to a class that cannot be instantiated which can hold this functionality. This keeps code common to many objects from being duplicated, it also holds hands tightly with polymorphism, which allows that classes can change by different implementations of their instance (we will cover this later).

A good way to understand some rules about inheritance is to take an example where you have a single class with functionality you desire (like Parent below), and build two new classes that both have this functionality (Derived1 & Derived2).

            .... We start with a single class
            class Parent 
            {
                void DoSomething(string thing)
                {
                    //some code
                }            
            }    


        .... these two classes created from Parent.
        .... Parent becomes Derived1    
            class Derived1
            {
                void DoSomething(string thing)
                {
                    //some code
                }    
            }


        .... We copy Derived1 to create Derived2
            class Derived2
            {
                void DoSomething(string thing)
                {
                    //some code
                }    
            }

Now you have two codebases to maintain in separate places. How would you fix this obvious functional and maintenance issue? You could build an abstract class that holds the desired functionality, and inherit both implementation classes from the abstract parent class.

            abstract class Parent 
            {
                void DoSomething(string thing)
                {
                    //some code
                }            
            }
            
            class Derived1 : Parent
            {
                ...no need to have a DoSomething method...exisits on the parent
            }
            class Derived2 : Parent
            {
                ...no need to have a DoSomething method...exisits on the parent
            }

Note: Be careful with inheritance. People who don't understand inheritance appropriately can make the mistake of creating too many layers of inheritance, and severely over complicate their code. I have seen and heard of this happening in professional environments, and it is usually because the programmers responsible for this bungle did not start out with a good set of rules or coding standards, and did not understand OOP enough to know better. A good rule of thumb for inheriting from another class is not more than two layers deep, if more than that is needed, maybe another approach is advisable.

Polymorphism

Polymorphism is the ability of a class to change according to its implemented instance. A good example of polymorphism is a class that is inherited from another class, but changes the values or internal functions of its inherited methods, overriding them. Another is when a class changes its type based on its instance, or any other changes like state, interfaces, etc. Inheritance is a form of polymorphism, as is abstraction, but polymorphism is not limited to compliable aspects of a class, it can also take the form of instance states, or variations. One way of seeing if a class is polymorphic is to inherit several subclasses from the class, create an instance, then change the instance from subclass to subclass based on external (or internal) conditions.

            abstract class H2O....


            class Ice : H2O....

            

            class Water : H2O....


            class Steam : H2O....



            H2O _h2o;

            if(temp < 50)
                _h2o = new Ice();
            else if(temp > 50 && temp < 120)
                _h2o = new Water();
            else if(temp > 120)
                _h2o = new Steam();

We see in the example above that we have a base class H2O, and three subclasses, Ice, Water & Steam. Each subclass is H2O, but which instance of H2O depends on the ambient temperature. Each implementation instance of H2O has its own specific properties, like solidity or fluidity or gaseousness and each can handle these properties in different ways. This is a good example of polymorphism because the inherited classes Ice, Water & Steam all display different behaviors depending on their particular instance, which is a state of H2O.

General Rules

There are a few generalized rules when dealing with OOP that I would like to cover. One would be the KISS rule (keep It Simple, Silly). That is my favorite, since I believe that the more complicated the functional solution is, the simpler the actual code behind that solution should be. Just make it as complicated as it should be, no more, no less. This is very realistic when you understand and start to practice better OOP and Design Patterns. You find very complex things can often be accomplished using less code and more objects. I don't say that more objects is a good thing by itself, good design is dependant on how well the developer understands OOP principles and even more important, how well and how diligently they apply them. A programmer who applies OOP rules lazily or gets in a hurry and simply ignores the rules, will be less efficient in the long run than one who takes a little time to do some basic design strategy. I hear from a lot of inexperienced programmers that they do not have time to use better design practices or even time to think about design. This is a misnomer, since it is their job to think about design, and it ends up costing them more time and effort later to write spaghetti code than to take a few minutes and do some planning and analysis.

I would say another good rule is learn refactoring principles. Martin Fowler has an excellent book on the subject, titled simply Refactoring, which has some real pearls about designing better code. For those of you who don't know, refactoring is a OOP practice or methodology that helps you continually improve your code as you are writing it, without getting bogged down in too many improvements at once, or too many that have an overreaching effect on too much of your code.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Christopher G. Lasater
Web Developer
United States United States
Christopher G. Lasater
 
I am also a published author, please check out my book:
ISBN: 1-59822-031-4
Title: Design Patterns
Author:Christopher G. Lasater
More from my web site
Amazon.com


Comments and Discussions

 
SuggestionWhat are the best practices in OOP? Pinmemberaiboogie3-Aug-12 7:15 
GeneralRe: What are the best practices in OOP? PinmemberChristopher G. Lasater3-Aug-12 7:31 
GeneralThe difficulty with OOP... PinprotectorMarc Clifton17-Jun-05 1:34 
GeneralRe: The difficulty with OOP... Pinmembertom_dx17-Jun-05 2:06 
GeneralRe: The difficulty with OOP... Pinmemberchris lasater17-Jun-05 4:09 
GeneralRe: The difficulty with OOP... Pinmemberchris lasater17-Jun-05 4:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.141015.1 | Last Updated 16 Jun 2005
Article Copyright 2005 by Christopher G. Lasater
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid