Null Object Pattern

Summarized using AI

Nothing is Something

Sandi Metz • May 14, 2015 • Atlanta, GA

In her talk 'Nothing is Something' at RailsConf 2015, Sandi Metz explores the hidden assumptions present in code and their significance in object-oriented design. She emphasizes that by exposing these hidden concepts, developers can create code that is easier to understand, change, and extend. Sandi sets out to share the lessons she learned while teaching short object-oriented design classes, distilling her insights into several key points:

  • Understanding Object Messaging: Metz draws from her background in Smalltalk, illustrating how Ruby operates on the principle of sending messages to objects rather than relying on type checks. By shifting focus to this message-centric method, developers can simplify their code.

  • Handling Nil and Booleans: She discusses Ruby's treatment of 'nil' compared to Smalltalk's straightforward syntax. Metz suggests that treating nil as an object rather than a special case can lead to cleaner code and reduced complexity.

  • The Null Object Pattern: This design pattern allows for a default object to replace missing ones, eliminating the need for excessive conditional checks. Metz refers to this approach as achieving 'active nothing,' where placeholder objects serve a purpose in code.

  • Avoiding Inheritance Complexity: Sandi emphasizes the drawbacks of deep inheritance trees. She illustrates this by referencing a programming example that involved dynamically generating variations of a cumulative story. Introduced inheritance can complicate code, so Metz advocates for using composition to manage different responsibilities elegantly.

  • Finding Abstract Solutions: Throughout her talk, Metz encourages programmers to seek useful abstractions within their codebases. Doing so fosters adaptability and flexibility, allowing developers to manage changes without compromising the integrity of their designs.

The key takeaway from Metz's presentation is that every decision in programming—no matter how trivial—has the potential to become 'something' meaningful. By promoting clearer communication between objects and embracing design patterns like the null object and composition over inheritance, developers can enhance their coding practices and create more robust applications. Sandi’s closing message reminds audiences that even in the world of programming, the nuances of seemingly insignificant details can lead to profound insights.

Nothing is Something
Sandi Metz • May 14, 2015 • Atlanta, GA

By, Sandi Metz
Our code is full of hidden assumptions, things that seem like nothing, secrets that we did not name and thus cannot see. These secrets represent missing concepts and this talk shows you how to expose those concepts with code that is easy to understand, change and extend. Being explicit about hidden ideas makes your code simpler, your apps clearer and your life better. Even very small ideas matter. Everything, even nothing, is something.

RailsConf 2015

00:00:12.240 Thank you for coming. I'm Sandi Metz. I was a programmer for thirty-five years and I am a woman of a certain age. I wrote a book a couple of years ago, and when it came out, it felt like a bomb went off in my life. I had to quit my day job because I was so busy doing other things.
00:00:22.960 Because I quit my day job, I needed a way to make a living, and people kept asking me to teach. So, I finally broke down and agreed to teach short object-oriented design classes. Then, I had to create a curriculum, which was incredibly challenging.
00:00:38.840 I sat down to figure out what I thought were the most important lessons I could teach in three really intense days. When I created the curriculum, I believed that the lessons were unrelated—good things, yes, but not the same idea. After teaching that course for the last year and a half, I have finally come to realize that I was really teaching one simple idea all along.
00:01:05.439 Today's talk will cover everything I've learned in the last year and a half in just thirty minutes. This will involve a lot of slides. Based on my calculations, I will change slides approximately every four and a half seconds. If you want to change seats now, it’s not too late to move over to the sides. You've been warned!
00:01:30.720 This talk is divided into four parts, and it will build up from the bottom. The first part is inspired by the realization I had after my book was published: I found that I had a different idea about objects compared to many people in our community. I was curious about why that was, and upon reflection, I realized it was because I had been influenced by Smalltalk.
00:02:00.000 I've been writing Ruby since 2005, but I had not written Ruby for as long as I had written Smalltalk. I say 'infected' because it's humorous, but I feel inoculated by Smalltalk. I want to show you just one small aspect of Smalltalk today that will clarify how I think about objects.
00:02:14.400 In Ruby, there's a method called 'send' that allows you to send a message to an object. You might not be familiar with it, but it invokes the method named in the symbol passed to it. This mechanism illustrates the fundamental truth about Ruby: at the lowest level, we are just sending messages to objects.
00:02:38.760 For example, consider the expression '1 + 1.' What Ruby does behind the scenes is send a message to the number one, and the operation is just syntactic sugar for message sending. This special syntax is what makes Ruby friendly; its surface appearance resembles math, but it's actually about sending messages to objects.
00:03:03.959 Now, when I send '1 == 1,' what I receive back is 'true,' an instance of the true class. This class knows various pieces of information, and for the sake of this talk, let’s simplify the concept and treat 'true' as just an object that we communicate with through message sending. Similarly, we can treat 'false' in a comparable way.
00:03:36.840 When I first arrived in Ruby from Smalltalk, the fact that 'true' is treated as an object wasn’t surprising to me. However, I was taken aback by Ruby's specific syntax for handling 'nil.' It's baffling within an object-oriented language that there's a unique syntax for 'nil,' just as there's for boolean values.
00:04:04.360 In Smalltalk, there are only a few keywords, enforcing a clear and straightforward message-sending structure. Yet, when I encountered Ruby, I found it had this additional special syntax concerning boolean conditions, which seemed unnecessarily complex to me.
00:04:29.280 In Ruby, we evaluate an expression, and based on the result—whether it evaluates to true or false—we execute one of two code blocks. This introduces a type check that, in an object-oriented context, feels counterintuitive. Instead of making type checks, I prefer to simply send messages to objects without concerning myself with their types.
00:05:08.680 If you transitioned to Ruby from a procedural language, writing extensive conditions might feel customary. However, the presence of keywords in Ruby encourages a procedural mindset, which hinders learning object-orientation and taking full advantage of the power of objects.
00:05:46.200 To illustrate how unnecessary this keyword is, let's pretend we change Ruby to have Smalltalk-like syntax for conditions: we could implement our own logic for handling true and false messages. When we break down this logic, we realize we really can send messages directly without needing this special syntax.
00:06:13.240 The core point I want to convey is that we should focus on sending messages rather than considering the types of objects we work with. In object-oriented design, the message-centric approach helps maintain cleaner code. Once we adopt this mindset, we can simplify our interactions with boolean values.
00:06:49.679 Let’s say we have an animal class with a factory method 'find' that returns an object or nil if not found. If you’re handling an array and calling 'animal.find' on each of them, you might receive nil, leading to a situation where your application could crash due to a nil error when messages are sent.
00:07:22.520 My point here is that while nil can sometimes be treated as nothing, when we send a message to a nil object, it represents something, and we need to handle it properly in our code to avoid crashes. Often, we add cumbersome conditions in our code to check for nil values, which results in a lot of extra complexity and verbosity.
00:07:43.440 This ranges from simple checks like saying 'no animal' when there's a nil value to deeper nested conditions that complicate our logic. As Ruby programmers, we want to simplify this mess. Instead of these lengthy conditionals, we should leverage Ruby’s truthy functionality, realizing that nil can also stand for something valuable in our design.
00:08:26.160 Furthermore, if you're using the 'try' method, it may seem convenient, but it tends to convolute the underlying logic. It's simply a shorthand that dusks over the fundamental principle of messaging. That said, we need to foster an environment where we focus on sending messages instead of complicating our logic with type checks.
00:09:01.600 Now, consider that conditions reproduce; if you spread that string throughout your code base, changing that one part can lead to extensive changes across your system, leading to what’s known as shotgun surgery. I despise these conditions and wish to promote a message-centric approach to coding.
00:09:33.520 The core issue here is that sometimes you might receive an object that has an attribute you expect, but at other times, you won't. When you receive objects that conform to different APIs, you risk crashing code that's not prepared to handle those discrepancies. What we aim for is the establishment of a null object, or a default placeholder that stands in when an object is missing.
00:10:05.560 This design pattern is known as the null object pattern, which was beautifully termed by Bruce Anderson as the 'active nothing.' Implementing this pattern within your code results in a clear improvement, allowing you to remove conditions while simplifying your logic.
00:10:36.800 Now that we've established a null object, we can create behavior without owning various responsibilities. By wrapping untrustworthy external APIs in a reliable object, we can manage conditions in a single place while interacting with multiple objects in a uniform manner.
00:11:06.840 The lesson I’ve extracted from the past 18 months is that the null object pattern is just a small instance of a grander abstraction. This talk builds towards that abstraction, and as a stepping stone, let’s switch examples to further illustrate this concept.
00:11:44.800 Consider the tale of 'The House That Jack Built.' It’s a cumulative story where each line builds on the previous one. If I were to create Ruby code that generates this tale without duplicating strings, I would likely use an array to store the various bits and have methods to manipulate those strings accordingly.
00:12:28.360 To produce the lines of the tale, I would iterate through my array, generating each line dynamically. However, let's say the spec changed. Now, they want to introduce a 'random house' where the order of lines is randomized rather than simply recited in sequential order.
00:13:12.560 This alteration necessitates a degree of refactoring in my existing design, as I need to ensure nothing from the original house class is broken. Moreover, I must implement a solution without using any conditional statements, inspiring me to consider inheritance to solve this dilemma.
00:14:01.040 By overriding the existing data method with one that shuffles the order, I can create a new subclass that successfully fulfills this requirement. This subclass needs to be deftly engineered to ensure that original functionality remains intact.
00:14:38.639 The next task is to implement an 'echo house,' a variant where each line gets duplicated as it goes in for extra emphasis. This requires additional refactorings since the methods will undergo various responsibilities, making it difficult to change without careful thought.
00:15:14.560 Eventually, I reach a point where extensive code duplication is occurring simply to manage this echo effect. I'm forced to reevaluate my approach, recognizing the importance of keeping code clean and free of redundancy.
00:15:51.960 To counteract the challenges of implementing additional variants of 'house,' I realize that inheritance creates complexity that can become unmanageable. Introducing a solution based on composition, where I define roles like 'order' and 'format,' becomes paramount.
00:16:39.560 By injecting order and format objects into the processes I need to maintain, I can effectively streamline the structure of my code base and avoid cumbersome inheritance hierarchies. This leads to a more elegant extension of functionality without introducing intricate conditional checks.
00:17:22.760 The final message I want to convey is the inherent value in finding useful abstractions within your codebase. The underlying object-oriented principles allow us to see beyond mere function, revealing deeper relationships and structures that empower robust design.
00:18:05.960 While navigating conditions might yield temporary solutions, I encourage you to embrace principles like the null object pattern and dependency injection. Doing so will yield greater flexibility in coding practices and help you avoid pitfalls associated with conventional inheritance methodologies.
00:18:54.720 Our journey through object-oriented design should focus on clarity and adaptability. Remember to prioritize communication between objects and avoid assumptions tied to specific class hierarchies; this agility enhances the quality of your code. Enough of this back-and-forth messaging—let's make something beautiful!
00:19:39.680 So, as we explore the boundaries of object-oriented programming, consider that every small detail in your design—no matter how seemingly insignificant—is actually foundational. Our challenges, whether big or small, contain important lessons and opportunities for growth, and those lessons will serve you well.
00:20:16.800 In shared experiences through code, we uncover truths that elevate our capabilities. Whether it's navigating the intricacies of inheritance or reaping the benefits of composing our designs through abstraction, the hidden gems emerge when we practice intentionality and diligence.
00:20:49.720 As I conclude my talk, I hope you leave with a renewed sense of wonder and possibilities that lie within code. Remember that every 'nothing' holds the potential for 'something,' and everything can become part of our evolving narrative as programmers and designers.
00:21:21.200 Thank you for listening. I’m Sandi Metz, and I wrote that book. Please sign up to find out more about my upcoming projects, including a new book I am writing. I rarely teach public courses, but I will have offerings in New York City, so sign up for my website if you're interested in that as well as stickers and tattoos. Come find me later, and I hope you enjoyed this presentation! Thank you!
Explore all talks recorded at RailsConf 2015
+122