Originally aired on
October 25th, 2015
With a new design pattern coming out every week it can be easy to get caught up in all the hype. If you frequently try to implement the latest-and-greatest design pattern and feel constantly paralyzed by the thought, "I know I'm doing this wrong," this episode is for you.
We discuss how "not seeing the forest for the trees" might be a good thing as we try to narrow our focus in order to write better code without thinking of patterns first.
This roundtable was originally inspired by a blog post by Anthony Ferrara "Beyond Design Patterns", which was also a talk given by Ferrara. Both focused on communication between objects and abstraction instead of a bunch of design patterns overwhelming you.
Also Adam Wathan had a FullStackRadio episode which discussed focusing on the smallest unit of code and refactoring to design patterns if it makes sense.
Also Ross Tuck discussed some topics in episode 008 of the PHPRoundTable that touched on design patterns.
3:05 - What is the purpose of Design Patterns?
Ferrara: When Design Patterns were originally developed one of the inspirations was architectural patterns, things we see in real life, things that are well understood that we can reason about and look at. They provide ways for us to reason about code in small logical units, well easily understandable units.
Tuck: They are ways for us to have a vocabulary, to talk about these things, to reason about them, it's also because naming things is very very powerful, because once it has a name we can assign it other properties and attributes...like "this is a good trade off for that" or "maybe this isn't so good for that." Without a name to hang those attributes on, it is very difficult to discuss them. It's also about having consistency, it's sort of like a style guide for how "we" solve this particular problem.
5:00 - Is there a problem when developers try to use design patterns?
Wathan: I don't think there is a problem with design patterns, I think patterns are important and useful to learn. I didn't learn them in school, but they made a big difference when I found the material myself and started going through it. Overtime though, I find that I stop thinking about them so much and instead I tend to internalize the mechanics of them and then I am able to apply them when they make sense without even really recognizing that I am applying something that has a name necessarily. I am more interested in what I learned from the books about design patterns...for example "I never thought about using an object for that type of communication before" or "I never really considered that I could have two things talk together this way" and that's been sort of consolidated into a few pages in a book so you can see an example of how that works. But once you understand the mechanism for the communication you can kind of stuff that in your back pocket in your tool belt and carry it along with you. - He finds some of the more interesting stuff to be in the lower level code and not necessarily in the higher level "system architecture" level. - Younger developers might have a tendency to latch on to a pattern or think they have to use a pattern.
Ferrara: I agree...I think the problem comes when you are a new (developer) and you start asking the question "what pattern should I use to solve this problem?" To me that's a negative...learn how to solve the problem and after you know how to solve the problem look and see "is that similar to a pattern?"
8:45 - Tuck: Seems like there is a consensus (amongst guests), we all think that patterns are really useful concept, and even agree that the problem is less the patterns themselves and the way they are brought to developers, the way we educate developers, is frankly, kind of poor. There are very few good examples, where is a book that we can say "this is well designed code?" It's easy for us (guests are developers for years) to say "I studied the way, I followed the way, and then I moved beyond the way." But there are still people that are still trying to figure out "what the heck is the way?" - Some of it is tooling, some frameworks have a particular set of patterns that the framework developer likes...and so people who learn that framework start to ingrain those patterns and approaches as the way to do things.
10:00 - Wathan: It's interesting that you mention that there isn't a lot of good examples. You look at a design patterns book and it's basically "here's an isolated problem, and here's an isolated solution to that problem" and it's not in the context of building an app.
Book that helps with the context of applying design patterns - "Agile principles, patterns and practices" an Uncle Bob book. Provides an example of building an application where requirements show up and how the author handles the situation. This helped Wathan understand what object oriented design was.
12:00 - Gang of Four
Design Patterns, elements of reusable object oriented software - 23 "classic" patterns described in the book.
The patterns in the books fall into basically three categories: Creational patterns, Structural patterns, Behavioral patterns.
Ferrara mentioned in blog post that they should be referred to as Shim patterns, Compositional patterns, and Decompositional patterns.
13:39 - Ferrara: Reasoning behind reorganizing the patterns came from a feeling that the patterns had a commonality that wasn't really expressed by the original categorization.
Design patterns eventually translate into input/output (I/O) kind of stuff.
Ferrara: It's a little more nuanced than that, it's not I/O, if you get rid of the Shim patterns, which are basically there to "shim" a language to do something it doesn't do natively, you are left with composition/decomposition...what's the difference between writing and refactoring? In practice there is a lot of difference, so there's really a good use for having those patterns separated, but when we are talking about learning about things, and learning about architecture there really is no difference. The technique is different but the end result should be pretty close to the same. This allows you to reduce the patterns down to two basic roles these patterns serve. Communicating between (individual) objects and communicating between systems. Part of Object Oriented Programming, you can treat an entire system like a single object, and a single object like an entire system. So really, all of these patterns do one thing..."control messaging and control information". So it's not really I/O it's more that the message is the important thing. When we focus on that, the messaging, that's really what object oriented programming is about. It's not about the architecture it's about the information.
17:00 - Sammy asks Wathan about focusing on smallest unit and growing from there.
Wathan: Patterns aren't something we implement, they are something we refactor towards. When I talked to Kent Beck on FullStackRadio I asked him about how I noticed he never talked or wrote about high level architecture, he focuses on low-level code usually. His response was "It's all the same to me." This idea resonated with me and I now try to spread that message, helping people who don't have a grasp on the lower stuff understand that there's still a lot to learn there. When they try to implement higher level patterns, they end up making a mess with because they don't understand how to keep the smaller bits inside of it clean and well organized, and behaving and communicating the right way as well. So it makes a lot of sense to me, to get good at those small pieces and let that bubble up into being able to do a good job at the higher level stuff, then implementing some higher level patterns and thinking that is going to result in a well designed system.
Ferrara: That's a really interesting thought, I was just having a conversation with CodeRabi about micro-services architecture and the right way that services should be related to each other, and how DDD fits into that picture. Where I brought up "isn't a service a service whether it's in a monolithic or it's across the network boundary, aren't the principles the same?"
Tuck: OOP is all about messaging, and whether we are messaging at the object level or server level or micro-services level. A lot of those design patterns are the same, if you look at a proxy for example, the difference between an object proxy and an HTTP proxy are actually not that far apart. You might implement a decorator for caching, in principle they're not that different really. I have clients that come to my and want micro-services, and I say "yeah, but you don't know how to build good object composition yet, and object composition is local, it's fast, it's easy to test, you won't have deployment problems" when it comes down to trade-offs, micro-services just aren't a good fit for your situation.
Tuck: You can try to abstract the difference between a local service and a remote service, but it's just not going to work out in the end. But in principle there, it is the same from beginning to end, but people need to focus on getting that object level stuff right because it's a simpler form of messaging. They just don't look at it that way. And I think this comes back to a problem of how it is taught.
24:00 - Wathan: The thing with naming patterns, it's powerful but when you are first learning patterns, you feel like you have to label every object that you make. Like you have to find a pattern that someone has named and people stop thinking about designing objects that are focused on doing something and are able to communicate in certain ways that make sense for your application. You can throw all the "by the book design patterns" away as long as you understand OOP fundamentals that drove out those patterns originally anyway. You are going to end up noticing those patterns in your application after the fact if you internalized the patterns when you learned them and took that exposure to those ideas and built that into the way that you solve problems and I think that's the right thing to do with that knowledge versus trying to think about "I have to make this object, is this a proxy, a mediator, a visitor, a template method." You just get focused on the wrong things and focus on categorizing everything when that's not the right way to think about designing.
26:25 - Tuck: We don't want the terminology to drift too much but on the other hand design patterns is a lot about the intent. We can get by confusing Observer and Mediator often when the intent is the same, but it comes down to a weird balance between "do you want a frozen language or an evolving one?"
27:40 - Powers: One thing that I have been doing is calling anything that pulls three or more objects together a "Service" when I think that would be called a Facade (not a Laravel Facade), but does it seem like it's more important to get the context of what your object should be called right...instead of the actual name of the pattern it fits?
Ferrara: The names matter, but not so much that we need to hash everything out...does the system perform well, does it abstract what it's supposed to abstract? If the answer to those questions is "yes", then who cares what it's named.
Wathan: I think it is a good idea to avoid letting pattern names leak into the names of the objects you are making anyway. Sort of like a "ProductRepository" versus calling it a "Catalog," often there is a better name than the pattern name.
35:00 - Ferrara: Alan Kay - the inventor of Smalltalk and arguably of Object Oriented Programming as a whole, said he thinks that everyone in the industry gets the wrong thing from OOP, they focus on the object, and it's not about the objects, it's about what's between the objects, it's about the messaging and the decoupling. And that's what the entire point was when he created back then.
36:25 - Powers: can you (all) provide a little more on what you mean when you say "the communication between the objects?"
Ferrara: if you look at a method, not as a piece of code you dispatch, if you look at the method as communication, that I am communicating I am making a remote procedure call, named "this", that has "this" payload...then it's up to that object to figure out what it wants to do and it's going to send me a message back. You could look at it like a procedural or functional view, that code is defined on an interface somewhere, that gets proxied back to a virtual method which then gets executed because this object declares that method. BUT that misses A LOT of dynamicism in the language, then when you talk about things like duck-typing, you can't duck-type in that world. In the world you look at OOP as interfaces and classes, duck-typing makes zero sense, you mean you're just going to call a method on an object...that's nonsensical. How do you know that method is going to exist? With message passing, it doesn't matter, you say to the object "here's a message, you figure out what to do with it because I don't care."
Wathan: The difference between calling a function on an object versus sending a message to an object are very fundamentally different things to me. The fundamental difference is about how much knowledge the caller has about the receiver, if you're calling a method on an object you know the method exists. This is why I sometimes "sort of" rant against type-hinting in PHP, because you are specifying "I need an object that provides this interface because I know better than that object does about it's ability to respond to the message I want to send it." You have a function calling a method on another object...it can "see" into the other object, it can see every method available on that object and it chooses which one to call. In a more message-y kind of world, sort of how Ruby and Smalltalk are designed, it's like every object has the same API, it's just one method called "send," it takes the name of the message and the parameters to that message. You have no way of knowing if an object is compatible with the message that you are sending, it's up to the receiver to decide if it's compatible not up to the caller. This ties into one of these principles that Alan Kay thought was really important in OOP "as late binding as humanly possible all the time and pushing responsibility away all the time as far as possible." That's why I feel like using things like "method missing" in Ruby or "__call" in PHP, are very object oriented ideas, because you are putting the responsibility on that object to decide what to do with that message and you're making it explicit that that object is deciding what to do with that message versus the caller. Something I think would be cool is sort of how "__call" works but if you could intercept it before they get sent to the actual method. To me that's what object oriented thinking is about.
55:45 - Are there any patterns that you feel you implement frequently across many contexts?
Wathan: The most common is probably Adapter and Decorator.
Ferrara: Those were the same two I was going to say in the same order with the exact same justification.
Tuck: I've been using Specification objects a lot. Which if you dive into it, I see it more of a fancy form of Builder.
57:00 - what advice do you have for people that feel like they can't right a basic CRUD app without getting completely paralyzed by trying to make sure "I'm doing it right?"
Ferrara: Don't be afraid of failure. Failure is a good thing. Getting it wrong is a good thing. You could do this for 20,000 hours, for 10 years, and if you've never failed and done it wrong...you haven't learned jack...zero.
Wathan: If you're writing your tests first, everything fails anyway right? Get that failure out of the way right up front.
Wathan: But try not to worry about the big picture, don't let that distract you. Try to worry about the exact problem you have to solve in front of you right now. And just try and solve it in a way where you are happy with the solution.
Tuck: Be vulnerable...there's a difference between being afraid and being vulnerable. Don't read Gang of Four...it's a terrible book as an intro to design patterns. Pick up "Head first Design Patterns" or there's a really good design pattern book in Ruby.
Ferrara: Do not open a book, do not open Google, turn off your internet connection and try to write some code. Try and Fail and try and fail and fail and just keep going until you start to understand.
Thank you, Cees-Jan Kiewiet for all your contributions to ReactPHP & open source. A $50 Amazon gift card from Laracasts is on its way to you.
Thank you Andy Huggins for authoring the show notes for this episode!
If you'd like to contribute show notes and totally get credit for it, check out the show-notes repo!