Something Isn’t Something Else


                                         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.

8 thoughts on “Something Isn’t Something Else

  1. Hi,
    Great post, I’ve run through all your blog posts and you’re doing quite a good job!
    I am myself a programmer of several years, and always been curious about OO, it’s only recently that I started to take a deep dive to OOD to improve my OO understanding in order to make better object oriented software. I found out that it’s quite difficult to find good materials on the internet amongst the horde of so-called OO tutorials, wherein “data+operations” are the only poor (though concrete) definition for an Object. Of course, there are stille some advanced topics about Design Pattern, SOLID, Demeter etc. but I’m still looking for more advanced techniques. 🙂

    When reading your conversation with Ladd on your chess game, I’ve realized that I have the same kind-of “problem” about design as you, I tend to be never satisfied with my designs. I very often find something after a moment that causes me to re-think the problem with a different mindset and then to refactor most of the code ^^”

    I’ve read dozens of other blogs such as yours, Yegor’s, James Ladd and his “East” principle, Sandi Metz’ book & videos, and I’m currently in the middle of the Object Thinking book by Dave West. It’s very informative and that’s the kind of contents I’m looking for 🙂
    One of the issues I still struggle with, is about making flexible interfaces, reducing the context of messages, naming messages/methods appropriately, and I would like to know if you finally mastered it 🙂

    Anyway, keep up the good work!
    Nicolas

    • Hi Nicolas. Thanks for reading. I’m glad you’re enjoying my blog. I appreciate the kind words.

      I can relate to the difficulty you’ve experienced with finding good resources about OOP. The popularity of mixed-metaphor languages, like Java created an abundance of information on a style of OOP that is actually a mixture of OO and procedural programming. I found even while researching and using recommended practices, my programs were difficult to change and suffered from poor coupling and cohesion because procedural metaphors assisted in making weak abstractions rather than strong ones. Even with SOLID and Demeter, there was no clear way to know if I was using those techniques well.

      You’re correct that the “data+operations” idea is pervasive. While it’s not entirely incorrect, it misses the bigger idea. It wasn’t until I started talking to James Ladd, and reading and watching Sandi Metz’s content did I find that “messages” are the missing concept from the available information on OOP. It seems that Smalltalk programmers like James and Sandi are the only people that are familiar with this concept, and it is more powerful than the “data+operations” idea. James’ “East” principle is a technique that has definitely helped in clearing up ideas like SOLID, Demeter, and “Tell, Don’t Ask”. As far as design patterns go, if you read “Smalltalk Best Practice Patterns” by Kent Beck, you’ll find some different, more applicable patterns that I think are more natural compared to some of the Gang of Four patterns.

      The problem you saw in my conversation with James where I constantly re-think the problem is what I call the “what if” problem. When I have a domain problem to solve, I solve it and then think, “well, what if I want to do ‘x’ in the future, or what if another developer using the code wants to do ‘y’?” Then I would worry that my code isn’t robust enough to handle all those possible cases. Like you, I would always be unsatisfied.

      One of the things I realized when faced with solving a domain problem, is to just code it in a way that matches the problem at hand. OOP allows staying in the “problem space” and deferring decisions (putting off implementation details, i.e., solution space concepts). If you code something that solves the problem in terms of the problem and hide implementation details then you have succeeded. Even if what you code doesn’t let you do all the things you can think of. If you solve the problem at hand and your code matches the problem, then you should be satisfied. If you start thinking of other things, then maybe it isn’t a problem with the code you just wrote. Maybe it’s another problem to solve that should have a new set of objects communicating in terms of the new problem space. I think David West talks about problem space vs solution space in Object Thinking.

      If your customers or domain don’t pose the problem, then there is no reason to code it. Changes should be based on feedback from customers or developers using your code. If you code to the problem, then hopefully requested changes will fit in naturally (or more naturally than they would otherwise).

      Object Thinking is a good book. It helped me see that OOP is not just “data+operations” and put me on a path to “fixing” the way I use OOP.

      I definitely haven’t mastered the topics you mention, but I’m finding ways of making better decisions when faced with the uncertainty of coding new objects and interfaces.

      I don’t think you can know how flexible interfaces are until you have to change code. I think if we code to the problem, then we can expect changes and additions to map more easily to the code.

      I wrote about context in my post “Revisiting Nouns and Verbs”. As programmers, we have an extra dimension of knowledge about our objects and their inner parts. Objects themselves don’t have that knowledge of other objects, but we can forcibly code that knowledge into them by adding unnecessary details about their collaborators. I think a good rule of thumb is to make sure objects only talk about what they know with respect to the part of the domain that they are responsible for.

      As far as naming goes. I’m currently working on a post about a technique that could help with naming. Stay tuned.

      Thanks again. 🙂 Let me know if there’s anything else you’d like to discuss.

  2. Hi there,
    I’ve received a notification of a new blog post, but the link provided in the mail is dead and I can’t find the post on your blog. Is that an error?
    See ya!

    • Hey Niko. Yes that is an error. Sorry about that. I never could could get the hang of the WordPress publishing tools, so I’m prone to accidentally publishing drafts. The notification you got is for a post I’m working on. You should get another notification when I publish it on purpose 🙂

      • Hi Pete. I peek my head in your blog every once in a while. You have good content, which has helped clarify some things for me. Looking forward to the next post!

      • Hey there. Thanks for your interest. I’m happy to know my blog has helped you. I’ve been a bit busy the last few months and unable to write, but I’ve been collecting some new ideas and will hopefully be posting new stuff soon.

  3. Hello, thanks for the post.
    While reading the part of representing SSN as a class with defined interface and hidden implemebtation… But what if this domain definition is widely used in application, and we want to keep distinct functionality separately (and not in one class)? Can you name some solutions of that problem?
    Thanks in advance

    • Hi Spaum. Thank’s for the question. One thing that comes to mind (if I’m correctly understanding your situation) is the “bounded context” concept from domain driven design. Essentially, if you have a concept that is widely used across an application, but has a different meaning or plays different roles in different domains, then the concept can be created in each domain to serve that domain. Each domain can have its own class representing the concept and the class can have its own public interface and implementation designed to service the specific domain. A SSN, for example may play distinct roles in a “Customers” domain vs. an “Investments” domain and one conceptual model may not suffice for both. The key point in my post is that a recognizable concept, like SSN, should be a fully realized concept and passing around a string as a stand-in (in any domain) causes confusion and duplicate code.

      Martin Fowler gives a concise overview of bounded context here: https://martinfowler.com/bliki/BoundedContext.html.

      I’ve seen some folks use extension methods in C# to apply a similar structure. They define a basic class that is shared across an application, and then define extension methods within distinct domains. So you might have App.SSN, and then App.Customers.SSNExtensions and App.Investments.SSNExtensions, which can be included within the individual projects.

      I know some languages like Ruby allow you to dynamically add functionality to classes, so you could do a similar thing as the C# example by creating a basic SSN class that is shared, and then in specific domains append methods specific to the domain.

      I hope I’ve understood your question and that this helps. Please feel free to discuss.

Leave a reply to Niko GJ Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.