<<

Dependency Injection (ESaaS §11.6)

© 2013 Armando Fox & David Patterson, all rights reserved Dependency Inversion & Dependency Injection

• Problem: a depends on b, but b SessionStore read_from_db() interface & implementation can store_in_db() change, even if functionality stable Database • Solution: “inject” an abstract interface that a & b depend on SessionMgr – If not exact match, Adapter/Façade get_session() – “inversion”: now b (and a) depend store_session()

on interface, vs. a depending on b «interface» SessionStore • Ruby equivalent: Extract Module to isolate the interface Database DIP in Rails: example

• What’s wrong with this in a view: ActionView - @vips = User.where('group="VIP"') User.where(…)

User • A little better: - @vips = User.find_vips ActionView

• Happiness: @vips # in controller @vips = User.find_vips Controller Independent of how VIPs are represented @vips in model! User model Injecting Dependencies with the • Problem: client wants to use a “service”... – service generally supports desired operations – but the API’s don’t match what client expects – and/or client must interoperate transparently with multiple slightly-different services • Rails example: database “adapters” for MySQL, Oracle, PostgreSQL, ... Delegation Overriding

ActiveRecord::Base AbstractAdapter connection() connection()

MySQLAdapter connection() MySQL mysql_connection() Example: Supporting External Services • I use external services for email marketing • Both have RESTful API’s • Similar features – Maintain multiple lists, add/remove user(s) from list(s), change user’s opt-in status, ... Customer EmailList @email_list opt_in() opt_out() update_email()

MailchimpList ConstantContactList subscribe() ?? unsubscribe() update_member() * Disclaimer: logos © or ™ their respective holders. This example is for education only and doesn’t imply endorsement, etc.—duh. Related: Façade

• In fact, we only use a subset of much more elaborate API’s – Initialization, list management, start/stop campaign... • So our adapter is also a façade – may unify distinct underlying API’s into a single, simplified API Connection mgt. Campaign mgt. MailchimpList subscribe() Member mgt. Report generator unsubscribe() update_member() List creation List exporter In RSpec controller tests, it's common to stub ActiveRecord::Base.where, an inherited method. Which statements are true of such tests: (a) The controller under test is tightly coupled to the model (b) In a static language, we'd have to use DI to achieve the same task in the testing framework.

☐ only (a)

☐ only (b)

☐ both (a) and (b)

☐ neither (a) nor (b)

7 More Adapter-Like Patterns (ESaaS §11.6)

© 2013 Armando Fox & David Patterson, all rights reserved Related: Null object

• Problem: want invariants to simplify design, but app requirements seem to break this • Null object: stand-in on which “important” methods can be called @customer = Customer.null_customer @customer.logged_in? # => false @customer.last_name # => "ANONYMOUS" @customer.is_vip? # => false EmailList opt_in() opt_out() update_email()

MailchimpList FakeEmailList subscribe() opt_in() unsubscribe() opt_out() update_member() update_email() Singleton: Ensuring there’s only one of something • Technically, a class that provides only 1 instance, which anyone can access • Ruby supports a very elegant way to do this, using singleton classes (no relation!) – A global $variable? others can reassign it – A CONSTANT? Can’t control when it’s initialized • The singleton object is in every respect a member of the base class, but immutable and singular • Singleton Example: http://pastebin.com/RBuvPMkR Related: Proxy

• Proxy implements same methods as “real” service object, but “intercepts” each call – do authentication/protect access – defer work (be lazy) – Rails example: association proxies (eg Movie.reviews) The use of FakeWeb to stub external SOA requests in testing is an example of which design pattern?

☐ Null object

☐ Proxy

☐ Adapter

☐ Façade

12 Demeter Principle (ESaaS §11.7)

© 2013 Armando Fox & David Patterson, all rights reserved Demeter Principle + Example

• Only talk to your friends...not strangers. • You can call methods on – yourself – your own instance variables, if applicable • But not on the results returned by them. • Solutions: http://pastebin.com/NRSkHstN – replace method with delegate – Separate traversal from computation (Visitor) – Be aware of important events without knowing implementation details (Observer) Observer

• Problem: entity O (“observer”) wants to know when certain things happen to entity S (“subject”) • Design issues – acting on events is O’s concern—don’t want to pollute S – any type of object could be an observer or subject— inheritance is awkward • Example use cases – full-text indexer wants to know about new post (e.g. eBay, Craigslist) – auditor wants to know whenever “sensitive” actions are performed by an admin Example: Maintaining Relational Integrity • Problem: delete a customer who “owns” previous transactions (i.e., foreign keys point to her) • My solution: merge with “the unknown customer” • ActiveRecord provides built-in hooks for Observer design pattern class CustomerObserver < ActiveRecord::Observer observe :customer # actually not needed (convention) def before_destroy ... end end

# in config/environment.rb config.active_record.observers = :customer_observer Suppose Order belongs to Customer, and view has @order.customer.name …is this a Demeter violation?

☐ Yes...but probably reasonable to just expose object graph in the view in this case ☐ Yes...replace with Order#customer_name which delegates to Customer#name ☐ Yes…you can make a case for either of the above ☐ No…by using belongs_to we’re already exposing info about the Customer anyway 17 Dealing With Collections: Composite

Armando Fox

© 2013 Armando Fox & David Patterson, all rights reserved Composite

• What: component whose operations make sense on both individuals & aggregates • Example: regular tickets, VIP tickets, subscriptions • What do they have in common? – Has a price – Can be added to an order • What’s different? – Regular & VIP Tickets are for specific show – Subscription has to track which tickets it “owns” Composite

• What: component whose operations make sense on both individuals & aggregates • Example: regular tickets, VIP tickets, subscriptions 1. Common base class: • What do they have in common? what stays the same? – Has a price – Can be added to an order 2. Individual • What’s different? object (may have – Regular & VIP Tickets>1 of these) are for specific show – Subscription has to track which tickets3. it Composite “owns” – (aggregate) of objects Simplified Implementation

class RegularTicket < Ticket Ticket attr_accessor :price, :show_date def add_to_order ... end price() add_to_order() def refund ... end end class MultiTicket < Ticket RegularTicket MultiTicket def initialize show_date() add_tickets() @tickets = [] tickets() super end attr_reader :tickets VIPTicket def add_tickets(t) show_date() @tickets += t end • Note, not all methods def price @tickets.sum { |t| t.price } necessarily make sense for end leaf vs. aggregate! def add_to_order • In this implementation, @tickets.each { |t| t.add_to_order } subscriptions can nest; may end or may not be desirable Simplified Implementation class RegularTicket < Ticket We can use Ruby’s open classes to attr_accessor :price, :show_date allow “overloaded” methods like << def add_to_order ... end to do the right thing as well: def refund ... end end class Subscription < MultiTicket class MultiTicket < Ticket def <<(subscription) def initialize add_tickets(subscription) @tickets = [] end super end end attr_reader :tickets class Customer def add_ticket(t) has_many :tickets @tickets += t ... end self.tickets << new_ticket def price end @tickets.sum { |t| t.price } end def add_to_order @tickets.each { |t| t.add_to_order } end Composite and Single-Table Inheritance • Rails Single-table inheritance (Google it) stores objects of different subclasses (but same parent class) in same table – Rails automatically manages a type column that denotes the Ruby object’s (sub)class – Can define separate validations, associations, etc. on each subclass • Can support Composite & some other patterns class Ticket < ActiveRecord::Base class RegularTicket < Ticket class VIPTicket < Ticket class Subscription < Ticket Plan-And-Document Perspective on (Engineering Software as a Service §11.8) David Patterson

© 2013 Armando Fox & David Patterson, all rights reserved 24 P&D Design Patterns?

• What are the Pros & Cons to Plan-and- Document from Design Pattern perspective? • In which is it easier to use Design Patterns: Plan-and-Document or Agile?

25 P&D Approach to Design Patterns • Careful planning can result in good SW architecture – P&D AKA “Big Design Up Front” • Break Software Requirements Specification (SRS) into problems • For each task, looks for design patterns that match – Then to patterns for subproblems • Design Reviews can help

26 Agile Approach to Design Patterns • Agile critique: encourages developers to start coding without any design – Rely too much on refactoring later • P&D critique: No code until design complete => No confidence design implementable, matches customer needs – When coding starts, design must change

27 How Much Design Up Front?

• Can plan for storage for SaaS app despite on 1st BDD/TDD tests don’t use database • Should think about horizontal scaling (Ch 12) early on • Agile Advice: If previously done project that has some design constraint or element, OK to plan for in similar project, as experience likely leads reasonable design decisions

28 Getting Started with Design Patterns • GoF distinguishes design patterns from frameworks – Patterns are more abstract, narrower in focus, not targeted to problem domain • Nevertheless, frameworks great way for novice to get started with design patterns – Gain experience on creating code based on design patterns by examining patterns in frameworks instantiated as code

29 Which statement regarding design patterns is FALSE?

1. P&D processes have an explicit design phase that is a natural fit to the use of design patterns and thus will have a good SW architecture 2. Agile does not have an architecture phase, so there greater risk of ending up with poor SW architecture 3. Agile developers may plan for SW architectures & use design patterns they expect will need (based on previous, similar projects) 4. None are false; all are true

30 Design Patterns & SOLID wrapup (ESaaS §11.8-11.10) Armando Fox

© 2013 Armando Fox & David Patterson, all rights reserved A Few Patterns Seen in Rails

• Adapter (database connection) • Abstract Factory (database connection) • Observer (caching—Chapter 12) • Proxy (AR association collections) • Singleton (Inflector) • Decorator (AR scopes, alias_method_chain) • Command (migrations) • Iterator (everywhere) • Duck typing simplifies expressing and “plumbing” most of these by “weakening” the relative coupling of inheritance SOLID Caveat

• Designed for statically typed languages, so some principles have more impact there – “avoid changes that modify type signature” (often implies contract change)—but Ruby doesn’t really use types – “avoid changes that require gratuitous recompiling”—but Ruby isn’t compiled • Use judgment: goal is deliver working & maintainable code quickly Summary

• Design patterns represent successful solutions to classes of problems – Reuse of design rather than code/classes – A few patterns “reified” in Rails since useful to SaaS • Can apply at many levels: architecture, design (GoF patterns), computation • Separate what changes from what stays the same – program to interface, not implementation – prefer composition over inheritance – delegate! – all 3 are made easier by duck typing • Much more to read & know—this is just an intro Rails’ ActiveRecord module defines an AbstractAdapter for connecting to databases. Subclasses of AbstractAdapter exist for each database type and can be added for new databases; when the app starts, the correct one is instantiated based on config/ database.yml. Which SOLID principle is NOT illustrated by this example: ☐ Open/Closed Principle

☐ Dependency Injection

☐ Demeter Principle

☐ Liskov Substitution

35