Private and internal classes

This post is not going to be about concurrent programming. It has been triggered by a Twitter initiated chat regarding how to write unit tests for private methods. The general consensus was you just don’t: one must test public methods, private ones are an implementation details.
What about private classes then, did I ask. One of the feedback I got was: are you sure private/internal classes are needed in the general case?

I definitely think so, so let me describe some uses.

Internal classes

As a reminder, internal classes in C# are classes that are accessible only from the assembly they are defined in (and friend assemblies, if any). Declaring a class public is a contract with anyone who may use this library: you must implement a quality service for the long run. It implies you provide documentation of some sorts as well as ensure the class will be published for the versions to come.

For example, in RAFTiNG I have classes that are used to implement the behavior of the node according to its current state. Definitely implementation details, not something I want to publish.

Private classes

As a reminder, private classes can only exists as member of other classes in C#. They are useful to implement specific collaboration scenarios. Going on with RAFTiNG examples, in the ‘leader’ state, a node must track the synchronization point for each of the ‘Follower’ nodes.

Implementing it directly in the ‘LeaderState’ class implies building up an array of follower state and managing them one by one. Definitely not very OO: no encapsulation!

So, the proper way to do it is to implement a dedicated class for that, a very specialized class that is only useful in that very state.

See LogReplicationAgent member class here.

Note also that member classes can access private members from their hosting class, which offer interesting collaboration model.

OOD and concurrency

LOOP: Lego Object Oriented Programming
LOOP: Lego Object Oriented Programming (Photo credit: jurvetson)

Some background first

Object Oriented Development is still the dominant programming model. It has replaced Structured Development something like 20 years ago. New languages made interesting breakthrough recently (Erlang, Python), but none of them is in a position to get the throne: Functional programming is powerful, but hard to master, and dynamic typing is expressive but leads to code mess.

Its success mainly comes from the accessibility of C++. Many interesting proposals were available at the time, including Smalltalk, but C++ ruled them all simply by being pragmatic.

Thanks to OOD, developers made significant progress towards the Holy Grail: code reuse. To that effect, it offered three major paradigms:

  • inheritance: reuse of code by having a class inherit from other(s). Permit changes done on the super class to trickle to the derived classes
  • encapsulation: the concept of interface: some code can manipulate entities thanks to some contract. The implementation details can change without impacting the code (as long as the contract does not change)
  • polymorphism: those contracts can be implemented by many classes, each of them having various side effects

Initially, there was a strong emphasis on inheritance, but, years of experience as taught us that the value lied in encapsulation and polymorphism, because they permit low coupling. Inheritance turned out to be more an embarrassment than anything else and is often used by opponents.

What about concurrency?

Concurrency is orthogonal to OOD. And as OOD implied linear execution, concurrency wreaks havoc in that perfect world. Methods are transactions that change or consume the state of the object (this/self). If two methods are executed simultaneously, the object sate will shift unexpectedly, e.g: the key found is some internal dictionary may be removed by a delete command, there is an error message but there is no error code, etc…

The traditional workaround for this is to rely on exclusive access, i.e. using synchronized in Java and locks for other languages.
But, we know that locks contain so many pitfalls that you cannot expect to produce durable code using them.
That pushed many highly skilled developers to reject OOP altogether and embrace a more radical approach: functional programing.
Clojure is their flagship, allowing to implement easily scalable code thanks to a share nothing paradigm.

I have a different view on this, I strongly believe that any solution will a steep learning curve will fail, as most of the developers would be kept out of it. That being said, I am very strong supporter of immutability, message passing and asynchronous programming. It is just that I am sure you can combine this with good old OOD.

Well, I have been doing that for more than 7 years now.