MicroObjects Driving Principle? (Part 1)

I had a fantastic chat with a colleague about the MicroObjects technical practices at lunch yesterday. Well... yesterday for writing this. Probably a week ago when it goes live... and no idea how long ago for you reading this. :)
She wanted to know more about the technical practices and was wondering if I had a nice bullet point list... Why yes, yes I do.

For those that may not know, my blogging on the practices is fairly well represented here microobjects, though I do have some more recent posts (like this one) that may not be represented there. And I stopped actually tagging things... so they might be harder to find. :|
I also have all of my slide decks available on speakderdeck from my talks. Ways to see what I promote.

She was interested in digging deeper into why I think these practices drive near-zero bug in the code. Which, hell yeah!

I don't think it's something I've written any posts about specifically... That'll be a good one. The practices drive code to near zero bugs. She wanted to know why I hold that position.
I wrote back my "big win" for each of the practices. As I wrote that I realized a pattern in how I saw things. This pattern is probably going to get a few posts as I think it through and play with the idea.

As the title of this post suggests, it might be the underlying overarching principle of how I write software, and the microobjects technical practices are the mechanism I find most effective to get there.

What is this Driving Principle?

Represent All Concepts

We need to represent all of the concepts that exist in the code as unique things. In Object Oriented languages, that's an object for every concept. In Functional... something else? A function per concept? I don't know - I haven't find FP yet. Procedural has similar ways we can encapsulate concepts.

Ignoring non-OO - This is still all about encapsulation; it's how we can effectively represent every concept that exists in our code. But just "encapsulation" is lacking. I've always felt it was lacking. It's part of it, so it's what I used, but it wasn't quite enough. I've found the higher level that encapsulation is part of - Concept Representation. This might impact how I write code, but I'm betting that however all concepts are represented, it'll be better code. It's a slight deviation from my an earlier way I looked at this of being 'a class per behavior...ish'. I think that's still mostly true; but it never felt quite right; there were exceptions to it.

I can't think of an exception in any of the microobjects code I've written for Represent All Concepts.

Much as I did in my email to my colleague, let's walk through the technical practices I advocate and explore a little more how they tie into representing all concepts.

The Practices

No Getters / No Setters

This is the explict focus on behavioral encapsulation. If we don't restrict where behavior lives, we can never represent all of our objects. Data structures aren't objects. They are data. They don't do things for us.
The power behind representing all objects is that the object does what we need. We don't have to implement the code to do it. Our representations must expose the behavior(s) they provide for that concept. Mutliple concepts may be related and expose the same behaviors; that's what we want. That's OO's wonderful polymorphism.

No Getters / No Setters ensure that the behavior of the concept in our code exists only within that concept.

if Only as a Guard Clause

This has the sub-heading of "No switch and no else".
Allowing branching control structures in the flow of the code is decision making. We're making decisions about what behaviors to do. These behavior decisions are outside the control of the concept. If we need to decide if we should call a method or not

if(fizz.Buzz()){
    foo.Bar();
}

just call it and let it make the decision.

foo.Bar(fizz);

//Inside Foo
void Bar(Fizz fizz){
    if(!fizz.Buzz()) return;
    //Do stuff.
}

This starts to tie into my "Trust your code" approach.
When we allow the concept to control what and when it does things we start to simplify our code. In the above example, we'll always have to check something before we call foo.Bar()... fail to - potential Bug.
Looking at some code colleagues are working on, there's a bug because they didn't do this check. It's easy to make the mistake and let the bug in becuse the code doesn't stop you from making this mistake. When we avoid this kind of branching, we eliminate the ENTIRE class of "did you check if it's OK to call" bugs.
It enables us to drive cohesion into objects. Maybe whatever has if(fizz.Buzz()) only needs a Fizz object for that check. If that's the case, then Foo can create the instance of Fizz.

foo.bar();
//Inside Foo
void Bar(){
    if(!fizz.Buzz()) return;
    //Do stuff
}

Our objects start to better represent the concepts that they are. They hold all the information they need. The information needed to correctly execute the behaviors they should is part of the concept the object represents.

If's only as a Guard Clause allows us to eliminate the ability to control how/when other objects behave. We either ask the object to do X or we don't. The object knows if it should ACTUALLY do X or not because that's a behavior that's part of the concept it represents.

Isolate Their Code

Their code does something for us. It's a concept we have. We want to control how those concepts are represented in OUR system. OUR representation is unlikely to need every behavior their code provides. Their concept is not our concept. We need to isolate THEIR concept in OUR concept.
We only want representations of the concepts that exist in our code - which their code is not.
This includes OS, UI, Libraries - All of those are concepts that are either to generalized and/or expose far more behaviors than our representations need.

Next Week

Next week we'll go how more of the practices support Representing All Concepts

Show Comments