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

An Overview on Many Terms and Principles

By , 10 Nov 2012
 

Background

I see many articles talking about loose-coupling, many times also talking about interface implementation instead of class inheritance or simple saying that the right thing to do is to follow the open-closed principle.

Usually those terms are related, but in many places they seem to be the same thing, so I decided to write an article about what each term means, giving explanation to help differenciate and relate them.

OOP - Object Oriented Programming

This is probably the most common term and the most confusing one for beginners. In the .Net world we don't have another option, we will inevitably create classes (or at least structs) and, if we instantiate them (that means, we do something like new MyClass()), we will be creating objects.

That's object oriented programming, right?

Well... that's something I hear a lot, and it is wrong. C has structs, which can hold data of specific types and give names to them. Yet, C is not considered object oriented.

I am not creating a struct, I am creating a class! So it is object oriented!

That's still not related to the OOP problem. In some C++ implementations the single difference between a struct and a class is the default visibility of their members. The problem is not the keyword used (class or struct), the problem is related to resources used. In special: Inheritance and method overriding.

To illustrate the problem, I worked on some places that used ORM like objects, having one class to represent each database table. They were all the same structure:

  • All the fields that existed in the database had a Get and a Set method;
  • There were some methods like Save, Delete etc.

But they had a little problem, there was not a common base. So, I couldn't create a method like DeleteWithConfirmation who was capable of showing a message and, if it was confirmed, delete the given record.

The problem is, to make such method, I should create it for a Person or for a Product (or for something else) when I should be creating the method for DatabaseRecords in general.

First step to OOP: Create the ancestor. A class like DatabaseRecord with a Delete method will work. Then, both Person and Product could inherit from DatabaseRecord and the method DeleteWithConfirmation could be created, showing the message and, if confirmed, calling the record.Delete() method.

But, how should look the Delete implementation? It is created at the DatabaseRecord, with does not know if it is a Person or Product.

Solution, create it as abstract and force the inheritors to implement (override) it.

So, we finished the basic OOP here. We need to have a class that has at least one virtual method (that we have real reasons to override) to say that we are using OOP. Simple creating a class or simply making our methods virtual without ever overriding them means we are not using OOP, even if we have objects.

But if you are not used to see virtual, override or abstract terms, here they are:

  • virtual: A method declared in one class that can have its actual code replaced by a child class;
  • abstract: It's a virtual method that does not have a default implementation, so instead of allowing its implementation to be replaced, it forces subclasses to implement it;
  • override: The act of implementing an abstract method or reimplementing a virtual method. In C# you need to use the override keyword to explicitly tell that you want to implement or reimplement an abstract or virtual method.

Class inheritance and Interface implementation

In C++ there is multiple inheritance, so it don't need interfaces. Independent that I don't agree with the reasons to remove multiple inheritance from the .Net, the main differences between an interface and an abstract class are:

  • An abstract class can have a default implementation, but if you inherit from that class, you can't inherit from any other class;
  • Interfaces don't have a default implementation, but your objects are free to implement as many interfaces as you want.

I usually see the affirmation "Interfaces are a contract".

What does that mean? Did I put my signature in anything? Do I need a lawyer?

The answer may be: The interface has the methods that tell you what you should implement (like a contract that asks you to do one or more things) and implementing it is accepting that contract.

Ok... valid, but I still don't like the contract term. But let's return to the topic.

You can create an abstract class without a default implementation. But should you do it?

In my opinion. No. Never do it. In fact that is how you create an interface in normal C++ (in that case it is valid, as normal C++ does not have interfaces) but doing so in .Net is like creating an interface that can't be implemented by classes that have a different ancestor.

So, if you don't need a default implementation, use an interface.

I can go one step further and say: Always create the interface and receive your parameters as the interface then, if you want, you create an abstract class with a default implementation that people can use when they want to implement the interface.

I can repeat all the words, but I don't get it. Why create an interface and an abstract class? Why not the abstract class directly?

Ok... abstraction is great but makes things complicated.

I will ignore some traits of the MarshalByRefObject (which I consider a real error) and the extension methods (that are a pseudo-solution, not a real solution) and I will use the Stream as example.

In the Stream class, we have the Write(byte[] buffer, int offset, int length) method.

We don't have a Write(byte[] buffer) with simple writes the entire buffer. It will be real easy to implement:

public void Write(byte[] buffer)
{
  if (buffer == null)
    throw new ArgumentNullException("buffer");

  Write(buffer, 0, buffer.Length);
}

What that implementation is doing is simple calling the Write that requires an offset (which will be zero, to get the beginning of the array) and length (which will be the full length of the array) so it will write the full buffer array.

With an abstract class I can do that. With an interface I can only declare that there are two different Write methods, but I can't provide the default implementation to one of them.

So, the abstract class can have a Write(byte[] buffer) that is implemented and a Write(byte[] buffer, int offset, int length) which needs to be implemented by the subclasses (like File and MemoryStream).

But now, to make an example on why it should start as an interface, imagine that I want to create an implementation that logs each action being invoked and then redirects to another Stream that does the real action.

The first problem can be that I need a different base class. In that case, the interface let me do it, the abstract class does not.

Also, if the abstract class did not make the Write with one parameter virtual, I can't reimplement it to do the log, so I will only implement the log on the write with 3 parameters.

The result is: the user calls Write(someBuffer) and the log will say that he called write(someBuffer, 0, ...someBuffer length...) when the wanted result was to log Write(someBuffer).

If, on the other hand, we had an interface, with the two Writes, we will have the option to implement each one with a different log, and then redirect to the real class.

But, in most implementations, we will simple use the Write with one parameter calling the write with 3... so, it will be good to create an abstract class that gives the default implementation to those who want it, but we should use the interface at every place where we consume or use the object, so we are free to receive all the implementations that can do any extra work they want.

AOP - Aspect Oriented Programming

I just showed a log example, so I will continue with it. To me the descriptions of AOP are hard to understand. I am not a natural English speaker, I think I read and write well in English, but terms like cross-cutting concerns don't make any sense to me. Is it only me?

Well, what I see of AOP is that it wants to add functionalities to unrelated classes.

For example, when creating my DatabaseRecord.Save I want only to implement the Save method. I don't want to bother about logging what is being done. That will conform better with the Single Responsibility Principle (we will see it soon) and so we will not be writing code for other concerns.

What is a concern? I still don't really get it, but I will say that logging is a concern about tracing what was being done. A validation if the user can do such modification or not is a security concern, but you may simple want to avoid those concerns when writing your class. You simple want to make your class do what it is supposed to do. Every other concern should (logging, security checks etc) should be put later.

As we can't put code to a class that already exists during execution, interfaces give us the possibility of putting another implementation in place, with the additional concern and then redirecting to the original class.

So, the log class can simple log the beginning of the call, call the original method, and then log the result of the call.

With some JIT we can even implement any interface at run-time to do the log, the security check or whatever.

JIT - Just-in-Time (compilation)

JIT means Just-in-Time compilation and actually it is mostly seen as an optimization technique for languages that compile to an intermediate byte-code. The idea is that during the execution (maybe during the load-time, maybe after an interpreted method is used too often) such intermediate byte-code is compiled to the target processor. Both Java and .Net have intermediate languages and do JIT compilation.

But that's not the only case where JIT is used. If, for any reason, we generate code during the execution of an application, compile it and then use it, we will be doing JIT - even if we compile to an intermediate language.

In this case, it usually helps avoiding repetitive code. To see such scenario, let see the log example with interfaces again.

We may have the original classes doing their action, without any log, and then we need extra classes that do the log.

But, to each method that exist in the interface we will need to provide an implementation that looks the same:

  • Logs beginning;
  • Call the original method;
  • Logs the result.

It will be identical, except that the method name and the parameters change. That's the single thing that changes.

So, why not let such repetitive code be implemented to you?

Well, if you create a code generator that does that then you will avoid writing that repetitive code by hand, but you will still need to compile it as part of your application (ahead of time compilation). But, if you generate and compile such code at run-time, then it will be JIT compilation.

Single Responsibility Principle - SRP

I wrote the real meaning and the abbreviation on the opposite order of the other abbreviations because I see this one more often fully written than as an abbreviation, which is the opposite to the others.

But as the AOP topic shown, SRP is something highly related to AOP... or should I say that AOP helps SRP?

I wrote an article about the SRP itself, I received some complaints and now I will put my view here:

Your classes don't need to deal with anything unrelated with their purposes. You, as a programmer, don't need to put anything to your classes (be it programming instructions, attributes or resources) that are not related to what the class need to do.

That means that if you need to put a single: [Serializable] attribute when you write a class to store some data, you are violating the SRP.

Maybe you use a framework that is forcing you to do so (the .Net serialization does that and you may have no choice). But if you have the option, avoid doing that. It will simplify your code and if you are writing libraries used by other people, you will be simplifying their lives.

Some say that putting a [Serializable] attribute does not add responsibility to a class.

But someone simple writes its class, without knowing that it will be serialized, and then he gets a complaint that his class is not serializable when it should be. So, it became a responsibility of the programmer to put that damn "flag". At the same time, we can say that it became the responsibility of the class to say: Hey, I am serializable!

In that SRP article I even received the response that implementing the ISerializable will violate the principle while the attribute does not. But, in my opinion, if the class was created with the idea of being serializable, implementing the interface is part of its responsibility, so the attribute is violating the principle while the interface is not. (I am already seeing someone discussing about it... I hope you understand my idea before complaining).

Open-Closed Principle

To me the Open-Closed Principle is simple Object-Oriented Programming and it is really needed for those who write libraries that may be used by many different users.

The premise is very simple (I will put in my own words): You don't need to write all the classes. But you will need to create extension points so users may create their own classes that work correctly in your environment.

Let's see Windows Forms for example. It has buttons, textboxes, checkboxes... but you can still create your own control and put it into a window. You don't need to modify the Windows Forms libraries themselves to do that.

But as that is normal inheritance, I should say that it is simple a different explanation of OOP.

And, why open-closed?

Open because it is allows expansion (by inheritance), closed because you don't need to change it (and we as users simple can't change it, it is a closed library).

Loose-coupling

This is one of the most difficult ones to explain and to apply, but I also consider it the most importante one.

When we use an interface instead of a base class we already let our code to be loosely-coupled. If the caller of our method has a different class that conforms with the interface, even if it does a different work than I was expecting (it logs, verifies accesses then calls a remote server instead of doing the work directly) we have loose-coupling.

AOP and the Open-Closed Principle already benefit from it. But there is still a little problem. I will start it by the example:

Imagine that you are working with many persons in a big project with too many different classes, concepts and so on.

So, everyone is aware that parameters should be passed as interfaces. There is no method receiving a File (specific class) or Stream (abstract class), all of them receive (IStream) the interface, which can be loggable or have other concerns (respecting AOP and again ignoring the MarshalByRefObject that already does that in a way I disagree).

So, you do your first call:

var color = CreateObject<IColor>(255, 0, 0, 0);

I suppose that if you work with factories already you understand that I am creating a Color, and that it is black.

But then I do:

serializer.Serializer(color);

I am omitting how the serializer was created on purpose.

The problem here is: a Color is not serializable.

  • It does not has the [Serializable] attribute.
  • It does not implement the ISerializable interface.

But I can easily read its ARGB (Alpha=Opacity, 255 = 100 % opaque, R=Red, G=Green and B=Blue) values and write it to a stream, and recreate the same color easily.

This is not a Cross-cutting concern (so it is not an AOP problem) because I don't want to add an extra actions to some method of the Color type, I want a new action (and so a new responsibility) to a class that's not aware on how to serialize itself.

When the serializer was created (let's say assembly X) it did not know about the existence of the Color type (which is in assembly Y), so it can't have specific code to serialize a color and it is also not its responsibility.

When the Color type was created it also did not know about the Serializer and, even if it knew about its existence, it was not its responsibility to be serializable.

The person who used both assemblies created that need, and so he should create the serialization of a Color in its own assembly (let's say assembly Z).

Creating a ColorSerializer is easy. The problem is, objects are passed to a generic serializer to be serialized. We are not creating an specific ColorSerializer to be able to serialize colors.

So, what should we do?

Creating a BaseSerializer abstract class means that we can create serializers for any other type, but if we call a method that will do some code and then serialize an object we should also give the right serializer to it.

Using the ISerializable interface that makes the object capable of serializing itself means we can create a Color subclass that is capable of serializing itself. What if we create such SerializableColor class, but we are still receiving a Color (not a SerializableColor) created somewhere else, which is still not serializable?

Now, if the serializer (at assembly X) allowed to register specific serialization code for different types (using a Dictionary it is very simple to do it), we could initialize the application, register that a Color is serialized by a ColorSerializer and, at any random moment, we can ask a serializer to serialize an object.

Using that dictionary it discovers that the serializer of Color is the ColorSerializer and uses it.

If that value was a string, it could also search on that dictionary for the serializer of strings.

So, that's loose-coupling.

But to do that, the Serializer class should be created with the idea of expansion in mind. It will surely use some interface or abstract class, but it should not expect that the serialized items themselves implement the interface. They are bind at run-time.

So:

  • If you can inherit your Color class to make it serializable, you are following the OOP and the Open-Closed Principle, even the Single Responsibility Principle, but you are not creating a loose-coupled code and you will be stuck when the object is of the base class (Color) instead of being of the child class (SerializableColor);
  • If you can inherit your Serializer to it can know how to serialize Colors you are also following all the same principles, and have the same problem. If you can't control which serializer is created, you will not be able to serialize a color;
  • But if you can create a ColorSerializer and register it as the serializer for colors and the Serializer class is capable of finding the right object serializer by using a dictionary (for example), then you will solve the problem and have loose-coupling.

IoC - Inversion of Control

Inversion of Control is a problematic principle because the term is too generic and usually the texts say one thing and give examples of another thing.

As the name tries to say, the Control is inverted. But, what kind of control are we talking about? And the answer is that it can be two kinds of control.

  • What: In this situation, the control of what exactly is being done is not part of the executing code. This happens, for example, when the code receives a delegate or an interface and it calls the delegate/interface as part of its execution. Surely there is some rule to be followed, but it does not even know if a property set will do a validation, will write that property value to the console or to a text file or what else it can do.
  • When: In this situation we are talking about the delegate or the interface itself. The delegate is given to a method. But the delegate can't control when it will be called or even if it will be called at all.

They are too similar right? In fact, it is enough to create an interface or delegate and we already have inversion of control, right?

Well, that's when things got complicated. Most documents talk about Inversion of Control as only the second case, the when situation, but then they use an example that's like this:

public static void ThisIsNotIoC()
{
  IData data = new Data();
  data.FirstName = "Paulo";
  data.LastName = "Zemek";
}
public static void ThisIsIoC(IData data)
{
  data.FirstName = "Paulo";
  data.LastName = "Zemek";
}

In such sample, the code is always in control of when the FirstName and LastName will be set and the Data class is never in control of when its properties will be set. Yet, the first example is not seen as IoC while the second example is. That's the problem I see in most documents and the truth is: There is Inversion of Control, and it is about what is going to happen when the properties are set (or what the data really is). In the first case, we know it is a Data instance, so if we know what it does, we already know what's going to happen. In the second case, the data object may be of type Data, of type LoggableData or any other type, and we can't control what we are effectively doing by setting such properties.

Factories

I used the example of creating an object like this:

var color = CreateObject<IColor>(255, 0, 0, 0);

We can say that CreateObject is a factory method (it creates objects) and that we are using Inversion of Control too.

The inversion of control here means that we are not in control of the created object (the what from the previous topic).

If we used something like:

var color = new Color(255, 0, 0, 0)

We will be in control. The real purpose of the factories is to reduce direct dependencies between classes, which is already a kind of loosely-coupling and, depending on the implementation, it can be completely loosely-coupled and allow dependency injection.

Dependency Injection

This is another term that I can't agree. I don't know if injection has a different meaning than the one I have in my mind, but when I see Dependency Injection I think we will be adding dependencies. So, the code didn't depend on anything and we put something that it must depend now.

But what happens is that the code already depends on something, which may not be there, and we fill the dependency (so, I will call it Dependency Filling).

Example? Well, on the CreateObject<IColor> case, imagine the the CreateObject method searches for the right type to create in a dictionary. Such dictionary starts empty and, so, the code that depends on IColor will not work until we register (inject) the implementation for IColor.

Something simple as Register(typeof(IColor), typeof(Color)) could solve the problem, and can be done in a different moment of the CreateObject call, like on the application startup. So, such register is going to inject the dependency... or, as I said, fill it, as the dependency is already there.

Library versus Framework

After the initial publication I received a comment in which I was asked to explain the difference between a library and a framework.

Personally, I call any .DLL a library. So, most frameworks start as a library. But one common definition is something like "you call a library, a framework calls you".

To such definition, a library is simple a class or method that you use when you need it. You don't need to follow specific rules to use it. On the other hand, a framework is usually a structure that works on its own, and you need to extend it by implementing some base classes or filling events and, in that case, the framework is going to call your code on the right moments.

I think that such affirmation works most of the time, but I can't really say that I agree with it. I will use as an example my ConversionManagement framework. I call it a framework. If you create specific conversions for it, it is going to call your code. But if you don't, and you simple use it in its original form, the framework will not be calling your code, but I can't say it will become a library. Ok, maybe it is a framework used like a library. But, then, what is a framework? A library that can call your code? Is that really enough?

I can't say I have a direct answer, but to me a framework is usually much more complete than a simple library and you usually (always?) need to follow some rules to use it properly.

Conclusion

I really hope this article helps understanding those principles. There are a lot of names, but in the end, if you can create really loosely coupled applications, you will end-up using all the other principles, even if you don't know their names.

Version History

  • November, 10, 2012. Rewrote IoC and Factories and added Dependency Injection;
  • November, 08, 2012. Corrected a small confusing affirmation and added the library vs framework;
  • November, 07, 2012. Added more details on JIT;
  • October, 25, 2012. Initial version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Paulo Zemek

Canada Canada
I started to program computers when I was 11 years old, as a hobbist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.
 
At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do they work easier, faster and with less errors.
 
Want more info or simply want to contact me?
Take a look at: http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 3memberstolbovoy12-Nov-12 6:31 

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130617.1 | Last Updated 10 Nov 2012
Article Copyright 2012 by Paulo Zemek
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid