Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection"...

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

Transcript of Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection"...

Page 1: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

Dependency Injection (ESaaS §11.6)!

© 2013 Armando Fox & David Patterson, all rights reserved

Page 2: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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 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 - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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_vips!Independent of how VIPs are represented

in model!

ActionView

User

User.where(…)

ActionView

@vips

Controller

User model

@vips

Page 4: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

Injecting Dependencies with the Adapter pattern"

•  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, ..."ActiveRecord::Base

connection()

AbstractAdapter

connection()

MySQLAdapter

connection() mysql_connection() MySQL"

Delegation" Overriding"

Page 5: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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, ..."

* Disclaimer: logos © or ™ their respective holders. This example is for education only and doesn’t imply endorsement, etc.—duh.

Customer

@email_list

EmailList

opt_in() opt_out()

update_email()

MailchimpList

subscribe() unsubscribe() update_member()

ConstantContactList

??

Page 6: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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"

MailchimpList

subscribe() unsubscribe() update_member()

Connection mgt."

Member mgt."

List creation"

Campaign mgt."

Report generator"

List exporter"

Page 7: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

only (a) ☐

only (b) ☐

both (a) and (b) ☐

neither (a) nor (b) ☐

7"

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.

Page 8: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

More Adapter-Like Patterns(ESaaS §11.6)!

© 2013 Armando Fox & David Patterson, all rights reserved

Page 9: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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

subscribe() unsubscribe() update_member()

FakeEmailList

opt_in() opt_out()

update_email()

Page 10: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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 11: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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)"

Page 12: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

Null object ☐

Proxy ☐

Adapter ☐

Façade ☐

12"

The use of FakeWeb to stub external SOA requests in testing is an example of which design pattern?"

Page 13: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

Demeter Principle (ESaaS §11.7)!

© 2013 Armando Fox & David Patterson, all rights reserved

Page 14: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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"– Separate traversal from computation (Visitor)"– Be aware of important events without knowing

implementation details (Observer) "

http://pastebin.com/NRSkHstN

Page 15: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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 16: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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

Page 17: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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

Yes...but probably reasonable to just expose object graph in the view in this case

17"

Suppose Order belongs to Customer, and view has @order.customer.name …is this a Demeter violation?

Page 18: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

Dealing With Collections:Composite!Armando Fox"

© 2013 Armando Fox & David Patterson, all rights reserved

Page 19: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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 20: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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 21: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

Simplified Implementation"class RegularTicket < Ticket attr_accessor :price, :show_date def add_to_order ... end def refund ... end end

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 22: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

Simplified Implementation"class RegularTicket < Ticket attr_accessor :price, :show_date def add_to_order ... end def refund ... end end

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) end end

class Customer has_many :tickets ... self.tickets << new_ticket end

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

Page 23: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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

Page 24: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

Plan-And-Document Perspective on Design Patterns

(Engineering Software as a Service §11.8)!David Patterson"

24"© 2013 Armando Fox & David Patterson, all rights reserved

Page 25: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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"

Page 26: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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"

Page 27: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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"

Page 28: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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"

Page 29: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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"

Page 30: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

Agile does not have an architecture phase, so there greater risk of ending up with poor SW architecture Agile developers may plan for SW architectures & use design patterns they expect will need (based on previous, similar projects) None are false; all are true

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

30"

Which statement regarding design patterns is FALSE?

1.

2.

3.

4.

Page 31: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

Design Patterns & SOLID wrapup

(ESaaS §11.8-11.10)!Armando Fox"

© 2013 Armando Fox & David Patterson, all rights reserved

Page 32: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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 33: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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 34: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

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 35: Dependency Injection - WordPress.com · 10.01.2014 · Dependency Inversion & Dependency Injection" • Problem: a depends on b, but b interface & implementation can change, even

Dependency Injection

Liskov Substitution

Open/Closed Principle ☐

35"

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:

Demeter Principle