00:00:24.199
My name is Sandi Metz, and I have been writing code for 35 years. Every day, I went to my desk and worked with code. As a woman of a certain age, I wrote a book by accident. I won’t tell you all about that, but writing a book was like having a bomb go off in my life; it changed everything. I started receiving invitations to conferences, which led me to quit my day job at a university because they were unwilling to grant me that much time off. It became necessary for me to find a way to make a living.
00:00:50.280
I began teaching as people asked me to share my knowledge. For the past couple of years, I've been teaching a lot about object-oriented design. Creating a curriculum is quite challenging. I made up some curriculum, and I have to admit I feel quite bad for the first students of my course. Katrina Owen, who's here, helped me create the curriculum for that first course. Initially, we had a five-day course, which we now teach in three days while covering more content. As a teacher, what can you do? We did the best we could.
00:01:19.720
Back in the day, when I started developing the curriculum, I believed I was creating various important but unrelated exercises. However, my current understanding is that everything I’ve been telling people revolves around a single underlying principle. It’s not a bunch of disparate lessons; it’s really one consistent idea, which I didn’t comprehend at the beginning. This talk will serve as a 30-minute distillation of two years of teaching experience, and it includes a lot of code, so let’s move forward.
00:02:08.560
After writing that book, it became clear to me that my perspective on objects differs from how many of my fellow Rubyists view them. This is partly because I have been using Ruby for ten years, but I spent more years programming in Smalltalk, where I learned object-oriented concepts. I am, in a way, 'infected' by Smalltalk. The structure of this talk is in four parts, each building on the last. I’ll start by sharing a glimpse of what working with Smalltalk has taught me.
00:02:56.760
Let’s begin with an example. You might be familiar with the send message method in Smalltalk, where you can send messages, such as invoking the method 2 + 3 awkwardly by sending a symbol. In Ruby, we have a similar construct. Although it appears unique, it’s present to make us feel at ease. Ultimately, it represents the same concept because we are simply sending a message. This subsequently leads to the necessary results.
00:03:44.320
In Ruby, the fixnum class knows its methods, like what '+' represents. This isn’t special syntax in the language; the special syntax is the syntax that enables us to write expressions like 1 + 1. At the core of every interaction, we’re simply sending messages. You might not require convincing when I say that I’m sending the message equal equal to a fixnum while passing an argument. The returned result is an object, which is an instance of the true class. Rather than finding Ruby's object-oriented structure confusing, I found it odd that Ruby had a distinctive Boolean syntax.
00:05:05.960
In Smalltalk, you have a more concise set of keywords, whereas Ruby includes some special syntax that seems procedural. This complexity is compounded by the existence of 'truthy' values, where a type check defines the control flow rather than just sending messages to objects. This results in condition-heavy coding practices, which I am aversive to. My goal is to simplify this. To illustrate, let’s explore how we can eliminate conditionals in favor of message-sending.
00:06:39.520
In object-oriented programming, we can rethink conditional syntax. For instance, redefining the true class to implement an 'if true' message will yield control to an implicit block when true is evaluated. If false is evaluated, it does nothing, thus directly reflecting the object's behavior without requiring checks. However, this approach does not truly address the entire problem, as all we have are true and false. Incorporating the nil class enables us to treat anything that isn’t false or nil as truthy, resembling the behavior of Ruby.
00:09:03.600
And so, without needing special syntax to control flow, we can just send messages to objects and manage interactions more intuitively. I am not suggesting we actually implement it this way; I want you to consider what object-oriented programming concepts might look like if conditionals didn’t exist. Most Rubyists, coming from procedural backgrounds, may have plugged directly into Ruby due to the presence of the if statement. Imagining a world without that syntax fundamentally alters how we think about object-oriented structure.
00:10:15.760
I am infected by Smalltalk, which makes me highly condition-averse. A common condition I detest includes situations where you receive a key and go on to look it up in a collection—if it's present, you retrieve it; otherwise, nil is returned. This can lead to unexpected behavior, especially when interacting with methods that yield results based on that retrieval. If you pass an invalid key, you'll be left to handle nil, leading to unclear messaging, which is frustrating.
00:11:06.760
It's essential to recognize that nil can sometimes signify nothing, in which case it should simply be discarded. If you encounter nil where you don’t care about it, compact it out of your arrays and streamline your code. However, if nil has meaning, it warrants thoughtful handling beyond just conditional checks. We often see primitive conditional checks leading to ugly coding practices; what we ought to do instead is encapsulate this logic into more elegant solutions.
00:12:27.039
Using conditional checks can lead to code smell, where the need for cleanup starts becoming apparent. In many places, programmers find themselves struggling with the same 'no animal' conditional logic throughout their codebase. This reflection leads to a practice known as 'shotgun surgery,' where changes in one place necessitate adjustments in multiple locations. Instead, my aim is to promote a message-centric approach where we miss the details, allowing the objects themselves to handle their own behaviors—implementing behavior with careful design.
00:14:04.080
When I first encountered the idea of the null object pattern, I thought it was a very powerful concept. Essentially, this pattern directly addresses the need for an object that responds appropriately, even if not all the expected behaviors are present. Instead of returning nil when looking up non-existent objects, I'd rather prefer returning an instance of a class that encapsulates that 'missing' behavior. This instance would always respond easier than having to handle nil checks throughout the code.
00:15:50.039
This 'missing animal class' would be a powerful tool. If I implemented it correctly, I could seamlessly replace nil with a 'missing' instance in various locations within the code. This minimizes condition checks, making error handling significantly easier and cleaner. Returning objects that maintain a consistent API enables the program's functionality without excessive conditional redundancy. To achieve this effectively, it would be vital to adopt the null object pattern, thus ensuring even nothing will still represent a valid something—an essential realization in object-oriented design.
00:17:53.680
Throughout my programming journey, I have learned much about these patterns and designs. Often, the literature available can significantly streamline the process of acquiring these concepts. I aim to share my journey of discovery regarding the null object pattern; after all, I spent years figuring things out the hard way. Now, I realize that incorporating established design patterns saves developers a lot of headaches in the future.
00:18:49.320
So, as we think about integrating these patterns into our applications, I want us to reflect, especially when dealing with nil. If you begin to notice nil as nothing, replace those occurrences with meaningful objects that adhere to your application’s requirements. This active handling of what nil embodies can lead to greater success in managing objection behavior. The higher-level abstraction of the null object pattern illustrates that nothing can indeed be a meaningful representation; it’s an instance of something you can work with efficiently.
00:21:28.920
One analogy I find helpful is the cumulative tale of 'The House that Jack Built'. This tale highlights how concepts build on one another, becoming richer and more complex with each iteration. When creating our code, we can adopt a similar concept—adding new layers meaningfully knows without breaking the previous structures. In designing functions and classes, we should set them up to expressively accumulate bits of behavior and logic without convoluted structures.
00:22:07.200
You can use this same cumulative concept to address new requirements when they come up. For instance, if you built the house structure and someone requested a 'Random House' feature, you can implement this effectively without breaking the existing architecture. By leveraging inheritance correctly while avoiding modified behaviors, your program remains resilient. We can bridge these features harmoniously so that new requirements integrate smoothly.
00:23:57.680
As more features, like moving towards 'Echo House,' arise, injecting behaviors appropriately allows you to sustain functional integrity. It reflects careful planning where each class and method serves their specific role in the program, facilitating clearer resolutions. Remember, adopting the right pattern focuses on abstraction seeking, isolating differentiation, and ensuring behaviors play together coherently.
00:26:17.680
The key takeaway is understanding how to manage inheritance and behaviors collectively without combining disparate aspects inappropriately. By utilizing this understanding, we can outsource behaviors to their respective roles, implementing and injecting these new patterns while minimizing confusion. As developers, it's our job to recognize the lines established by coding behavior and how they elevate our designs, ultimately generating effective solutions.
00:28:48.720
In conclusion, embrace nothingness as an opportunity for something meaningful. Object-oriented design should be about flexibility and understanding the principles that govern effective software structure. Interactions should drive clarity without reliance on cumbersome conditions. I encourage you to seek out more literature, adopt practices like the null object pattern, and acknowledge that believing in nothing can ultimately result in impactful something. Thank you.