I wrote this article trying to explain Object Oriented Programming to people that actually don't know anything about the topic and I hope I am making it simple enough to be understandable.
Yet, I really expect that readers of this article know the basic about programming, like what's a variable, what's an array, loops and the idea of function calls (also known as procedures and methods).
I started to program computers when I was 11 years old (maybe 10 and a half, I am not really sure) using AMOS Basic. That language was focused in game development and was very simple (or, as some say, very limited). Such simplicity helped me better learn the programming constructs, like
elses, the logical operators used on
gotos, function calls etc. Also, by its limitations in "object allocations" (as it didn't really support object allocation) I learned how to use different arrays kept in sync to deal with grouping of information and how to reuse empty places in arrays for new items.
Some of the limitations of such language made me think about better solutions... and when I found the C language I found many of those better solutions. I spent some time trying to learn assembler after that (my first try wasn't successful), later I learned C++ (at the first look was terrible, but this is a flaw of the C++ libraries, not of the language) and, when I understood C++ I loved it even more. It had even more solutions for those problems that I was still finding in C. Actually, everything that I was looking for was related to Object Oriented Programming and, as I still see people misusing and misunderstanding OOP and I also receive many requests to explain OOP to new developers I decided to write this article.
Note: It is a personal view on OOP and a personal explanation on how I learned things. I will not be focusing on the "correct definitions" as, by the most basic definition, assembler can be an OOP language as we can create objects in assembler (and, in the end, any program can be seen as an assembler program).
When I started to program computers, I was using a language that didn't support objects at all. It only knew 3 data-types. Integer numbers (also known as whole numbers [1, 2, 3 are examples of integer numbers]), real numbers [like 3.14] and strings ["a text that can be presented to the user"]) and arrays of any of those 3 types.
The type of a variable was encoded in its name (a
string variable had a name that ended with a $, so
name$ meant a
string), an integer didn't had any special character and real numbers used a # postfix (so,
Price# will be a real variable).
I am not going to discuss naming rules. The only thing that matters is that by having only 3 kinds of data types it helped me understand and focus only on the programming keywords (like
gosub [or function calls] etc) and it also helped me understand how to built algorithms that reutilise an specific item in memory that was dead (actually, a dead enemy in a game) to put another one in its place as it didn't allowed me to "deallocate" memory.
Yet the fact that I could only use one of those 3 data-types or an array of those 3 data-types was something that bothered me. I actually created many functions to try to "automate" that repetitive work of allocation, deallocation and of "object construction". To exemplify, considering the language was used to create games and considering we can easily have many "enemies" on the screen, each one with its own X/Y position on the screen, it was not uncommon to have arrays like:
And so, if I wanted to execute the same action over all the enemies I actually needed to:
- Know that "someSize" value. I could extract it from any of the arrays but it was my responsibility to keep them in sync, to make them have an acceptable size to support all the enemies that could appear on the screen and even to know when an enemy was dead (maybe another array to tell that);
- I was forced to access all of those arrays in the same index to get all the information of a single enemy.
This may look silly, but I will try to explain it with rectangles. Imagine that you simply want to store a variable number of rectangles with their colors in memory and at some moment draw them on the screen.
You would actually need to have 5 arrays (left, top, width, height and color [we could actually store right and bottom instead of width and height, but this is not the important factor now]).
Imagine the function that draws all rectangles. It could look like this:
For I=0 To ArrayLength(RectanglesLeft)-1
DrawRectangle RectanglesLeft(I), RectanglesTop(I), RectanglesWidth(I), RectanglesHeight(I), RectanglesColors(I)
Wouldn't it be better if we could create a new "data-type" that had all those traits?
For example, a
Rectangle type with
Color so we could simply have an array of
This could simplify the code to something like:
For I=0 To ArrayLength(Rectangles)-1
r = Rectangles(I);
DrawRectangle r.Left, r.Top, r.Width, r.Height, r.Color
Or, even better, the
DrawRectangle could actually receive a
For I=0 To ArrayLength(Rectangles)-1
The advantage here is that we have a single array, so there's no trouble for accessing them all and it is impossible to make them get out of sync (this can happen if you resize the first and second array and there's an out-of-memory when resizing the third one).
Rectangle type it is guaranteed that all the traits will be there and, when calling another function, we don't need to pass 5 parameters, we can pass a single parameter of the right type. When dealing with the array directly we only need to deal with one array. Resizing it is also a single operation, so it either succeeds or fails entirely, without in-the-middle situations.
So, being able to create this
Rectangle type is our first step towards OOP (it actually was my first step towards OOP).
C allowed me to put all the traits that I wanted in a single array of a
struct could be given a name (
Rectangle in this case) and it could have all the traits that I needed (the
In a game (remember, I started with games) I needed traits like the X/Y position of the enemy in the screen, the direction that he was looking at, the speed that he moved, the kind of enemy etc. And C allowed me to create a single array of the right type (
Enemy for the new examples).
But even being able to create those
structs and then being able to create many
objects of that
struct, C is not considered an Object Oriented Programming language.
This happens because even if we can say that those enemies are objects, the language doesn't support all the features that are the base to OOP (or at least doesn't help using them). In fact OOP has some confusing definitions and by the most basic ones C is object oriented. Still the most important traits of OOP aren't enforced by the language, and those are Encapsulation and Polymorphism.
class and an
interface are called "types" (of objects). They actually say which traits exist in an object of its type. An object is the actual "data" of a given type. For example, the
Enemy type defines that it has the
Direction and others. Each enemy, then, is an object that have those traits.
Encapsulation actually means two things. One of them is grouping of "members" and the other is hiding of "members".
For example, when talking about the rectangle I was talking about
color, and I grouped all that data in a single
Rectangle type, which we can achieve by using C
But what about grouping the functions that we can use with a rectangle?
DrawRectangle, for example, doesn't need to be a "global" function that receives a
Rectangle. It can be a function "inside" a
Rectangle. In other words, we can have a
Rectangle type, with the traits (
color) and the functions (called methods, in OOP, like
Draw). So, we could draw a rectangle by doing
rectangle.Draw instead of
This actually make things simpler to organize, as instead of needing to have
DrawImage etc, we only need to have
Draw methods, which will of course be related to the actual type (so, we expect a
rectangle.Draw to draw a rectangle and a
circle.Draw to draw a circle).
So, being able to group the functions together with the object definition is something that differentiates OOP languages from non-OOP languages and C can't put functions "inside" the new types (some people may argue that function pointers do the same, but they aren't quite the same as they occupy space "per object"... they are actually a way to achieve OOP in C, even being harder to use than their C++ counterparts).
About the other trait of encapsulation, which is used to hide implementation details of a class, it is both very important and also very problematic. It is problematic because a lot of people avoid it because they say "it is useless to create methods to get or set a variable if I can do that directly by making the field public".
To explain the entire situation, I said that we could use the traits
Right in a
Rectangle. In fact, to have
Width we only need to have 2 traits in the object's memory, and the other one can be calculated. The same applies to
Should we store all traits in the object? Should we always recalculate some of them to use less memory?
If we have all traits in the object, how do we guarantee that when setting
Right users will not forget to set the
If later we decide that we will have only
Width in memory, calculating the
Right, how would we guarantee that users will not try to set the
Right, after all it doesn't really exist?
The answer is: Users of the class should not know anything about how the data is stored in memory. Only the writer of the class should know which data is really stored in memory.
This kind of thinking actually creates a Principle in which we never expose fields (the actual traits that consume memory per object) and so we should always create functions (or better, methods, as this is the name of functions inside types) or properties that will get or set the needed values.
That is, if users have the option to call
getWidth methods, it is not important which one of them is calculated (if any of them is calculated on reads). Also, when changing the values, users will be able to call methods like
setRight, which can actually validate the new value and recalculate the other fields if required (so, if the
Width is calculated, a call to
setWidth can verify that the new value is not less than zero and then set the
right field as
left + new width value, but the user of the class doesn't need to know about this calculation).
And, to complete this job, the fields that will exist in the class must become completely inaccessible to other classes. The other classes should only see those methods, so they can get all the info they need through them, without knowing the inner details of the class.
In the sense of not caring about the internal details of a class made by someone else C++ mostly follows OOP. It has the visibility modifiers (things like
private) that inform which traits can be seen and manipulated by other classes and which traits are accessible only by the class itself. Yet, when we deal with compilation C++ has a problem with the size of the "internal details" as, even if other classes can't access the traits marked as private, when creating a new object instance the actual size of those "inaccessible" traits is important. That's not a problem if we recompile the class that's being instantiated together with the code that creates new instances of the class, but it causes serious problems if the class (like
Rectangle) lives in a DLL and the code that creates the
Rectangle instances/objects lives in another application. If, for some reason, the size of the object in the DLL changes, the application that uses it must be recompiled or crashes will happen at run-time. In this sense, even with the capacity to hide members, C++ isn't really allowing the implementation details to change. C# and Java do allow this.
Traits, properties, fields and members
I used the terms traits, properties, fields and also members.
- Trait is not a term used in programming. It was my decision to use this term exactly to avoid confusion with existing terms and I was only talking about a characteristic that exist in an object, independently of how it works;
- Field is the name used for those traits that are really part of an object and consume memory. Getting and setting a field does a direct memory operation and can't execute validation code or change other fields;
- Property is used to talk about those traits that are usually composed of
set methods. For example, the
Left property can be composed by
setLeft methods in languages that don't support real properties. In those languages that support properties we will use them like fields, but they can be implemented by methods, so they have the possibility to calculate values on gets and do validations and change many fields on sets;
- Member is actually any field, method or even property in a type. But a member is only one that really exists in a type so, for languages that don't support real properties, the
setLeft will be members, but the
Left property will not, as it doesn't really exist, it is only a name given by developers to refer to the get and set methods.
I "love" this term and also its basic explanation (to let it clear, I actually hate this term and its basic explanation): "Polymorphism is the capacity of an object to assume different forms".
"So, if I have a
Shape object with an
IsEllipse property that is used to determine if it is drawn as an
Ellipse or as a
Rectangle and I can change it at any time, this is polymorphism, right? After all, the shape will be able to change from
Ellipse and back to
Rectangle at any time."
The worst is that I actually said that to a teacher in highschool and he said that I was right. But polymorphism has nothing to do with an object changing forms. In fact, the object doesn't need to have any visual representation to be polymorphic.
Polymorphism is the capacity to do "the same call" but to have different results (different code being executed) thanks to some kind of context.
For example, do you remember that I talked about putting a
Draw method inside the
Now imagine that there's another type, called
Line. It has the initial and final coordinates of the line to be presented in the screen and it will also have a
So, if we have something like this:
For Each Object in the Array
We will be able to
Rectangle as a rectangle and to
Line as a line. We will not need to call different methods (like
DrawRectangle) and we will not need to check for the object type before doing the call, so the
Draw call is actually Polymorphic. In fact, such example is run-time and dynamic polymorphism. It is dynamic because the
DrawAll function has no idea about the types of items that will exist in the array. It only knows that it will be an array and it believes it can call
Draw on the items of such an array.
Such dynamic behavior is many times seen as problematic as it can cause run-time bugs. What will happen if I pass an array of numbers, which don't have a Draw function? It will compile fine, as there's no validation on the input parameters, but it will fail during execution.
Function DrawAll(Array of Rectangles)
For Each Rectangle in the Array
Function DrawAll(Array of Lines)
WriteText "I should be drawing lines but, instead, I decided to write this text"
In this case, I can't simply call
DrawAll giving "any" array. I must call
DrawAll giving either an array of
Rectangles or an array of
At compile-time, by the type of the array, the compiler will choose which one of the
DrawAll functions to call. This is the kind of polymorphism that exist only to help developers. It is not really different from having a
DrawAllLines functions but we (developers) don't have to use different names when doing the call. The type of the arrays will decide the right method for us.
Then, there's the run-time polymorphism that exist in C++, C#, Java and other languages that is not as dynamic as the first one. We will write a single
DrawAll function expecting an array of some abstract class or interface.
That is, our
DrawAll function could look like this:
Function DrawAll(Array of Shapes)
For Each Shape in the Array
Ellipse (and any other shape that has a
Draw method) should inherit/implement the
In such class or interface there will be the declaration of a
Draw method, without actually having any code for it. This is useful for a situation like this one, so the
DrawAll method can receive any
Shape and know that it can call a
Draw method, as it should exist. At compile-time it will be possible to validate that a Shape will have a
Draw method, so it is possible to call such a method for
Lines, but it is not possible to even try to give an array of
ints to the
DrawAll function, as
Shapes, causing a compile-time error (which will forbid the developer to publish its program, also avoiding failures when clients actually try to run the code). Yet, at compile-time we don't need to know which code will be actually called (will it be a
Rectangle.Draw or a
Line.Draw or some other
I know that a
Rectangle is a visual shape and so, even if I said that polymorphism has nothing to do with "forms", I am still giving a "visual" example.
So, let's see (or better, only read) another example. In the programming world we deal a lot with
Streams. But to make it even simpler, let's talk about
As a developer you can create a function that "writes some text" (like the date and time) to... an
Writeable doesn't need to have a visual representation. Yet, it can be
Polymorphic, doing its work by:
- Writing to text file;
- Sending data through a TCP/IP connection (maybe sending an e-mail);
- Storing the text in memory;
- Sending the text to the screen (ok, this one is visual);
- Doing nothing.
Is it strange to create writeables? Especially one that does nothing?
Well, this is used very frequently in logging algorithms. You write code that does some work and also generates some logging, but the actual logging goes to some
Writeable, which may be saving to a file, may be writing things to the screen, may be registering things in a "memory stream" so another task will show it accordingly... or may be doing nothing, so the code that generates the log can call the
Write method everytime, even if we don't want logging.
A different presentation
I tried to be brief and at the same time to explain all traits of Polymorphism and Encapsulation. I know that those concepts are hard to get without good examples, so I will try to present a better example, even if it doesn't cover all the details of those topics.
I was talking about games and I commented about enemies. Let's imagine it is a space shoot em up game, that we actually use lists to store the enemies that are shown in the screen (lists are like "redimensionable" arrays) and that we have 3 kinds of enemies.
- The basic enemies simply go down the screen. They can do it in diagonal, but they will not change their direction and they will inevitably go off-screen;
- The intermediate enemies go down in zig-zag, so they will also get off-screen;
- And the hard enemies keep moving in the screen until they die or until they kill you, so they are not going to disappear by going off-screen.
As we all know, an animation is done by many similar (yet a little different) frames that are presented successively so, in a game, we may use a frame-by-frame based calculation.
So, before checking if there is any collision (which can cause an enemy or the player character to die), we will first move every enemy.
The algorithm to animate all enemies can be like this:
For Each enemy in enemiesList
It can also be like this:
For Each enemy in enemiesList
The first case is more similar to how it will look in C. The second case actually has better Encapsulation, but it may or may not use Polymorphism.
Even if reading both blocks of code are pretty similar, writing the second one can be much easier with the right editors. With the modern editors we can see a list of the available functions/methods in a given context and, in the first case, we may end-up seeing lots of functions that aren't related to what we want to do. In the second case, by writing
enemy. we will be able to see only the functions that are part of the
Enemy class. So, it may be actually easier to write code using the second version.
It is also better encapsulation as the
MoveSingleFrame will not be a "global" function, it will be contained in an
Independently of the Encapsulation part, both solutions may not use Polymorphism (and they can actually generate exactly the same code when compiled). So, how is the
Without Polymorphism we may have the Kind of an enemy instance as a number (
- Basic enemy;
- Intermediate enemy;
- Hard enemy.
And we can implement the
MoveSingleFrame like this:
If Enemy.Kind = 1
ElseIf Enemy.Kind = 2
ElseIf Enemy.Kind = 3
Do you see a problem with this approach?
Actually there are many. For example, if we create new enemy kinds we will need to revisit this function to add new tests.
If we have a really big game, with more than 1000 kinds of enemies, we will need to put 1000 conditions in a single function. Also, considering there are going to exist other functions (like the damage calculation) that can be related to the kind of enemy we will have to keep all the functions "in sync" having those 1000 conditions, and we will have to remember the number of each enemy kind. Even worse, we will have a very slow algorithm if we need to animate an enemy of kind 1000, as 999 tests will be done before actually reaching at the appropriate condition.
So, looking for possible improvements, we can have:
- Enums. Enums are actually a way to put a name to a value. So, instead of testing if the
Kind equals 1, 2, 3... or 1000, we will test if the
Switches. Instead of using
If, and many, many
ElseIf we can use
Switches are actually optimized to check many conditions over a single value, being able to do bit tests (so, only 10 tests for 1000 possible values) or using jump tables (which will check the minimum and maximum values and, if everything is ok, will use an internal array of jumps... this is an implementation detail of the compiler itself, so only keep in mind that switches are meant to be fast);
- Use some kind of virtual/dynamic dispatch. This is Polymorphism and this means that instead testing the
Kind on the
MoveSingleFrame to then call the appropriate
MoveSingleFrame will be actually the correct one for the object being manipulated.
Making use of Polymorphism
To use Polymorphism, instead of putting a
Kind inside a single
Enemy class we will have the
Enemy as a base class, only saying that it will have a
MoveSingleFrame method, then each
Kind of enemy will be a sub-class that implements the
MoveSingleFrame differently, with the actual
So, it is the class that represents the
Kind, not a numeric value, and the call to
MoveSingleFrame will jump to the correct code based on the actual
Enemy sub-class instance we are working with.
In source code, we gain the following advantages:
- Each class can live in its own file;
- Each class only needs to know what's related to its own
Kind of enemy, so the
MoveSingleFrame will be very small;
- The other methods that may be needed (like
DetectCollisionAndCauseDamage) will be near each other by the enemy kind. That is, in the
BasicEnemy class you will be able to see both the
MoveSingleFrame and the
DetectCollisionAndCauseDamage that are related. It is differently than seeing a
MoveSingleFrame inside a function that has 1000 conditions and then having to search for the appropriate enemy kind on the
- New enemy kinds can be added by creating new sub-classes of
Enemy, without having to change any of the existing classes. When we had those
Kind, adding a new enemy kind meant that we needed to change all the methods that do a
Kind to check for the new one;
- Actually, in applications that load libraries dynamically, it is possible to create new
Enemy kinds in different libraries and load them without problems (imagine that the level definition is in a text file and then the text says which enemies to load). This is simply impossible with a code that needs to know all the existing kinds to chose the right method to call.
The programmer side of OOP
When I first talked about Encapsulation I said that the support for
public members is the part of encapsulation related to hiding implementation details.
Well, the support for some features (like Encapsulation and Polymorphism) is what is usually used to determine if a language is object oriented or not. But such simplification may cause problems, as C is actually able to hide implementation details if the programmers know how to use it.
Another way of seeing it is this: If a language supports the OOP features and make them easy to use (usually by having specific keywords that make their usage pretty simple) then it is an OOP language. This actually doesn't mean every application created in an OOP language will be written in an object oriented way. We expect that it will be simple to use it, but this will not happen automatically, by simply using the language.
For example, we can still use
switches and only primitives data-types in C#, effectively programming as if it was a structured language. It is also possible to do the opposite and to program in an OOP way even on languages that aren't considered OOP. For example, every code ends-up compiled to machine code and it is simple to translate a binary representation (assembly) to a text representation of that machine code (assembler). Assembler is not OOP but it surely allows OOP code to be written.
So, independently if our language is officially considered an OOP language or not, it is our responsibility to make sure that our programs are really using OOP. This actually created many "principles" to create good OOP programs.
Those principles include:
- Never, ever, create
public fields. Always expose traits by methods or properties;
- * If using C, C++ or any language that may be bound to the object size in memory, which can break client code, either create the types with some reserved fields for future use or create functions like
Destroy for your types. Client code that uses the
Delete methods will not break if the size of your objects is changed in the future, as the
Create code will be in your library and will surely be recompiled together with the object that changed in size;
- Never create global functions. If needed, create
static methods inside a class so you can at least have some kind of grouping (if you are actually using a language that doesn't support classes or namespaces you will need to always prefix all your functions with the type to which they are related to);
- If you have a
Kind trait and you use an
switch to take different actions based on it, think about creating an
abstract class or
interface to represent the kind and putting each condition code in a different sub-class;
- * If the
switch is used over some external value (like a text character or an enum value provided by another library) it is still possible to replace a
switch with a mapping/dictionary between the input values and instances that implement an interface/abstract class.
And, well, there are many other principles that were built on top of OOP. Some of them became so popular that actually some people call them OOP principles. This include things like:
- Program to interfaces, not to implementations;
- Prefer composition over inheritance;
- SOLID (which is an acronym for 5 principles);
- Many design patterns like the the (abstract) factory pattern that says that we should not create our object instances directly, we should depend on a "service" that will do the job for us.
Interfaces and abstract classes
I already presented that an abstract class may have a definition of a method without an implementation and this is useful so you can create yet another method that will be able to call it, like a logger method that calls
writeable.Write when the actual
Write could be implemented in many different sub-classes.
I also said "abstract classes or interfaces" in many situations. So, what's the difference between them?
Well, the difference doesn't exist in all languages and it is more conceptual than a need. Abstract classes are actually classes that have at least one abstract method, but they can have other non-abstract methods and fields.
Interfaces are like abstract classes, but all their methods must be abstract and they don't allow any fields;
C++, for example, doesn't have real
interfaces yet many developers create classes as fully abstract and call them interfaces.
C# (the .NET in general) and Java allow us to create classes marked as
abstract even if they don't have a single
abstract method... this is actually a way to force users to inherit the class and some developers will say that those classes use the
abstract keyword but aren't really
Also, .NET and Java don't support multiple-inheritance, so using a fully abstract class as base doesn't allow a class to inherit from another base class, but they allow a class to implement many interfaces. Implementing many unrelated interfaces in a class is usually a bad practice but some developers are capable to find uses for this trait.
I am actually of the opinion that good code (good class/object) do what it needs to do without caring about sub-classing or interface implementations. It should only inherit or implement an interface if its purpose is to do that. So, on those codes that don't implement interfaces or base classes, if they must be "seen" by code that requires an interface or base class, we create another class that inherits that base class or implements the needed interface(s) and redirects to the code that works without doing such implementation (actually such class that implements the interface and redirects the calls is called an adapter). Only in performance critical code or to use those bad frameworks that can't deal with adapters we may actually create our code that "works by itself" and still make it inherit a base class or interface so it executes faster or is directly compatible with another framework.
Before I explained what is Encapsulation. Now I want to present how to use it.
Most languages utilise 3 keywords for Encapsulation:
private. These are also the visibility modifiers used in UML class diagrams.
private are the easiest ones to understand. A
public member is accessible to everyone. A
private member is accessible only to the class itself.
protected is a particular case. It is accessible by the class itself and by any of its sub-classes, but it is not accessible to other classes. In most frameworks the methods that are polymorphic are usually the protected ones. They are there to be (re-)implemented, but only the framework itself is expected to call them, so they aren't
Actually these 3 keywords miss two situations . As I just said, most frameworks use
protected methods as the polymorphic ones, but:
- They should be (re-)implemented only, not called. The
protected keyword allows sub-classes to (re-)implement methods (implementing or reimplementing a method is called overriding) and also allow the
protected methods to be called, even if this is not what the framework wants to allow;
- Usually the framework class that calls those
protected methods is manager class, which is not the class that declares those
protected methods or a sub-class it. So
protected is not enough.
Actually I don't know any language that solves the number 1. It is simply a rule that
protected methods meant to be overriden should not be called by the sub-classes.
For the number two each language creates a different way to solve the problem. UML purists consider this breaking Encapsulation but I actually consider it as a correction to a missing feature in the visibility modifiers.
C++ allows a class to say which classes are "its friends". Those friends are capable of accessing all the members of the former class, independently of the visibility modifier (OK, C++ is really breaking Encapsulation).
C# has two extra visibility modifiers, named
internal protected (or
protected internal, the order doesn't affect the result).
internal members are considered
public to other classes compiled in the same executable or DLL, but are considered
private to code compiled in other executable or DLL.
internal protected is accessible from sub-classes independently from which assembly they come from and are also accessible to other classes compiled in the same executable or DLL in which the member is declared, but they aren't accessible from non sub-classes declared in other executables or DLLs.
Personally I consider the C++
friend class better than
internal, even if it breaks Encapsulation, as in many projects people put all classes in the same executable, so the
internal ends-up working as
public, but I would prefer it even more if languages used a combination of
friend class and
internal, in which only the
internal members (not all members) could be exposed to friend classes, not to all classes of the executable or DLL. But I don't know any language that works like this.
public (I say put into an object because we don't declare which members exist in a class, we can add members into an object at any time). We can achieve a kind of
private members because a function can declare yet another function and put it into an object, and this new function has access to the local variables of the first function. These local variables aren't accessible to external code and, so, they work as
protected members, so any member that is meant to be polymorphic must be
C doesn't have visibility modifiers but actually the C code is split into declarations (usually in a .h file) and implementation (usually in a .c file). So, by not giving a header to a struct you don't want users to have access to, you make that
struct work as if it was
internal (to compare to C#).
In fact, considering that we should not expose the internal details of a class and
structs in C can only hold internal details, this means that we should never give the headers to the
structs. Then, it is up to us to create the functions that will create the objects, destroy them and also manipulate them (working as the properties or methods) and to put them into a public header file. So, those functions that aren't in a public .h file are
private and the ones in the public .h file are
public. This means that we don't have
It is actually possible to create them, but it becomes so hard that I will not try to explain it, as my purpose is not to teach how to achieve OOP in C.
Namespaces are part of encapsulation. We can say they fit in the grouping category but, well, all groupings also help hiding information that should not be seen.
In .NET and using Visual Studio, for example, when we write
System.Collections. we will see all the types and sub-namespaces found in the
System.Collections namespace. That is, it will not be showing the classes from other namespaces (a kind of "hiding", even if we still have access to use the other namespaces) and it surely shows that there's a grouping of classes related to collections (arrays, lists, etc are all "collections of objects").
They are also useful to avoid ambiguous situations. For example, a
Rectangle may be an in-memory representation of coordinates only (so, the
Color will not be part of it) and it may be a class that represents a visual
Rectangle, which may include border color, border size, border style (dotted, solid etc) and, of course, its inner color or filling. Both classes can exist which the same name, as long as they are grouped in different namespaces.
In C the function pointers allow Polymorphism, even without the support for classes (and they are the base to simulate classes if you want to use OOP in C). In C# we can use delegates (similar, but a little more complete than function pointers) and virtual methods.
Well, the virtual methods are what most people think when we talk about Object Oriented Programming and Polymorphism, so I will focus in them.
To use virtual methods in C++ and C# we must mark the methods with the
virtual modifier. In Java all methods are virtual by default and we can mark them as
final to remove such polymorphism. Abstract methods are always virtual, but they don't come with a default implementation.
To try to explain what is the difference of a polymorphic (or virtual) method and a non-virtual one I think it is good to have examples.
So, imagine that I have the
Rectangle class and a
Line class. Both with a non-virtual
We can already do things like:
Rectangle rectangle = new Rectangle(0, 0, 100, 100, 0xFF0000)
Line line = new Line(0, 0, 100, 100, 0xFF0000);
Those calls aren't polymorphic because the compiler knows the real type of the
line variables, so the calls are pretty equivalent to these:
This is different from those
DrawAll methods that receive an array (be it an untyped array or an array of shapes). In those cases, the code knows that it will receive some objects and will call
Draw on them, but it doesn't know which code is actually being executed (is it
Line.Draw?) and every object can have a different Draw (we can put
Lines and others in the same array).
Draw method that both
Rectangle can use, and it is not logical to make a
Line be a sub-class of
Rectangle or vice-versa, as they are simply different classes.
So, the developer is forced to create some kind of base type (class or interface) and put all the methods that are to be polymorphic in it.
In my case, I will use this class:
public abstract class Shape
public abstract void Draw();
Then we make both
Line sub-class the
Shape class. In C# we will also need to be explicit about virtual method implementations, so we should use the
override keyword to say that we are implementing the
Draw method first declared in
Shape (C++ and Java do automatic overriding if a method has the same name, result and parameter types).
Now we will be able to do:
If the compiler knows the real type at compile-time it is a possible optimization to invoke a virtual method as if it was not virtual.
This is the case of:
Rectangle rectangle = new Rectangle(0, 0, 100, 100, 0xFF0000)
In this case, the compiler might be capable of knowing that the
rectangle variable is really a
Rectangle, not a sub-class of
Rectangle, and will avoid the virtual call.
The result will be the same if the call is virtual or not, but virtual calls are slower than non-virtual calls, so doing such kind of optimization might be good. Of course, we can't do this kind of optimization if we have a
DrawAll method that receives an array of shapes. We don't know if those shapes are
Lines or others.
dynamic languages, but it is actually faster.
Low Level Details
I wanted to keep this article simple, but I believe it is easier to understand virtual methods (and why many languages don't make all methods virtual by default) by understanding the low-level details. Those are details the compiler do for us to make the code simpler.
As you probably know, everything the computer can execute must be loaded in memory and every location in memory can be represented by a number.
When we do a non-virtual call like:
rectangle.Draw (... parameters here...)
The compiler will determine where the
Rectangle.Draw method is related to the beginning of the file and, instead of keeping a call by the "Draw" name, it will do something like:
- Fill the parameters in processor registers and/or stack. The rectangle itself is a hidden first field;
- Call the address of the
This means that if the
Rectangle.Draw starts at the byte 500 from the beginning of the file, the method call will be
If 500 is the address of
Rectangle.Draw, how will we be able to call
Well, if the
Line type is known at compile time, the compiler can determine the address of
DrawLine... if the call is not virtual this will really be the same) and do the appropriate call. So, supposing it is at address 700, it will be
If the type can't be determined at compile-time, then it should use an alternative call. This is where the
virtual calls, like
shape.Draw(), come into play as the compiler can't know if it should
CALL 700 or something else. The same compiled code can use the address
700 or another one depending on the object.
In most OOP languages the virtual methods are put inside a VTable (virtual table). A VTable is like an array of function pointers. Then, every object has a first "hidden field" that points to this VTable.
Rectangles will point to the
Rectangle VTable, all
Lines will point to the
Line VTable and, considering they inherit from
Shape, the VTable will start with the
Shape VTable content. The child classes are free to replace the items in their VTable, and by replacing those that come from the base type they actually "reimplement" it. This is how we can implement the
Draw method on the sub-classes.
Of course, the compiler needs to change how the calls are done. So, when we call
shape.Draw, what the compiler needs to do is something like this:
- Fill the parameters in registers or stack, as normal;
- Then, using the shape, reads the hidden field that references the VTable;
- With the VTable, it reads the function pointer array in the appropriate position for our call;
- Finally, it calls that address it just read.
If we try to put such code in C, it will be like this:
shape->vTable->Draw(shape, ... all other parameters ...);
And we should remember that this
Draw is not a method, it is a function pointer.
Why do we need a base class? Why not consider all methods with the same signature (signature is the name, input parameter types and result type) as being naturally the implementation of the same method?
The base class is accepted but why aren’t all calls virtual?
For performance, to reduce the size of those VTables and because a class that is not meant to be sub-classed is probably not prepared for this. Most programmers do less validations on method results when they believe the method is not going to be reimplemented, so reimplementing it can cause serious bugs (many developers actually consider the fact that Java methods are virtual by default as a design flaw).
Can we do those virtual calls in C?
Sure. The example I gave in C will work but it will be our responsibility to create the VTable. Also, it can be very annoying for the users of the "class" to have to use the
shape variable twice, one to read the
VTable and the function pointer to call and then to pass it as the first parameter, as this is a needed parameter to the function pointer (and we can't simply store the shape in the VTable pointers because it is a single VTable for all objects of a given type).
Component Oriented Languages
For some time I saw many documents and web pages saying that C# is a Component Oriented Language but without an explanation of what is a Component Oriented Language. Such expression seems to be exactly the same as Object Oriented Language and many people really say it is the same thing.
The Wikipedia explanation makes things even worse as it appears that Component Oriented Languages give less features than Object Oriented Languages. So, why will someone be proud to say that a language "is not only Object Oriented, it's Component Oriented", if this gives less features?
Well, I searched about the topic for some time and I can say that there are two different things seen as important for a language to be considered Component Oriented.
The first item is, in my opinion, a precision of something that Object Oriented Languages should always have with good encapsulation but because of failed implementations (like in C++) people decided to create a new term instead of trying to say that C++ was not Object Oriented (I think C++ has a very strong reputation of being OOP, so "no one" discusses it). The second item is more related to how components created by those languages can be manipulated in a visual editor.
Modification of the internal details of a class should never cause breaking changes
The sole purpose of the
private visibility is to hide implementation details from the clients of the class. So, I should ask: Why would we want to hide details from the clients?
In my opinion, for two main reasons:
- To avoid users from putting inconsistent states in instances of our classes;
- To be free to change the internal details without breaking existing clients.
But in C++, if we simply add new fields to a class we would cause client code that lives in a different library/executable to crash, as the
new operator uses those "internal details" to calculate the size of the allocation, which may be invalid when the new version of the component is used.
So, a Component Oriented Language should allow components (that are still classes) that live in different libraries to be updated individually without causing problems to dependent compiled code.
Surely it is impossible to forbid developers from doing breaking changes to the components, but at least Encapsulation will work really fine. As long as we don't change the signature of
protected members, the new version of a component will work. We are free to completely replace the
private fields (making the component bigger or smaller in memory) and to add new methods and properties to our components and the client code, which is not going to be recompiled, will continue to work fine.
In this sense both Java and C# are Component Oriented Languages, C++ is not. C++ can of course create components but it is the responsibility of the component creator to provide the right Create/Destroy methods and the responsibility of the users to correctly use them instead of using the
Discoverable compiled code
The second item that's important for a Component Oriented Language is to allow those components to be created and manipulated by a visual editor. The support for properties, events and custom information on those two is considered fundamental, as well as storing the information about the existing types, properties and events on the compiled code.
Both C# and Java include enough information on the compiled code to allow us to list the existing types and their members and to instantiate objects and manipulate them thanks to such information. There are visual editors for Java capable of using the get/set methods as properties but for some that's an work-around because Java doesn't have real properties. As C# has real support for properties and events and those can be decorated with
[Attribute]s, C# is considered Component Oriented.
In my opinion the support for real properties and events shouldn't be taken into account to define what is a Component Oriented Language and is used to try to take Java out of the comparison. In fact, if I could simply redefine the terms freely, I will say that the capacity to change the private members without breaking client code should be part of OOP (it's the purpose of Encapsulation, after all) and a Component Oriented Language is one that is Object Oriented and also allows the compiled code to be analyzed to discover the existing types and members and to create and manipulate them after discovered, so they can easily be put into a visual editor.
By this personal definitions, C++ is not Object Oriented. It is almost there, but the problem with the
new operator is enough to get it out of the Object Oriented Languages. In contrast, both C# and Java are Component Oriented Languages. C# can make things easier for the editor by having real properties and events but this doesn't mean Java is less Component Oriented.
What about C and C++?
We can always create work-arounds in C and C++. Nothing forbid developers from creating methods that return the definition of the existing classes and members, but keeping things in sync can be very problematic, as nothing in the language will forbid developers from saying that a class has a given method when it doesn't, or simply writing the wrong name in the definition. So, it is possible to create components in C and C++, but I don't think it's worth doing it (at least not by hand... it is always possible to create code generators that keep things in sync).
I don't have any real conclusion. I only hope this article is useful to anyone wanting to learn a little about Object Oriented Programming.
Do you have a question or don't agree with something? Ask me by using the forum. I will try to answer your question and, depending on the case, I will update the article to help others that may have the same question.
- 31, March, 2014: Added the Component Oriented Languages topic;
- 30, March, 2014: Added the Using Encapsulation and Using Polymorphism topics;
- 29, March, 2014: Initial version.