iteration cover image

iteration

Latest episodes

undefined
Feb 4, 2019 • 38min

Modules! Modules! Modules!

Metz, Sandi. Practical Object-Oriented Design in Ruby Chapter 7. Sharing Role Behavior with Modules Welcome to iteration A weekly podcast about programming, development, and design through the lens of amazing books, chapter-by-chapter. Modules! Modules! Modules! Last episode we talked about inheritance which was kind of an extension of duck typing… but sometimes we need to combine the qualities of two existing subclasses, something that inheritance cannot readily accommodate. Many object-oriented languages provide a way to define a named group of methods that are independent of class and can be mixed in to any object. In Ruby, these mix-ins are called modules. Discovering the Schedulable Duck Type The Schedule expects its target to behave like something that understands lead_days, that is, like something that is “schedulable.” You have discovered a duck type. This specific example illustrates the general idea that objects should manage themselves; they should contain their own behavior. Mountain Bike? Mechanic? Now, in the code above, the dependency on Schedule has been removed from Bicycle and moved into the Schedulable module, isolating it even further. Like Using Inheritance COMES BACK TO AUTOMATIC MESSAGE DELEGATION Loggable Example When Bicycle includes Schedulable, all of the methods defined in the module become part of Bicycle’s response set. When a single class includes several different modules, the modules are placed in the method lookup path in reverse order of module inclusion. Thus, the methods of the last included module are encountered first in the lookup path. When a single class includes several different modules, the modules are placed in the method lookup path in reverse order of module inclusion. Thus, the methods of the last included module are encountered first in the lookup path. Picks: John: Type to Siri - Accessibility > Siri > Type to Siri JP: Scrimba - https://scrimba.com/playlist/pKwrCg
undefined
Jan 28, 2019 • 44min

Inheritance At Its Core

Acquiring Behavior Through Inheritance A weekly podcast about programming, development, and design through the lens of amazing books, chapter-by-chapter. Sani Metz - Object-Oriented Design in Ruby “Inheritance is, at its core, a mechanism for automatic message delegation. It defines a forwarding path for not-understood messages.” Where to Use Inheritance Objects that share a common parent - The objects that you are modeling must truly have a generalization-specialization relationship. Bicycle Touring Company - FastFeet Bikes - Mountain and Road Bikes Bikes have an overall size, a handlebar tape color, a tire size, and a chain type. Only mountain bikes have shocks Only Road bikes have handlebar tape Create a new empty Bicycle Class Let RoadBike > Bicycle And MountainBike > Bicycle When in doubt put less code in the parent, it’s easier to promote code later when you need a shared code. “The general rule for refactoring into a new inheritance hierarchy is to arrange code so that you can promote abstractions rather than demote concretions.” A superclass may have many subclasses, but each subclass is permitted only one superclass. This family tree image is, however, a bit misleading. In many parts of the biological world, it’s common for descendants to have two ancestors. It’s really useful to rails a NotImplementedError in the parent class in the methods that are required from their children, for example, default tire_size - “Creating code that fails with reasonable error messages takes minor effort in the present but provides value forever.” Initialize > Post Initialize to append any or overrode attributes from the parent initialize method. Initialize + post_initialize Closes with a mic drop - Initializing a new RecumbentBike is so DRY and painless! “Inheritance solves the problem of related types that share a great deal of common behavior but differ across some dimension.” “The best way to create an abstract superclass is by pushing code up from concrete subclasses.” “When your problem is one of needing numerous specializations of a stable, common abstraction, inheritance can be an extremely low-cost solution.” PICKS JP: Machine Learning with JavaScript John: Refactoring UI by Adam Wathan and Steve Schoger - Hard Wired Internet - Ran a Cat 5 Cable from my router - SO WORTH IT.
undefined
Jan 21, 2019 • 34min

Ducks! Ducks! Ducks!

Practical Object-Oriented Design in Ruby A weekly podcast about development and design through the lens of amazing books, chapter-by-chapter. Duck types are public interfaces that are not tied to any specific class. Objects that are defined by their behavior than by their class name. if an object quacks like a duck and walks like a duck, then its class is immaterial, it’s a duck. In a way, the “+” operator is a “duck type” - because it responds differently to a different type of objects. Preparer TripCoordinator, Mechanic, Driver can all be a preparer - they all respond to the prepare method in their own ways. At a higher level, what are these all doing? They are preparing something - we can trust within this shared interface. Within mechanic - we only call prepare_trip vs gas_up or full_water_tank- Instead of switching on type You Talk through this Object: Notification Object? https://github.com/withbetterco/wellstart/blob/master/app/models/notification.rb#L156 It has this insane method checking to see if the user’s notification settings match the notification that’s trying to send We are wanting to extend the notification object, for another type - Push Notifications Object (message, comment, like) User NotificationSettings Notifier Notification Policy “The ability to tolerate ambiguity about the class of an object is the hallmark of a confident designer.” Recognizing a duck: “You can replace the following with ducks: • Case statements that switch on class • kind_of? and is_a? • responds_to?” Polymorphic Objects Went from a ParticipantNote, and ProgramNote, and ApplicantNote, to one single Note object through a Notable Module. They all have “ has _many: notes, as:: notable” instead of each having “program_notes”, applicant_notes, etc. “you are missing an object, one whose public interface you have not yet discovered.” Dot files - .zshrc - Custom prompt - with my git status - Slippers from JP - Quadie
undefined
Jan 14, 2019 • 38min

Get Flexible

Chapter 4: Creating Flexible Interfaces At an object-oriented level, applications are made up of classes but defined by messages The design is concerned with messages that pass between objects This conversation between objects takes places using interfaces Defining Interfaces Imagine a restaurant kitchen. Customers order food off a menu. The menu is a kind of public interface. Within the kitchen, messages get passed and the way a meal is prepared (implementation details) are private to the customer. Classes are like kitchens. The class exists to fulfill a single responsibility but implements many methods Public Interfaces reveal primary responsibility expected to be invoked by others will not change on a whim safe for other to depend on thoroughly documented in the tests Private Interfaces handle implementation details not expected to be sent by other objects can change for any reason whatsoever unsafe for others to depend on may not even be referenced in tests The public interface is a contract that articulates the responsibilities of your class Example App: Bicycle Touring Company FastFeet Inc. is a company that offers road and mountain bike trips They use a paper system Routes that are offered may occur several times during the year Each Route has limitations on the number of customers who may go and requires a specific number of guides who double as mechanics Each Route is rated according to its aerobic difficulty. Mountain bike trips have an additional rating that reflects technical difficulty. Customers have an aerobic fitness level and a mountain bike technical skill level to determine if a trip is right for them Customers may rent bikes or they can bring their own FastFeet Inc has a few bikes available and also shares in a pool of bike rentals from local shops Rental bikes come in various sizes and are suitable for either road or mountain bike trips Use case A customer, in order to choose a trip, would like to see a list of available trips of appropriate difficulty, on a specific date, where rental bikes are available Constructing an Intention WHEW - where do we even start with this brand new application? A lot of requirements here. It's important to start DESIGNING your application. Don't just dive in and start writing code! PLAN The first thing you should do is form an intention about the objects and messages needed to satisfy the use case Sequence diagrams can be helpful for planning because the design conversation has shifted Instead of thinking about what classes should exist and what responsibilities they should have, we are NOW deciding what messages should be sent, where to send them, and who should receive them You don't send messages because you have objects, you have objects because you send messages Asking for "What" Instead of Telling "How" The distinction between a message that asks for what a sender wants and a message that tells the receiver how to behave may seem subtle but the consequences are significant Let's say we have two objects that need to communicate: trip and mechanic We could have the trip send messages to the mechanic in order to prepare a bicycle: clean bicycle pump tires lube chain check breaks The problem with this is that we're telling the mechanic HOW to prepare the bike. We should be telling the mechanic WHAT to do and leave the implementation details up to the mechanic. Instead, the trip should just send the message: prepare bicycle Doing this gives the responsibility of preparing a bicycle to the mechanic entirely. We trust the mechanic to accomplish this task. We tell what and not how Seeking Context Independence The best possible situation is for an object to be completely independent of its context. An object that could collaborate with others without knowing who they are or what they do could be reused in unanticipated and novel ways We could do this with dependency injection Using Messages to Discover Objects Recall the use case: A customer, in order to choose a trip, would like to see a list of available trips of appropriate difficulty, on a specific date, where rental bicycles are available This application needs an object to embody the rules at the intersection of Customer, Trip, and Bicycle We have an unknown object; we just know we need some object to receive our message What if we defined a TripFinder class that is responsible for finding suitable trips It contains all the knowledge of what makes a trip suitable it knows the roles its job is to do whatever is necessary to respond to this message Writing Code That Puts Its Best (Inter)Face Forward Methods in the public interface should: be explicitly defined as such be more about what than how have names that will not change take a hash as an options parameter Ruby provides three relevant keywords: public, protected, and private These keywords indicate which methods are stable and unstable. It also indicates how visible a method is to other parts of your application JP’s: HackingWithSwift John’s Pick: MacStadium.com -
undefined
Jan 7, 2019 • 47min

Managing Dependencies

Chapter 3: Managing Dependencies To collaborate, an object must know something about others. Knowing creates a dependency. If not managed carefully, these dependencies will strangle your application Recognizing Dependencies An object has a dependency when it knows: The name of another class. Gear expects a class named Wheel to exist The name of a message that it intends to send to someone other than self. Gear expects a Wheel instance to respond to diameter The arguments that a message requires. Gear knows that Wheel.new requires a rim and a tire The order of those arguments. Gear knows the first argument to Wheel.new should be rim and the second should be tire Writing Loosely Coupled Code Inject Dependencies see 1_inject_dependencies.rb Referring to a class by its name inside of another class is bad. If the name of Wheel class changes, the gear_inches method must also change The bigger problem is that gear_inches is explicitly saying that it is only willing to calculate gear inches for instances of Wheel Gear will only collaborate with any other kind of object even if that object has a diameter and uses gears! It's is not the class of the object that's important, it's the message you plan to send to it. Gear needs access to an object that can respond to diameter - a duck type We can use a technique called dependency injection to move the creation of a new Wheel instance outside of the class Isolate Dependencies see 2_isolate_dependencies.rb Isolate Instance Creation Sometimes you can't break all unnecessary dependencies, but you can isolate them The first technique moves Wheel.new from gear_inches and into Gear's initialize method The next alternative isolates the creation of a Wheel into its own wheel method Isolate Vulnerable External Messages gear_inches depends on Gear responding to wheel and wheel responding to diameter by creating a different diameter method to hold wheel.diameter, we remove the dependency within gear_inches Remove Argument-Order Dependencies see 3_remove_arg_orer_dependencies.rb Use Hashes for Initialization Arguments arguments of our initialize method must be passed in the correct order. we can pass an object instead to remove this dependency Explicitly Define Defaults we can use the fetch method to set defaults when using hashes in our initialize method fetch expects the key you're fetching to be in the hash and supplies several options for handling missing keys fetch will only set the default if the key is not found in the hash Managing Dependency Direction All examples thus far have shown Gear depending on Wheel or diameter - but the code could have easily been written so that Wheel depends on Gear or ratio Choosing Dependency Direction Depend on things that change less often John’s pick - Pick Krisp -https://krisp.ai
undefined
Dec 10, 2018 • 39min

Single Responsibility

Chapter 2: Designing Classes with a Single Responsibility The foundation of an object-oriented system is the message, but the most visible organizational structure is the class Questions to ask yourself: What are your classes? How many should you have? What behavior will they implement? How much do they know about other classes? How much of themselves should they expose? Creating Classes That Have a Single Responsibility A class should do the smallest possible useful thing; that is, it should have a single responsibility An Example Application: Bicycles and Gears Let's take a look at bikes. Consider the types of gears that bikes use Small Gears easy to pedal, not as fast takes many pedals just to make the tires rotate once can help you creep along steep hills Large Gears harder to pedal, fast sends you flying down those steep hills one pedal rotation with your foot might cause the tires to rotate multiple times Let's start with a small script and then extrapolate classes out of it: Large Gear chainring = 52 cog = 11 ratio = chainring / cog.to_f puts 'Large Gear:'\ "\n#{chainring}-tooth chainring"\ "\n#{cog}-tooth cog"\ "\n#{ratio.round(2)} rotations" Small Gear chainring = 30 cog = 27 ratio = chainring / cog.to_f puts "\nSmall Gear:"\ "\n#{chainring}-tooth chainring"\ "\n#{cog}-tooth cog"\ "\n#{ratio.round(2)} rotations" Since we're talking about gears, it only makes sense that we start by creating a Gear class based on the behavior above see 1_gear.rb Our Gear class has three methods: chainring, cog, and ratio Gear is a subclass of Object and thus inherits many other methods besides the three that we defined What I'm trying to say is that the complete set of behavior / the total set of messages to which it can respond is fairly large This is great and all - but what if we want to extend the functionality by taking into account the effect of the difference in wheels Bigger wheels travel much farther during each wheel rotation versus smaller wheels Consider this formula gear inches = wheel diameter × gear ratio (where) wheel diameter = rim diameter + (2 × tire diameter) see 2_gear.rb This new code is great except our old call to Gear.new(52, 11) no longer works because we added 2 more arguments to our initialize method Why Single Responsibility matters Applications that are easy to change consist of classes that are easy to reuse. [...] A class that has more than one responsibility are difficult to reuse Determining If a Class Has a Single Responsibility How can you tell if your class is only doing a single thing? Try describing what it does in a single sentence. You'll find out very quickly Remember that a class should do the smallest possible useful thing When we look at our Gear class - perhaps it is doing too much We are calculating gear_inches, which is fine - but calculating the tire size seems a little weird When to Make Design Decisions When we look at the Gear class, there's something off about having rim and tire in there. Right now the code in Gear is transparent and reasonable - this doesn't mean that we have great design. All it means is that we have no dependencies Right now, Gear lies about its responsibilities as it has multiple responsibilities in that it has to do "wheel" calculations in our gear_inches message Write Code That Embraces Change Here are some techniques that help you write code that embraces change Depend on Behavior, Not Data Behavior is captured in methods and invoked by sending messages Objects also contain data (not just behavior) Hide Instance Variables Always wrap instance variables in accessor methods instead of directly referring to variables, like the ratio method does. We can do this by using an attr_reader BAD def ratio @chainring / @cog.to_f end GOOD def ratio chainring / cog.to_f end If your instance variable is referred to multiple times and it suddenly needs to change, you're in for a world of hurt. Your method that wraps your instance variable becomes the single source of truth One drawback is that because you can wrap any instance variables in methods, its possible to obfuscate the distinction between data and objects But the point is that you should be hiding data from yourself. Hiding data from yourself protects code from unexpected changes Hide Data Structures Depending on a complicated data structure can also lead to a world of hurt For instance, if you create a method that expects the data structure is being passed to it to be an array of arrays with two items in each array - you create a dependency see 3_obscuring_references.rb Ruby makes it easy to separate structure from meaning You can use a Ruby Struct class to wrap a structure see 4_revealing_references.rb the diameters method now has no knowledge of the internal structure of the array diameters just know that it has to respond to rim and tire and nothing about the data structure Knowledge of the incoming array is encapsulated in our wheelify method Enforce Single Responsibility Everywhere Extra Extra Responsibilities from Methods def diameters wheels.collect { |wheel| wheel.rim + (wheel.tire * 2) } end this method clearly has two responsibilities iterate over wheels calculate the diameter of each wheel we can separate these into two methods that each have their own responsibility def diameters wheels.collect { |wheel| diameter(wheel) } end def diameter(wheel) wheel.rim + (wheel.tire * 2) end separating iteration from the action that's being performed on each element is a common case of multiple responsibilities Finally, the Real Wheel New feature request: program should calculate bicycle wheel circumference Now we can separate a Wheel class from our Gear class see 5_gear_and_wheel.rb
undefined
Dec 3, 2018 • 38min

New Book: Practical Object Oriented Design

Discover the fascinating world of object-oriented design with insights from Sandi Metz's book. Explore the challenges of technology scaling as major companies shift from React Native to native solutions. Learn the significance of maintainable code and the balance between rapid feature delivery and thoughtful design. The hosts emphasize the importance of classic programming literature and the evolving insights that come with revisiting these texts. Plus, dive into practical tips for mastering JavaScript testing to enhance your development skills!
undefined
Nov 2, 2018 • 40min

Alternatives to Exceptions

Alternatives to Exceptions Multiple return values in failures can be helpful - Represent a process as an object “Download Provisionment” - This is a really interesting idea…. “MessageSend.new” - would actually handle a lot of the issues in the SMS interface concept we’ve been talking through. “It’s tempting to raise an exception every time you get an answer you don’t like. But exception handling interrupts and obscures the flow of your business logic. Consider carefully whether a situation is truly unusual before raising an Exception.” Diagnostics- don’t fail SO aggressively early that you don’t capture the diagnostics. Examples of when to not "fail fast": test suites Sometimes you just need a way to proceed through steps and have the system tell you what parts succeeded and what parts failed Sideband data When communication failures without resorting to exceptions, we need a sideband: a secondary channel of communication for reporting meta-information about the status and disposition of a process. Multiple return values hehe, this reminds me of stuff I do in javascript ruby supports multiple return values in the form of array splatting. in JS, you could do this with destructuring def foo result = 42 success = true [result, success] end result, success = foo puts "#{success ? 'succeeded' : 'failed'}; result #{result}" Or you can use an open struct def foo OpenStruct.new(:result => 42, :status => :success) end Output parameters Caller-supplied fallback strategy if we're not sure we want to terminate the execution of a long process by raising an exception, we can inject a failure policy into the process def make_user_accounts(host, failure_policy=method(:raise)) # ... rescue => error failure_policy.call(error) end def provision_host(host, failure_policy) make_user_accounts(host, failure_policy) end policy = lambda { |e| puts e.message } provision_host("192.168.1.123", policy) Picks JP: https://github.com/xotahal/react-native-motion https://github.com/fram-x/FluidTransitions https://github.com/callstack/react-native-testing-library John: Slack Video Calling + Collaboration
undefined
Oct 26, 2018 • 48min

Designing for Failures

Designing for Failures Failure flags and benign values Sometimes responding with a nil is good enough, i.e. def save # some failing code rescue nil end Related to this is the concept of "benign values" The system might replace the erroneous value with a phony value that it knows to have a benign effect on the rest of the system When the system's success doesn't depend on the outcome of the method in question, using a benign value may be the right choice. Benign values are also helpful in making the code more testable. Example: begin response = HTTP.get_response(url) JSON.parse(response.body) rescue Net::HTTPError { 'stock_quote' => '' } end Instead of 'puts'ing, we can usewarn` Warning as errors Check out this hack: module Kernel def warn(message) raise message end end warn 'uh oh' Remote failure reporting At OL we use Bugsnag Idea of bulkheads -> a wall beyond which failures cannot have an effect on other parts of the system you should put bulkheads between external services and processes Circuit breaker pattern Ending the program Calling exit ends the whole program Remember that time I used exit in the Whiz Tutor codebase? Picks John: User Onboard - https://www.useronboard.com/ by https://twitter.com/samuelhulick  JP: singlediv.com - https://twitter.com/samuelhulick
undefined
Oct 19, 2018 • 50min

Exceptional Failure

Exceptional Failure In this short season, we are going through EXceptional Ruby by Advi Grimm What is a failure? Let's talk about some definitions first: Exception - the occurrence of an abnormal condition during the execution of a software element. Failure - the inability of a software element to satisfy its purpose Error - the presence in the software of some element not satisfying its specification "Failures cause exceptions which are due to errors" Failures - methods are thought to be designed to fulfill a contract. when they don't fulfill the contract, they fail The life-cycle of an exception Exceptions are raised with either the raise or fail methods In Ruby, we signal failures with exceptions 🗣 Discussion: When do you find yourself using raise? 🗣 Discussion: When do you find yourself using rescue? The rescue clause should be a short sequence of simple instructions designed to bring the object back to a stable state and to either retry the operation or terminate with a failure syntax Starts with the rescue keyword, followed by one or more classes or modules to match, then a hash rocket and the variable name to which the matching exception will be assigned Subsequent lines of code up to an end keyword will be executed if the rescue is matched rescue IOError => e puts "Error while writing to file: #{e.message}" end Moar Code begin raise RuntimeError, 'specific error' rescue StandardError => error puts "Generic error handler: #{error.inspect}" rescue RuntimeError => error puts "runtime error handler: #{error.inspect}" end Order matters when stacking multiple rescue clauses. the RuntimeError rescue block will never execute! 🗣 Discussion: Have you ever used retry? tries = 0 begin tries += 1 puts "trying #{tries}" raise rescue retry if tries < 3 puts 'I give up' end Ruby gives you the power to retry I can see how this might be useful for making API calls Be super careful that your 'giving up' criteria will eventually be met Retry is nice for things that are unreliable Picks: JP: Overcast https://overcast.fm/ John: Apple Refurbished

Get the Snipd
podcast app

Unlock the knowledge in podcasts with the podcast player of the future.
App store bannerPlay store banner

AI-powered
podcast player

Listen to all your favourite podcasts with AI-powered features

Discover
highlights

Listen to the best highlights from the podcasts you love and dive into the full episode

Save any
moment

Hear something you like? Tap your headphones to save it with AI-generated key takeaways

Share
& Export

Send highlights to Twitter, WhatsApp or export them to Notion, Readwise & more

AI-powered
podcast player

Listen to all your favourite podcasts with AI-powered features

Discover
highlights

Listen to the best highlights from the podcasts you love and dive into the full episode