40
Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

Embed Size (px)

Citation preview

Page 1: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

Dependency Injection(ESaaS §11.6)

© 2013 Armando Fox & David Patterson, all rights reserved

Page 2: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

Dependency Inversion& Dependency Injection

• Problem: a depends on b, but b interface & implementation can change, even if functionality stable

• Solution: “inject” an abstract interface that a & b depend on– If not exact match, Adapter/Façade– “inversion”: now b (and a) depend on

interface, vs. a depending on b• Ruby equivalent: Extract a Module

to isolate the interface

SessionStore

Database

read_from_db()store_in_db()

SessionMgr

get_session()store_session()

«interface»SessionStore

Database

Page 3: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

DIP in Rails: Example

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

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

• Happiness:# in controller@vips = User.find_vipsIndependent of how VIPs are represented

in model!

ActionView

User

User.where(…)

ActionView

@vips

Controller

User model

@vips

Page 4: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

Injecting Dependencies with the Adapter Pattern

• Problem: client wants to use a “service”...– service generally supports desired operations– but the APIs 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, ...

ActiveRecord::Base

connection()

AbstractAdapter

connection()

MySQLAdapter

connection()mysql_connection()MySQL

Delegation Overriding

Page 5: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

Example: Supporting External Services

• Suppose RottenPotatoes adds email marketing – send discount emails to moviegoers

• Use external service MailerMonkey

• Suppose very popular, you want to extend to send emails to Amiko friends

• Suppose Amiko exposes a different API– Adaptor:

http://pastebin.com/ZdhcYb7w

http://pastebin.com/8PHBpm5k

http://pastebin.com/Eimsw8ZF

Page 6: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

Related: Façade

• In fact, we only use a subset of much more elaborate API– Initialization, list management, start/stop

campaign...• So our adapter is also a façade

– may unify distinct underlying APIs into a single, simplified API

MailchimpList

subscribe()unsubscribe()

update_member()

Connection mgt.

Member mgt.

List creation

Campaign mgt.

Report generator

List exporter

Page 7: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

Related: Façade

Page 8: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

9

END

Page 9: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

More Adapter-Like Patterns(ESaaS §11.6)

© 2013 Armando Fox & David Patterson, all rights reserved

Page 10: Dependency Injection (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? # => [email protected]_name # => "ANONYMOUS"@customer.is_vip? # => false

EmailList

opt_in()opt_out()

update_email()

MailchimpList

subscribe()unsubscribe()

update_member()

FakeEmailList

opt_in()opt_out()

update_email()

Page 11: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

Example: Supporting External Services

• Suppose RottenPotatoes adds email marketing – send discount emails to moviegoers

• We want to disable email sending from time to time

http://pastebin.com/js6C67mJ

http://pastebin.com/avRQAgZc

Page 12: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

Related: Proxy

• Proxy implements same methods as “real” service object, but “intercepts” each call– do authentication/protect access– defer work (be lazy)– Rails example: r= @Movie.reviews

• Respond to all collection methods without querying the database, except when it has to

Page 13: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

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

Page 14: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

17

END

Page 15: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

Demeter Principle(ESaaS §11.7)

© 2013 Armando Fox & David Patterson, all rights reserved

Page 16: Dependency Injection (ESaaS §11.6) © 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:

– Replace method with delegate– Be aware of important events without knowing

implementation details (Observer) – Separate traversal from computation (Visitor)

http://pastebin.com/NRSkHstN

Page 17: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

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

Page 18: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

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 ... endend

# in config/environment.rbconfig.active_record.observers = :customer_observer

Page 19: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

Visitor

• When traversing data structure (DS), provide a callback method to execute for each DS member– Visit each element without knowing how the data

structure is organized

Page 20: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

25

END

Page 21: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

Dealing With Collections:Composite

© 2013 Armando Fox & David Patterson, all rights reserved

Page 22: Dependency Injection (ESaaS §11.6) © 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”

Page 23: Dependency Injection (ESaaS §11.6) © 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”

1. Common base class: what stays the same?

2. Individual object (may have

>1 of these)

3. Composite (aggregate) of objects

Page 24: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

Simplified Implementation

class RegularTicket < Ticket attr_accessor :price, :show_date def add_to_order ... end def refund ... endend

class MultiTicket < Ticket def initialize @tickets = [] super end attr_reader :tickets def add_tickets(t) @tickets += t end def price @tickets.sum { |t| t.price } end def add_to_order @tickets.each { |t| t.add_to_order } end

Ticket

price()add_to_order()

RegularTicket

show_date()

MultiTicket

add_tickets()tickets()

• Note, not all methods necessarily make sense for leaf vs. aggregate!

• In this implementation, subscriptions can nest; may or may not be desirable

VIPTicket

show_date()

Page 25: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

Simplified Implementation

class RegularTicket < Ticket attr_accessor :price, :show_date def add_to_order ... end def refund ... endend

class MultiTicket < Ticket def initialize @tickets = [] super end attr_reader :tickets def add_ticket(t) @tickets += t end def price @tickets.sum { |t| t.price } end def add_to_order @tickets.each { |t| t.add_to_order } end

class Subscription < MultiTicket def <<(subscription) add_tickets(subscription) endend

class Customer has_many :tickets ... self.tickets << new_ticketend

We can use Ruby’s open classes to allow “overloaded” methods like << to do the right thing as well:

Page 26: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

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::Baseclass RegularTicket < Ticketclass VIPTicket < Ticketclass Subscription < Ticket

Page 27: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

34

END

Page 28: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

Plan-And-Document Perspective on Design Patterns

(Engineering Software as a Service §11.8)

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

Page 29: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

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?

36

Page 30: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

P&D Approach to Design Patterns

• Careful planning can result in good SW architecture– P&D a.k.a. “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

37

Page 31: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

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

38

Page 32: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

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 to reasonable design decisions

39

Page 33: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

40

END

Page 34: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

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

43

Page 35: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

44

END

Page 36: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

Design Patterns & SOLID Wrap-up

(ESaaS §11.9-11.10)

© 2013 Armando Fox & David Patterson, all rights reserved

Page 37: Dependency Injection (ESaaS §11.6) © 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

Page 38: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

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

Page 39: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

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

Page 40: Dependency Injection (ESaaS §11.6) © 2013 Armando Fox & David Patterson, all rights reserved

49

END