Something Isn’t Something Else

12 min read Original article ↗

                                         888    888      d8b                   
                                         888    888      Y8P                   
                                         888    888                            
.d8888b   .d88b.  88888b.d88b.   .d88b.  888888 88888b.  888 88888b.   .d88b.  
88K      d88""88b 888 "888 "88b d8P  Y8b 888    888 "88b 888 888 "88b d88P"88b 
"Y8888b. 888  888 888  888  888 88888888 888    888  888 888 888  888 888  888 
     X88 Y88..88P 888  888  888 Y8b.     Y88b.  888  888 888 888  888 Y88b 888 
 88888P'  "Y88P"  888  888  888  "Y8888   "Y888 888  888 888 888  888  "Y88888 
                                                                           888 
                                                                      Y8b d88P 
                                                                       "Y88P"  
d8b                  d8b 888                                                   
Y8P                  88P 888                                                   
                     8P  888                                                   
888 .d8888b  88888b. "   888888                                                
888 88K      888 "88b    888                                                   
888 "Y8888b. 888  888    888                                                   
888      X88 888  888    Y88b.                                                 
888  88888P' 888  888     "Y888                                                
                                                                               
                                                                               
                                                                               
                                         888    888      d8b                   
                                         888    888      Y8P                   
                                         888    888                            
.d8888b   .d88b.  88888b.d88b.   .d88b.  888888 88888b.  888 88888b.   .d88b.  
88K      d88""88b 888 "888 "88b d8P  Y8b 888    888 "88b 888 888 "88b d88P"88b 
"Y8888b. 888  888 888  888  888 88888888 888    888  888 888 888  888 888  888 
     X88 Y88..88P 888  888  888 Y8b.     Y88b.  888  888 888 888  888 Y88b 888 
 88888P'  "Y88P"  888  888  888  "Y8888   "Y888 888  888 888 888  888  "Y88888 
                                                                           888 
                                                                      Y8b d88P 
                                                                       "Y88P"  
         888                                                                   
         888                                                                   
         888                                                                   
 .d88b.  888 .d8888b   .d88b.                                                  
d8P  Y8b 888 88K      d8P  Y8b                                                 
88888888 888 "Y8888b. 88888888                                                 
Y8b.     888      X88 Y8b.                                                     
 "Y8888  888  88888P'  "Y8888                                                  
                                                                               
                                                                               
                                                                               

(ASCII art text provided by http://patorjk.com/software/taag.)

A conference talk by Sandi Metz called “Nothing is Something” (Nothing is Something – YouTube) inspired the title of this post. In her talk, she talks about several code design topics and in her usual manner, is entertaining and profound in her statements. Everything she says made me pause and think as if someone just explained one of the deep mysteries of the universe.

One of her points is: “nothing is something”, meaning if an object is sending a message to an object or objects and you expect a receiving object is null, then the programmer should introduce an object that provides a default response instead of repeatedly performing a “null check” and providing a default response. Essentially, you’re missing an abstraction that will prevent you from duplicating code (null-object-pattern-ish). It’s a good talk.

That being said, this post has nothing to do with the actual content of Sandi’s talk. My title is just an homage to the title of her talk. I don’t know why I felt the need to explain this, but there it is.

Who Loves ASCII-Art Text? I do!

What’s up with that ASCII-art text? It’s a metaphor for what this post is about. That ASCII art resembles the same alphabetic characters you’re reading on the screen, but they are not the same. Nor are they the same as the characters I’m using to write this post or the characters I use to write a grocery list. An ‘s’ character composed of symbols is not the same as a written ‘s’ character. Just like a painting of a bowl of fruit is not a bowl of fruit.

We recognize them the same way visually, but they are not the same. They are not created the same way. They are not used in the same ways. If you disagree, then I hope you like eating paint.

Standing in a Garage Does Not Make You a Car

I’ve been thinking about noun/verb analysis (what else is new?) and paying particular attention to the words and concepts I use to analyze a problem or domain and noticed a pattern, which I’ve named: the congruence problem. Fancy, eh?

Congruent is an adjective describing things that are in harmony. In geometry, it describes shapes that superimpose perfectly without changing either shape’s dimensions.

In the context of programming, I assert a congruence problem is when a domain provides explicit concepts, but a programmer chooses to code the concept using abstractions that don’t match, and are often at a lower level of abstraction. Essentially, what is in the programmer’s head is incongruent with the code; they don’t superimpose well.

Incongruent

WTF, right?

It sounds a lot like “primitive obsession”, which is using primitive types to represent domain concepts. The difference is the congruence problem speaks to our tendency to ignore certain parts of the domain and instead map our thoughts to concepts outside of the domain. Primitive obsession is often the result. It can also result in dependence on other domain objects to compose functionality in situations where creating new abstractions is a better idea.

Why do we do this? I don’t really know. Maybe we think that using the tools provided to us is the simplest thing to do? Maybe we feel like we’re saving effort by using code that already exists? (We can still use code, that exists. Just hide it in a new abstraction). Maybe we’ve been told by TDD that we should do the simplest thing that works? (But the simplest thing doesn’t mean the most primitive thing.) Maybe we’ve been told that introducing new classes is adding technical debt? I’ve read/heard people say that you should add classes carefully because you have to “maintain the abstraction”. The truth is, we have to maintain every bit of code we write, including the code that uses primitives instead of new abstractions.

While I’m writing this I’m thinking of an article by Sandi Metz, called The Wrong Abstraction. She asserts that replacing duplication with the wrong abstraction is worse than duplication. The reason she says we do this is far from technical, and speaks more to our human tendencies, which you can read about in her post.

I’m not talking about duplication here, but I am talking about our human tendencies. This is about mapping our thoughts to code to enhance the objects we work with; enhance our interface to the program as well as the interfaces between objects. I don’t suggest replacing primitives just for the sake of it. I suggest using the domain’s language to create the base abstractions that the code likely should have. One of my goals when writing code is ensuring that the next person to read my code understands it with as little effort as possible. I think it helps if code maps closely to the domain, i.e., it reads and writes as you would think about it.

I see programmers introduce vaporous Service class after Service class, Model after Model, View after View, and Repository after Repository. When someone suggests adding a class like SocialSecurityNumber, the response is an underwhelming shrug toward using a String, because that’s all a SSN is, right? Making a class for that is unnecessary, and insignificant and we’ll have to “maintain” it. That’s the story of how you end up with 14 different ways of creating, parsing, and comparing SSNs. Only some of which are implemented correctly.

For some reason, we think one thing and write something else. Incongruence forces programmers to manage the misaligned parts. They constantly adjust code to fix values and state that are strewn about because nothing encapsulates related things in a domain-level representation. Values are open for any procedure to modify, and state changes occur everywhere.

char apple;
double bird;
int person;

Do any of those variable declarations make you feel funny? They should. They’re weird.

How about these?

Apple apple;
Bird bird;
Person person;

The same goes for dynamically typed languages. I shouldn’t expect a variable named apple to have char methods, or a bird variable to have double methods.

A social security number is a String as much as an apple is a char, a bird a double, a person an int, or standing in a garage makes you a car. Something isn’t something else. If you have a name for something, that’s what it is.

I Know the Horse is Dead, but I’m Not Finished

We often introduce domain concepts in our code, but at some point stop as if some things don’t deserve recognition. Formalizing domain concepts makes code more powerful. If you have two or more things that have a relationship to each other and you encapsulate them in a single familiar abstraction then what you have created is a one-to-many relationship between the concept and possible implementations. You also have a many-to-one relationship among the many things you can do with that concept and the one location that implements them.

Even if you have one thing and encapsulate it, the new level of indirection provides an opportunity for a one-to-many/many-to-one relationship. This relationship allows programmers to interact with code at a higher level. It allows changing the internal concepts that compose the new abstraction, or changing the implementation without affecting callers of the code or our mapping of thought to code.

In my post about Smalltalk, I quoted Alan Kay talking about OOP vs procedural languages. He talks about encapsulating data and procedure behind an interface, which creates something metaphorically equivalent to the computer itself. He says to that, “…you’ve done a powerful thing in computer science, which is to take the powerful thing you’re working on and not lose it by partitioning up your design space.”

When we ignore domain concepts, instead exposing primitive parts, we take a thought/concept and partition it into primitive pieces, thus losing integrity. We’re forced to maintain the relationships in our mind and hunt through code for implementations.

For example, implementing SSN functionality as a String distributes functionality and rules, and calling code has an obvious dependency on functions dependent on Strings.

SSNString

Hiding the dependency on string and keeping the behaviors provides something dependent on the behaviors and must implement them. It’s a central place for the behaviors and rules. This also shows the separation of messages and methods. validate(), for example is an abstract message, while validate(string) is part of the method implementing it.

ssnstringencapsulated3

Naming it after the domain concept provides a class/object.

SSNClass

Quick Examples

I examined the “bowling score kata” and thought about how I might design it. If you’re not familiar with this code kata, it’s to design a program that calculates the score of a bowling game.

I naturally started with how bowling works and how scoring works. For those unfamiliar: a bowler bowls a frame consisting of a maximum of two throws. The frame is scored in the context of the other frames, i.e., some frames (strikes, spares) require future throws or frames contribute bonus points to that frame and further add to the total score.

I considered the first message and which object would receive it. What should the interaction look like? What should the method signature look like? What objects are involved?

A first thought was to start with a class like this:

public class BowlingScoreCard {

    public void WriteFrame(int firstThrow, int secondThrow) {
        // ...
    }
}

Here I’m writing throws as integers.

I thought about using:

public class BowlingScoreCard {

    public void WriteFrame(Throw firstThrow, Throw secondThrow) {
        // ...
    }
}

But it still doesn’t match what I’m thinking. The throws have a relationship to each other, called “frame”. So I changed it to:

public class BowlingScoreCard {

    public void WriteFrame(Frame frame) {
        // ...
    }
}

This is almost unquestionably accurate to what I’m thinking. This simple change significantly affected my ability to design. It removed certain implementation details from scoring that I was able to ignore and add later with the frame concept. If I write what I’m thinking, it’s less likely to require changes and extra effort to make it conform to what I’m thinking.

I’m not suggesting that every noun in the domain be a class and every verb a method. What I’m saying is that every significant noun should probably appear as a class, method, or both, and every significant verb should probably appear as a class, method, or both.

The concepts of frames, throws, bonuses, etc. should appear in some explicit form in the application. I should use them wherever I find myself thinking or talking using those concepts. This makes calling code less dependent on implementation details, because those details will be an afterthought.

Consider something else. A bank account example. Maybe two concepts from the domain manifest themselves in code as classes Account and Transfer.

public class Account {

    public void deposit(Money money) {
        // ...
    }
}
public class Transfer {

    public void executeOn(Account account) {
        account.deposit(this.transferAmount);
        // Maybe do some other things to the account.
    }
}

It’s possible that transferring money to an account has other behavior outside of just depositing. Transferring is a concept. It’s already been captured in a class, but maybe the system should also capture it in a message sent to Accounts.

public class Account {

    public void deposit(Money money) {
        // ...
    }
    public void transfer(Money money /*, whatever else...*/) {
        // ...
    }
}
public class Transfer {

    public void executeOn(Account account) {
        account.transfer(this.transferAmount /*, whatever else...*/);
    }
}

You could depend on the “deposit” concept for transferring, but in this context it’s more primitive than “transferring”. The idea of transferring more closely fits the concept we’re talking about. The urge to reuse deposit might be instinct, but we can still reuse it within the concept of transferring. Creating an explicit message for transferring protects us from possibly having to alter calling code that wants to transfer, and from having to change the deposit method to fit the needs of transferring. deposit can continue to do one job, and transfer is a composed method that includes depositing and whatever else.

Fin

That’s it. Something isn’t something else. Write code in terms of the concepts and not in terms of the available implementations, or the code will forever depend on those implementations.