Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Design Patterns in Ruby

Design Patterns in Ruby

A Synopsis of "Design Patterns in Ruby" by Russ Olsen and how design patterns are still useful in the Ruby environment

csaunders

June 13, 2012
Tweet

More Decks by csaunders

Other Decks in Programming

Transcript

  1. WTF? Wednesday, 13 June, 12 So... Why Design Patterns? They’re

    those things used by people in static languages like Java and C# right?
  2. Wednesday, 13 June, 12 Design patterns aren’t some shit that’s

    set in stone. They’re guidelines, patterns, perhaps a good idea because some poor bastards run into the same problem already.
  3. Wednesday, 13 June, 12 These patterns are more like the

    the cookbook of software development. Sure if I wanted to cook a chicken I could put it on a fire and eat a piece of it every once in a while to see if it’s good. Or I could open a book or the internet and get some guidelines on the right way. We want to make systems that are awesome and don’t break each time we want to introduce changes.
  4. Single Responsibility Wednesday, 13 June, 12 One of the SOLID

    principles. We want to reduce the cascading effect changes to modules can have on other modules within a system
  5. T X Y Z Wednesday, 13 June, 12 If I

    make a change to Module T, I shouldn’t have to make changes to any other modules except for perhaps Z. Any further than that, and well you’ve done goofed and didn’t adhere to the SRP.
  6. Interface Wednesday, 13 June, 12 Well wait... let’s scratch that.

    Interfaces sound to java/C# esque and bring in some programming ideas along with it.
  7. Protocol Wednesday, 13 June, 12 Instead program to a protocol.

    Here we have a set of functions we expect and we’ll *only* use those ones. That is our protocol. Therefore, any object given to us that follows that protocol can be used by us.
  8. Wednesday, 13 June, 12 For example, here we reverse a

    stack. The stack object could be whatever the hell it wants. As long as it responds to the push and pop function, our method doesn’t give a damn.
  9. Composition Wednesday, 13 June, 12 Prefer composition objects versus just

    having things that inherit from ancestors. Inheritance can easily lead to a violation of SRP and a change in a parent class can mess shit up for your classes. Composition allows us to keep our inheritance tree relatively shallow, which can drastically simplify our system.
  10. Delegate Wednesday, 13 June, 12 Finally, whenever possible delegate a

    job to something. Often our objects don’t need to know the fine details of how something works.
  11. Wednesday, 13 June, 12 For example, there’s too much going

    on in this function. The adventurer needs to know too many minute details about the target.
  12. Wednesday, 13 June, 12 Instead, what we can do is

    delegate as many of the functions off to others since the adventurer really doesn’t care about the details of how damage is dealt or how dice are rolled.
  13. Template Wednesday, 13 June, 12 A relatively simple pattern. Usually

    when we have objects we want them to do something. For example we could want objects that generate charts or have different adventurers who all attack differently.
  14. Wednesday, 13 June, 12 Here we could have a hero

    that does an attack, but what if we want to add a new hero to the mix? We could simply add some conditional statements, but that kinda sucks.
  15. Wednesday, 13 June, 12 Instead what we could do is

    simply have a function that we expect the subclasses to implement or override. Those optionally overridden functions are often known as stubs.
  16. Strategy Wednesday, 13 June, 12 While the template can work,

    it’s often not the right kind of solution. We don’t always want a different class just because our objects do slightly different things. This is where the strategy can come into play.
  17. Wednesday, 13 June, 12 A ruby-ish way of implenting the

    Strategy would be to allow a block to be passed in as the Strategy function vs, passing in an object that conforms to a certain protocol.
  18. Wednesday, 13 June, 12 Now we can easily create some

    objects that all have different kinds of attacks.
  19. Observer Wednesday, 13 June, 12 We’ll run into situations where

    other objects need to know about the changes that are going on to things.
  20. Wednesday, 13 June, 12 Since these changes are relevant to

    the objects interest, we can simply make them observers and any time changes happen (that we deem relevant) we inform our observers about it.
  21. Wednesday, 13 June, 12 Now, that we have an observable

    object we can add our observers. All the need to do is respond to the saw_flash function, and boom, they’ll know when changes happen to the observed object. To get an idea of where the Observer is used, we need look no further than Active Record. We are able to create Model Observers so we can be notified when a model’s changed so we can perform various actions based on however the content changed.
  22. Composite Wednesday, 13 June, 12 Usually we want to be

    able to represent a very complex workflow as simply as possible. Through the Composite we can figure out some basic components and build our system from that. GUI libraries are an example of how the composite pattern can be used. We have a window, which has sub components like buttons and text areas which can be arbitrarily deep.
  23. Command Wednesday, 13 June, 12 Another straightforward pattern is the

    Command. Basically, we want to tell an object to perform a specific task such as when a button is clicked or when some other kind of event has happened. As rails developers we've actually seen the command pattern in action any time we've wanted to make a change to the database.
  24. Wednesday, 13 June, 12 For Active Record, our Execute commands

    are the #up and #down methods. By providing these we've told the migration what to do when it's time comes.
  25. Adapter Wednesday, 13 June, 12 Often we'll run into an

    interface that we naturally cannot use. For example we could have an HDMI projector but are stuck with a Mini-Display port. Luckily, we have adapters that make it possible for our computers and display devices to exist in harmony. As with hardware, we often need something to make it possible for our existing system to easily plug into a new library or service. This is where our Adapter comes into play.
  26. Wednesday, 13 June, 12 I was going to show off

    some active merchant code, but there’s way too show off for a 1hr presentation. In essence though, I wanted to elaborate how we can use Adapters to take an unexpected API and turn it into something that fits more comfortably into our system. This is often the case when integrating with 3rd parties, and Active Merchant is a perfect example of how we can create order from a chaos.
  27. Proxy Wednesday, 13 June, 12 Sometimes we want to provide

    access to an object, but there are some conditions. This could be the actual data is remote or we could have some sort of permissions required before we actually give access to the object. This is the time where instead of providing the actual object we'd put a proxy in front of it. For all intents and purposes, the client doesn't really know the difference between a proxy and the real thing since the proxy behaves in a similar manner
  28. Wednesday, 13 June, 12 So let’s say we have a

    brewpub. They like to let people in, and they do. Unfortunately we have these laws that only permit 21 year olds into establishments of fine drinkery, but we don’t want these details to bleed into that of the BrewPub. They have more important things to worry about like making beer and serving people.
  29. Wednesday, 13 June, 12 Instead what we could do is

    have an proxy to the brewpub that enforces the age limit. We could achieve this as follows. But this sucks! If there’s anything we don’t care about we need to just pass it off to the actual brewpub instance. Luckily in ruby, there’s an easy way to do this.
  30. Wednesday, 13 June, 12 We can simply implement the method_missing

    function in ruby then just pass of anything we don’t care about off to the patron instead. This allows us to add new features to the patron object while keeping the code in our proxy stable, and only requiring changes when we need to change how the proxy works.
  31. Singleton Wednesday, 13 June, 12 The global variable, a single

    instance, the app delegate, whatever. This is the one object to rule them all. While they can be useful (such as storing settings for a desktop app or something) the temptation to add things that shouldn’t be there is ever so strong. And before you know it...
  32. Wednesday, 13 June, 12 Your singleton has turned into this

    horrendous glob that corrupts everything it touches with it’s plagued appendages.
  33. Decorator Wednesday, 13 June, 12 Similar to the Composite, the

    Decorator allows us to add new functionality to our objects at run time. The GoF example is a GUI window. Let’s say we wanted to add a horizontal slider to it. We could subclass window with a HorizontalSliderWindow, but what if we wanted to add a vertical slider too? Well now we’d need to have a VerticalHorizontalSliderWindow. As you can tell, things can get a little bit out of hand, and the system could become a bit of a nightmare to maintain.
  34. Wednesday, 13 June, 12 Let’s say we this LineWriter which

    does some pretty simple things. It takes in an array of strings and writes them out to the terminal. Simple enough.
  35. Wednesday, 13 June, 12 Now, let’s say we wanted to

    add the ability to add timestamps and line numbers to the output. We could’ve went the more traditional approach and had our writers take in a writer, but instead we can use the power of the Mixin to easily add the functionality we desire.
  36. Wednesday, 13 June, 12 We could’ve included these writers in

    the TextWriter class, but then that would’ve made it a hard inclusion. What if depending on certain conditions we want to use different writers? This is where we can simply call extend on our instance with the module we want to add to our object.
  37. Factory Wednesday, 13 June, 12 Often times we’ll run into

    the situation where we don’t completely know what objects we’ll be using at runtime. But we do know what protocol the objects we’re getting will conform to, but other than that we really don’t have any more information.
  38. Wednesday, 13 June, 12 Typically when we see an example

    of a Factory it’s in a style like this. While it does work, it’s not very rubyesque and there’s most definitely a better way we can implement the Factory pattern in Ruby
  39. Wednesday, 13 June, 12 Here’s a simple example of where

    we used it in Active Fulfillment and within Shopify. In this example, Base.service is the factory that constructs the classes for our consumers to use. While, it’s not pulling out fully instantiated objects, it’s doing almost all the work for actually getting the object we care about.
  40. Builder Wednesday, 13 June, 12 The final GoF pattern we’ll

    be talking about. Often when we want some easy way of setting up data we’ll use a builder. They provide a nice interface and a way of helping ensure that the resulting objects are in a sane state.
  41. Wednesday, 13 June, 12 An even better example of a

    builder is Nokogiri. It provides an interface that makes constructing XML extremely simple
  42. Wednesday, 13 June, 12 Alright, so now that GoF is

    out of the way, let’s get into the good parts. That is, some new patterns that are somewhat specific to ruby, but are also very prevalent in Ruby projects.
  43. Domain Specific Languages Wednesday, 13 June, 12 Sometimes we reach

    a point where we’d like an easier way to interact with our system. This could be for the sake of our end users, or it could be to simply our own lives. The Ruby world is rich with examples of DSLs that range from rendering HTML to testing and automation.
  44. Wednesday, 13 June, 12 Let’s return back to our Hero

    example. Now, let’s say we wanted to provide an easy way for a user to pass in their characters statistics and gear. Currently that would require they write a bunch of code, which isn’t necessarily friendly.
  45. Wednesday, 13 June, 12 What we could do instead is

    have this HeroSheet, which loads up a file or string and extracts that information into a hero object. We can also delegate anything that is hero specific off to the hero by simply implementing method_missing
  46. Wednesday, 13 June, 12 Now this is what our DSL

    would look like for the HeroSheet shown previously. Now, we've all seen plenty of DSLs in our every day work at Shopify. The rails routes file, our test suite, rake files and if you're working outside of Shopify core, RSPec. And this is just naming a few!
  47. Meta- Programming Wednesday, 13 June, 12 This is something I

    don't have a lot of experience with, but it's a pretty awesome concept. The basic idea behind metaprogramming is we can, at runtime, modify our objects and even classes. This could be things like adding new methods to an object or including modules. Rails relies heavily on meta-programming to provide a lot of it’s magic.
  48. Convention Over Configuration Wednesday, 13 June, 12 Configuration via XML,

    it's a nightmare. Often to get even the most basic system running, you'd need to configure. Things, shouldn't need to be this complicated and rails showed a way to have a very solid system that didn't need all this configuration. The premise is quite straight-forward, we have a system that makes some assumptions about where things will be or how they will be named. With this in place we can provide an easier to use interface which also allows for extensions to be easily added.
  49. Wednesday, 13 June, 12 Let's go back to that ActiveMerchant

    code we were looking at. Primarily that of ActiveMerchant::Fulfillment::Base. Adding new services is exceptionally simple and requires the least amount of change to the code base (simply add tests and the new service).
  50. Wednesday, 13 June, 12 Design patterns have a pretty bad

    reputation, but as we’ve seen, they are still quite relevant to other languages like Ruby. The difference is that several of these patterns we can pretty much get for free or for very little effort because of how Ruby has been designed. Also, because of how easy it is to dynamically change things in ruby, implementing the other patterns is typically quite simple.