Nano Oriented Programming (NOP)
By Leonard Ponce
Introduction
The word "nanotechnology" has become very popular these days. It is used to describe many types of research where the characteristic dimensions are less than about 1,000 nanometers. The basic idea behind nanotechnology is that arrangement of very small and simple elements (usually atoms) can form large complex and sometimes beautiful systems. It’s hard to imagine that Coal and Diamonds are made of the essentially the same material arranged differently. Using “Nanotechnology” as a metaphor to describe software creation is perhaps a stretch, but only in the physical sense. Like “Nanotechnology,” NOP emphasizes the arrangement and organization of small entities. In the ideal logical sense it is a perfect metaphor.
No matter how much or how little a software application accomplishes, it is always made up of smaller components. Those components could be data, objects, or function calls, or even machine code. Unfortunately, so much of the software written these days is more on the side of Coal than Diamonds. With the slightest unexpected force, poorly arranged software can usually be crushed into a messy black powder.
As with so many software engineering methodologies, Nano Oriented Programming (NOP) is not completely distinct from existing ideas. In fact it is derived from Object Oriented Programming, Aspect Oriented Programming, Adaptive Programming, Intentional Programming, and of course Common Sense. Many existing methodologies try to solve a specific problem in the realm of software engineering, by introducing paradigms and metaphors that elegantly solve a certain problems. NOP tries to solve all the problems by keeping its focus on the “Small” and the “Simple.” Having small and simple components allows you shape you solution into just about any form. In a very obscure connection this concept parallels elementary Calculus. In calculus we could solve for the area under a non-linear curve, by summing the areas of tiny little rectangles.
Object Oriented Programming
Object oriented programming created a paradigm to model our world using Inheritance and Polymorphism. Using these two concepts we could factor out commonality and hide our ugly details from our peers. Having Objects that modeled reality gave us a way to bring life to a very procedural existence. Over all, what OOP gave us, was a reason to combine Data and Behavior. Gone are the days of “Dumb Data.” These paradigms from OOP are essential to good software creation. At least it’s better than no paradigm.
Unfortunately, the justifications for using these ideals are vague, arrogant, outdate and sometimes just flat out wrong. For example, Encapsulation is supposed to hide the details of our brilliance from the peasants. Instead, it usually separates our crap from other people’s crap. Given the premise that we are the domain experts, authors, and that we have the ability to hide the details of our sleazy operation, we have all the makings of a corrupt government (or software). Another OOP tool with misguided justification is the idea of Inheritance. When I used to think of inheritance I thought good things, like money, property, eye color… etc. In reality, however, we inherit all things good and bad. Just like in OOP, when you inherit you inherit ALL things good and bad, whether you need them or not. Finally there is Polymorphism, which allows each object in a family of objects to do something his own way. For example, a bunch of geometric shapes could have a “Draw” function which each object implements its own way. Most Programmers will get the highest satisfaction when they can group objects via common function interfaces. This is similar to what mathematicians call Lowest Common Denominator. It allows us to group our otherwise unrelated object into stereotypical abstract blobs. This concept has motivated many programmers to create the “Cosmic Hierarchy.” This is when object are so unrelated but must be grouped, the author creates a base class that is so generic that anything and everything has this functionality (this is where we get “CObject”, or in math this would be the number “1”). Nano Oriented Programming does not shy away form object oriented programming at all. It does however try to create “new” justifications and rules for using OOP.
Behavior and Data
In all software languages the most basic entities are the intrinsic data types and operators. Next on the scale of importance are functions and data structures. If you zoom out even further, the main entities are Objects and …uh Objects. This is where we begin loose sight of what is going on.
Before Objects we could easily tell what “was” something, and what was “doing” something. After the concepts of Objects it became unclear if an object “was” something or was “doing” something. All of a sudden an object could do a bunch of things: it could write itself out to file, it could draw itself to the screen, it could save itself etc. The possibilities were endless. The problem with having an object many features is that it now serves a very application specific purpose. All this functionality was added because it was needed for our application. If someone wants to use this class in their program, they get everything that comes with this class. The behaviors of this object are no longer context specific, it’s ubiquitous.
NOP tries to look at things a little differently. It tries to emphasize the distinction between Data and Behavior. Everything can and should be divided into a Data component or a Behavior component. NOP uses the idea of Behavior-Only Components and Data-Only Components quite frequently. This allows object to exist in different context more freely, by being able to import only the necessary behaviors. Having separate behaviors makes it much easier to understand the intentions of each component.
Intentions
In the American legal system “Intention” is almost as important as the actual deed. That is why so many defense attorneys focus on proving the spontaneity or the thoughtlessness of their client. I think many programmers have taken this legal strategy as their own defense, “If my intentions are unclear how do you know I failed?” When an Object is decorated with forty different functions, it’s very easy to loose sight of the object’s intentions. NOP tries to solve this by minimizing the use of the word function, and force you to call them intentions. By calling functions intentions, it’s easy to keep concepts succinct. Having a class with 40 intentions, accentuates the fact that something is no right. In NOP, even data components are intentions (at least conceptually). By referring to all data components as an “Intention,” you will be better able to group and eliminate unnecessary data elements. As you design or Re-factor your class’ data elements: Ask the question: “What is the intention of this data”? For every function, ask this same question. For a behavioral intention in the form of a class, only one function should be public (not counting the accessor functions). This function is the “Intention,” any other function should be private or protected they are “Utility” functions. Soon your classes will become the building blocks for a beautiful diamond.
class BasicOp
{
private:
//Utility function.
int helperFunction(int a,int b) const
{ return a+b; }
public:
//Main
Intention
int Exec(const BasicBase& Base)
{
return helperFunction(Base.i, Base.j);
}
};
Good Intentions
Always make sure you “tell” Behavioral Intentions to do something. Never “ask” an Intention to do something. Having to ask something implies that you have some internal knowledge about the nature of the object you are calling. This sounds like and easy enough rule to follow but it’s also easy enough to break. Suppose you have a class that initializes itself from a file; you should be able to ask it at any time for any data, you shouldn’t have to check it if it is initialized before you ask for data. A very good article exists on this very topic: http://www.pragmaticprogrammer.com/ppllc/papers/1998_05.html .
Eliminate or minimize coupling
Structural components should never under any circumstance communicate or own a Behavioral components of its own type. Structural components, on the other hand, can contain other Structural components and use, other Behavioral components. Obviously, the more you use containment, the more dependencies you create, so containment should be minimized. Also, Behavioral components were conceived to be a create-when-used entity. They should never be held onto as member/global variables.
Organize your components
One of the most interesting features of C++ is the namespace. NOP tries to take advantage of namespaces by using them to group intentions. In NOP, a group of intentions is called a Domain. Here is the organization of a NOP program:
|
Lib/Program |
Namespace/Domain |
Intentions |
Data |
|
Function |
|||
|
Multiple Functions |
|||
|
Class |
|||
|
Multiple Classes |
|||
|
Multiple Intentions |
|
||
|
Multiple Namespaces/Domains |
|
Intentions can be just about anything, data, a single function, multiple functions, a single class, or multiple classes. One thing to keep in mind is that Intentions spanning multiple classes or functions tend to add complexity to the design. It’s almost a linear relationship. Also keep in mind that excessive use of namespaces can lead to confusion. Don’t try to have a separate namespace for every class, since the class itself acts like a local namespace. For example if you have a String class in a String namespace the user would have to call a function with the syntax String::String::function().
Namespaces should have its own directory with an identical name. Everything within that namespace should be in that directory. Here is namespace trick. Suppose your namespace conflicts with another library’s namespace. A way to solve this is to always name you namespaces with unique names:
namespace Strings_ver1_02_016
{
… some code.
}
Just before you use your Domain, rename the namespace to something human readable but not conflicting:
#ifndef STRINGS_VER1_02_016_NO_DEFAULT_NAMESPACE
namespace StringsEx = Strings_ver1_02_016;
#endif
This is not really part of the NOP idea, but when using many namespaces the problem may arise.
Design Patterns and Idioms
There is no question that Design patterns can helped to turn Software Coal into Software Diamonds. However, I have seen perhaps more bad implementations of Design Patterns than good implementations. I have even participated in a few bad implementations. A pattern is usually a good starting point for most designs. It can sometimes inspire architecture, and provide a good starting point for many developers. Bad implementations usually come about when the problem doesn’t quite fit the solution, or when the author doesn’t see the connection. In either case the author usually adlibs the non-conforming details into his own version of the pattern. This can happen quite often, and is somewhat expected. However, if no guidelines are given on how to “fit” the extra pieces, the code will often turn into something that is worse than a non-patterned code. NOP does not attempt to solve patterns. I have much hope for solutions in this area, but there is so much lower level work to be done by NOP, I simple haven’t explored the possibilities. NOP does try to use software Idioms as much as possible. Software Idioms are much smaller than patterns and fit more closely to the idea of Intentions. Some of the Idioms that NOP uses quite frequently are:
Handle Body Idiom
http://www1.bell-labs.com/user/cope/Patterns/C%2b%2bIdioms/EuroPLoP98.html#HandleBody
This idiom uses a combination of Structural (Handle) and Behavioral (Body) Intentions.
Named Parameter Idiom
http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.17 . This can be use as either a Behavioral or a Structural Intention.
Class Expression Idiom
There might be another name for this, but this is what I will call it for now. This is a Behavioral Intention which is designed to be used inline and on-demand. The client code calls the Class by name with the appropriate arguments (Usually the Structural Intention) and the conversion operator will do all of the work.
//Class Expression Idiom
class DoIt
{
//Reference to the structural intention.
const Type& m_ref;
public:
//Set out reference.
DoIt(const Type& ref):m_ref(ref){}
//return the result of our expression.
operator ReturnType ()
{
return ReturnType(m_ref.a + m_ref.b);
}
};
//Client
Code
…
Type clsTest;
ReturnType retValue = DoIt(clsTest);
There are probably more Idioms which I have not yet noticed. They will be posted on the NOP main site whenever they are discovered.
Your Successor
One of the two biggest mistakes a programmer can make is to think that no one will ever read his code; and the person using his code is unqualified to understand it. This first mistake is obvious. It’s all about accountability, and clarity. If your code breaks and no one can understand it, someone will rewrite it. Also if you didn’t take the time to make your intentions crystal clear, the person working on your code may break your architecture in order to add a feature or fix a bug. This will lead to the eventual decay of your architecture.
The second mistake is perhaps more harmful. If you believe that your code is “rocket science” then for the short term everyone else will consider it rocket science (at least until they free up some time to actually think about it). What will happen is that you will become the “owner” of that code. This means that whenever something comes up, you will be the most likely person to take care of it. When new projects or tasks come around you will be stuck working on your “rocket science” code and unavailable to work on anything else. My point is that no matter what, you have to write clear concise code. Someone is always going to have to look at your code. NOP will hopefully provide you with enough tools to solve complex problems, write beautiful code to be productive developer.
Ten Rules of Nano Oriented Programming