DEV Community

Ka Wai Cheung
Ka Wai Cheung

Posted on

The dichotomy of writing and reading object-oriented code

I've spent most of my twenty year career writing object-oriented code. Even though I've been at it for so long, I still find myself stuck with a particular roundabout habit.

I usually don't start writing code in an object-oriented way. Instead, I often refactor into object-orientedness as part of my editing process.

Here's a very simple example. Suppose I have a class called User. It looks something like this:

public class User
{
    public readonly string FirstName;
    public readonly string LastName;

    public User(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }
}
Enter fullscreen mode Exit fullscreen mode

In some other place in my application, far from the User class definition, I have an instance of the User object and output some information. Perhaps, I want to return a statement exclaiming a user has finished a task. Here's what I'd write:

public string GetTaskResponse(User myUser)
{ 
   return myUser.FirstName + " " + myUser.LastName + " has completed the task!";
}
Enter fullscreen mode Exit fullscreen mode

When I need to assemble some logic using the data within an object, I tend to assemble that logic at the location that currently needs the logic. For instance, if I'm working in a view class that displays a user's full name, I'll scribble out that bit of logic in the view class first. I do the work in the place where the work needs to be done. This approach feels most natural to me. But, it is also not a particularly object-oriented style of thinking.

After I've completed this phase, I then hunt for where I can break this logic apart. I investigate whether I've duplicated some logic that already exists in another method to keep my code DRY. I also figure out where I can extract logic and replace them with more expressive methods within the class I am working in.

Back to our example, I might extract the portion of the string that creates the user's full name into a new method, still living in the place where I'm doing the work:

public string GetTaskResponse(User myUser)
{ 
   string fullName = getFullName(myUser);
   return fullName + " has completed the task!";
}

private string getFullName(User myUser)
{
    return myUser.FirstName + " " + myUser.LastName;
}
Enter fullscreen mode Exit fullscreen mode

Up until now, though, I've simply written and refactored out code in a procedural way. My next move parks this method into the object it rightfully belongs to. In this simple case, getFullName() has an obvious home. It feels more appropriate in the User class itself. And, because it need only inspect the elements it owns, I can turn this method into a readonly property. FullName is now just another property of a User instance:

public class User
{
    public readonly string FirstName;
    public readonly string LastName;

    public string FullName
    {
        get 
        {
            return FirstName + " " + LastName;
        }
    }

    public User(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }
}
Enter fullscreen mode Exit fullscreen mode

This also pretties up the original method I was working in as well:

public string GetTaskResponse(User myUser)
{ 
   return myUser.FullName + " has completed the task!";
}
Enter fullscreen mode Exit fullscreen mode

For a simple example like this, I've gone through this mental dance enough times to skip right to the end result. But, as the logic around features gets increasingly entangled and complex, I go through this dance a lot more deliberately. The refactorings aren't so obvious. For me, object-oriented code is the end result of a fair amount of contemplative code editing.

The funny thing is, I read code in much the same way. Procedural code feels far more approachable to me at the outset. If I'm on the lookout for a specific piece of logic, I can probably hunt down its whereabouts by driving down a procedural codebase far more quickly than by hopping around an object-oriented one.

If reading or writing object-oriented code feels strange to me, then why program this way at all? Because, to me, object-oriented design wins out in the long run. The merits of OOP pay off the longer an application lives and grows.

An object-oriented codebase paints a far more consumable mental model of the entire application than a procedural codebase. But, consuming the whole thing naturally takes time. The trade-off I make with OOP is the immediate understanding of one particular piece of the puzzle for a slower, more methodical understanding of the application as a whole.

Keeping a clean, expressive, and well-designed object-oriented codebase is hard work. It requires persistent re-evaluation and discipline. The leap of faith is that it is in this re-evaluation where you cement a far better understanding of what you are actually building.

Top comments (7)

Collapse
 
royhobbstn profile image
Daniel Trone

This is exactly my thought process as well, though I write JavaScript rather than Java, so I refactor heavily to small functions and modules. Writing custom Class-like objects with methods and _hidden properties that are more than just data structures is a completely unnatural paradigm to me. I've often wondered if this is a consequence of being self-taught or if this is 'just the way I think'.

Collapse
 
developerscode profile image
Ka Wai Cheung

Good to know I'm not alone :)

I'd guess that it's the way most people perceive the world. You have a thing you need to build and you build it in the way you interpret it in your mind - which is fairly linearly. In a strange way, you're the sole "object" at play at the time you create that code. Refactoring into OO is kind of like looking back in the rear view mirror and thinking about driving down that path again, but letting the real "objects" do the work.

Collapse
 
adanteny profile image
Alain Danteny

Interesting article, although I disagree with 'the spirit' of it. IMO, you cannot write OOP code the same way you write procedural code: you have to think different (no pun intended) - and the error that was made is to consider C++ as a better C, for instance -.
Once you know/decide 'what to do','who is acting' and 'what is involved', your classes and methods and relationships are obvious, and you can start to code 'object', even as a prototype or a simple approach.
Then, 'refactoring' sounds more like 'refining' to me, as OOP coding is also an iterative process... but not in the way you describe.
But I totally agree with your conclusions ;)

Collapse
 
ghost profile image
Ghost

Well done, intelligent walkthrough. Mental models are important.

Collapse
 
k2t0f12d profile image
Bryan Baldwin • Edited

"Refactoring" working procedural code into an ideological OOP design is a completely unnecessary step.

Collapse
 
gosukiwi profile image
Federico Ramirez

Have you tried functional programming? Personally, I think imperative is a mess, and prefer OOP (a-la smalltalk), but functional is also nice, Clojure, F# and OCaml are great :-)

Collapse
 
developerscode profile image
Ka Wai Cheung

I haven't looked into functional deeply yet. Thanks for the suggestion!