Posts

Showing posts with the label Design Patterns

Name Your Problems

Sometimes when developing code, I run into problems. Ok, a lot of the times I run into problems, but there's a certain kind of problem I run into pretty often. The problem is that I want a thing that doesn't exist. I am writing code, and it is just getting gnarlier and more tangled around a pile of logic. At some point, I hit a wall with it. It is too complicated to really reason about, and I don't want to make things any worse. I've found a trick to solving these problems, though. I take those problems and give them a name. Once I do that, I can get a handle on them, manipulate them, or even sequester them off into a corner. In almost every case, this drastically reduces the complexity back down to a reasonable level, and I often get the side benefit of offering extensibility I didn't know I needed. To demonstrate, let's say we are working on an e-commerce site. The company is based in Alabama, and sells to customers in all 50 states in the US. We have a met...

Magic Numbers, Semantics, and Compiler Errors

Image
Magic numbers are considered such a scourge upon code bases that there tools out there to automatically find them and root them out. After all, who knows what setValue(6); really means? There are also many approaches to giving better names to magic numbers. Not all of them are good, though. For example, we have a callback that sets a completion percentage on a status: void callback(Status status) { status.updateProgress(0, "Starting"); // Do some stuff... status.updateProgress(25, "Some stuff done"); // Do some more stuff status.updateProgress(50, "More stuff done"); // Reticulating splines status.updateProgress(75, "Splines reticulated"); // Validate everything status.updateProgress(100, "Complete"); } Those percentages would get flagged as magic numbers by the automatic tools. The developer would need to extract those out to constants to make that tool happy. A thoughtful developer would create ...

Break Down Code to Make It DRYer

Image
We are always looking to make our code DRYer . Code that is not repeated is easier to maintain. We can make a change in just one place rather than hunting for different incarnations in many different places. Making code DRYer is not always easy, and sometimes how to remove the duplication is not apparent. Let's say we have the following two functions: public List<Stats> processLines(File file) { List<Stats> stats = new ArrayList<>(); for (String line : lines(file)) { Data data = extractDataFrom(line); Stats dataStats = calculateStatsFor(data); stats.add(dataStats); } return stats; } public List<Stats> processLines(List<File> files) { List<Stats> stats = new ArrayList<>(); int fileNumber = 0; for (File file : files) { for (String line : lines(file)) { Data data = extractDataFrom(line, fileNumber); Stats dataStats = calculateStatsFor(data); ...

Every Case Is Special

Special cases are everywhere. This one little thing has to work this other way. Soon, there is an if/else chain twenty conditions long. Worse, that if/else chain is embedded in another chain of twelve conditions, all in one happy method. The next person making a kind of change has to tack on another condition and hope nothing blows up. We know it's bad when we see a bird's nest like that. How can we deal with it? Why should we? The problem with code no one understands is that no one understands it. We will have a huge problem the day it stops working. We assume that, if it works now, it will work forever. That is a faulty assumption. The world changes around us. The solid rock beneath that code can erode away. And our hair will be on fire when that day comes. Special cases are special. Normal cases are also special. Every case is special in some way. There are some classic special cases common to many code bases. There are quirky special cases unique to your own code. We ne...

Inheritance as Polymorphism vs Inheritance as Code Sharing

We've all heard the mantra that we should favor composition over inheritance, and in most cases this is good advice. Occasionally, though, we really do need an inheritance relationship. Inheritance provides that useful “is-a” relationship that sometimes fits a situation so well. The pitfall we have to watch out for is using inheritance for code sharing between classes that should otherwise not be related, and not for polymorphism, that “is-a” relationship. What makes code sharing through inheritance so bad in the first place? An inheritance relationship is one of the tightest dependencies that could be made between two classes. It is hard-compiled in, and cannot be swapped out at runtime. It's an all-or-nothing relationship, and anything the parent wants to expose, the child must also expose. The child class may be able to override some of the parent behaviors, but the parent may finalize some methods and prevent this, locking the child class in even more tightly. This may be...

YAGNI vs Uncertainty

You're sitting there staring at the (pretty good if you say so yourself) code you just wrote. It solves a pretty tricky problem in a pretty elegant way. But something is worrying you: this code might need to change. You're not sure when and you're not sure how, but you just get this feeling tingling in the back of your neck that this code is going to have to change, and probably sooner rather than later. Somehow sensing your hesitation from across the room, that guy comes over. Yo, bro, looks like you got a problem. Just remember, bro, YAGNI! You ain't gonna need all that! You've heard it all before from that guy . You've even followed his advice a couple of times. Once, yeah, he was right, but that other time... oh, that other time. So much extra work that you just know you could have avoided, somehow. Maybe I exaggerate a bit, but we've all seen or heard of or (shudder) worked with someone who strictly follows the One True YAGNI. They never add any co...

How Big Is Your Object's Interface? The Answer Might Surprise You!

We have a Person object, eddie . Part of the responsibilities of a Person object is to operate a Car , accessible as eddie.getCar() . Car s are made of parts like Door s and Seat s and an Engine . Starting a Car entails starting its Engine . So how would we start eddie 's Engine ? eddie.getCar().getEngine().start(); This accomplishes the task, but we see a violation of the Law of Demeter in this code. We can see this quite plainly: look at all the dots! But what is this "law," and who passed it? I don't think Congress concerns itself with these matters (at least not yet). How bad can writing code like this really be? To understand why we should follow Demeter, we have to see what happens when we don't. Our Person , eddie seems to have a very simple interface so far, a getCar() method. He just has that one method, but that method gives us back a Car with a getEngine() method. That method then gives us an Engine with a start() method. Each intermedi...

This One Subtle Bug You Might Not See Coming

Sometimes bugs are obvious. You used a ++ where you should have used a -- . Other times, bugs are much less obvious. A regular expression was missing an escape of a . . Then there are those bugs that are so subtle and insidious that they deserve their own blog post just to document and excoriate them. This post is about one of those bugs. Fortunately for me, this wasn't my bug. It was a bug hidden in a Programmers.SE question . The questioner was blaming himself for not seeing or understanding a change in an external library, and he was desperately looking for a way to write better code to account for such changes in the external library. What did that library do? There was a method that returned a long, where the long was milliseconds since epoch. In the update, the method was changed so that it still returned a long, but the long now represented nanoseconds since epoch. Putting aside the discussion about whether a long has enough precision, this change is a special type of a...

Moving Sideways with Lateral Abstractions?

When I'm doing design and refactoring, part of me worries that I'm just creating a mess of objects and classes. One of the kinds of mess that I feel I make the most are lateral abstractions. What I mean by lateral abstraction is an abstraction that is at the same level as the code I abstracted it out of. It doesn't add very much to the conversation, and it doesn't introduce a finer vocabulary than was already there. When looking over the code I wrote in Writing a Better Code Narrative , I get a slight feeling in the back of my head that some of that code is lateral abstraction. Without re-posting all of the code (which can be found here ), I started with a CommandParser class that read a string of character commands and "executed" those commands to produce a new output string. The original code was intentionally hairy, but I refactored it down into a series of Command classes to encapsulate the individual behaviors, and a CommandFactory to encapsulate the...

This 1 Weird Trick to Almost Any Design Pattern!

I was answering a question on Programmers.SE the other day, as I am wont to do from time to time, and something interesting struck me. As I was constructing my answer, I found myself looking for a design pattern to name that would be apropos for the situation (I settled on the command pattern ), and while I was grasping for the right one, I thought, I want the one where we put an object in between. It hit me: that's almost all of them! That's right, nearly every design pattern is basically just "create an intermediate object to encapsulate what you want." For example, we have the command pattern: create an object that calls behavior in the way that we want. We have the adapter pattern : create an object that transforms what we have to what we want. We have the strategy pattern : create an object that will polymorphically perform a behavior we want. As I realized that, I felt like, while design patterns offer a great way to name and model a certain solution when...

Interfaces and the Need to Know Implementations

In many languages, interfaces and abstract classes serve as constructs for abstractions. Other languages rely on duck typing for the same purpose. Either way, we hide the underlying complexity of implementations under simpler façades. Though we may use these abstractions, all too often we don't trust these abstractions. Developers frequently talk about how they can't use an abstract type. They need to know exactly what concrete type of object they are dealing with. But in many cases, we don't or even can't know. It's possible that it is some second- or third-party type that we've never seen. How could we possibly trust some unknown code that is just shoved willy-nilly into ours? But do we really need to know the concrete runtime type? Knowing can simplify debugging. When I need to chase a bug, I need to know where to look. If it is in some indeterminate subtype, how will I be able to track it down? This is a legitimate concern. But tools help there. Mod...

Writing a Better Code Narrative

Writing code is like writing a book in a lot of ways. Like a book, code is read far more often than it is written. Therefore, like a book, code should be written in a way that gives it a clear narrative. By having a clear narrative, one can follow the code and understand it without too much head scratching. There is a lot of code out there that doesn't have a clear narrative. It meanders and backtracks, leading to confusion. One of the biggest contributors to confusing structure is the complex if statement. if 's and else 's take the story back and forth, and all over the place. There are many tools in good design to fight the confusing tendencies of complex if 's. One that changed the way I write my code significantly is the guard clause . Rather than wrapping a whole bunch of code in a big if , we reverse the condition and return early. Rather than worrying about the condition the whole way down, we can conceptually just eliminate that case as early as possible. Gu...

Exposing the War on Design

A long time ago, I watched Sandi Metz' GoRuCo 2011 talk, Less - The Path to Better Design . I really liked that talk, and I felt like I got a lot out of it at the time. She talks about design, and shows how to improve the structure of a piece of code. There were some things I didn't feel like I all-the-way understood the first time around, but it was a really good talk and got a bookmark. Then, one day more recently, I watched the talk again, and I discovered how much I missed the first time around. The ideas about design were much deeper than I originally understood, and those parts I didn't all-the-way understand were suddenly much clearer. We develop software in a cloud of uncertainty. This is not a failing, but simply a reality. This cloud of uncertainty frustrates us when we try to design our software. It frustrates us so much that we as an industry have taken the argument up a meta-level to a debate about whether we should even try to design, when, and how much. We...

The Visitor Pattern Needs Fixed. Here's How.

The Visitor Pattern is a powerful idiom of object-oriented programming. It has the unique power to break through the type system and recover hidden type, all while using the type system and double-dispatch to accomplish this feat. Through this superpower, it is able to give the impression of adding new behavior to closed classes. The visitor pattern is very powerful, but it is also quite burdensome. Classes to visit must implement an #accept method that takes a visitor, and then immediately calls that visitor with itself (generally, or some data on itself). This double-dispatch is what allows the type to be recovered, since a type's this will always be its own type. The problem then, is that there is a repetition on all of the different types to visit. Each subtype must implement #accept . It also treads the line of an SRP violation. A class must be concerned with what it does, plus allowing a visitor to visit it. This really has never sat that well with me, so I began to ...