<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Pivotal Labs &#187; Jeff Dean</title>
	<atom:link href="http://pivotallabs.com/author/jdean/feed/" rel="self" type="application/rss+xml" />
	<link>http://pivotallabs.com</link>
	<description>Agility Developed</description>
	<lastBuildDate>Thu, 23 May 2013 19:42:15 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>DelegateClass rocks my world</title>
		<link>http://pivotallabs.com/delegateclass-rocks-my-world/</link>
		<comments>http://pivotallabs.com/delegateclass-rocks-my-world/#comments</comments>
		<pubDate>Thu, 21 Jan 2010 03:59:00 +0000</pubDate>
		<dc:creator>Jeff Dean</dc:creator>
				<category><![CDATA[Labs]]></category>

		<guid isPermaLink="false">http://pivotallabs.com/delegateclass-rocks-my-world/</guid>
		<description><![CDATA[<p><p>If you notice that your classes have more than one responsibility, you can easily split them up into multiple, more cohesive classes using Ruby's DelegateClass.  </p> <a href="http://pivotallabs.com/delegateclass-rocks-my-world/">Continue reading <span class="meta-nav">&#8594;</span></a></p><p>The post <a href="http://pivotallabs.com/delegateclass-rocks-my-world/">DelegateClass rocks my world</a> appeared first on <a href="http://pivotallabs.com">Pivotal Labs</a>.</p>]]></description>
				<content:encoded><![CDATA[<p>If you notice that your classes have more than one responsibility, you can easily split them up into multiple, more cohesive classes using Ruby&#8217;s DelegateClass.</p>
<p>Let&#8217;s say that you have a Person class, and that people in your system can sell things and/or publish articles. You can&#8217;t use subclasses, because a person can be an author and a seller at the same time. At first you might start with something like this:</p>
<pre><code>class Person &lt; ActiveRecord::Base
  has_many :articles
  has_many :comments, :through =&gt; :articles
  has_many :items
  has_many :transactions

  def is_seller?
    items.present?
  end

  def amount_owed
    # =&gt; some fancy math
  end

  def is_author?
    articles.present?
  end

  def can_post_article_to_homepage?
    # =&gt; some fancy permissions
  end
end
</code></pre>
<p>This might seem OK at first. You might say &#8220;Well, it&#8217;s the responsibility of Person to know about both the items they&#8217;ve sold, as well as the articles they&#8217;ve published.&#8221; I say that&#8217;s hogwash.</p>
<p>Imagine a new requirement: People can be buyers as well as sellers / authors</p>
<p>The way this is setup, you&#8217;d have to re-open the person class and add things like:</p>
<pre><code>class Person &lt; ActiveRecord::Base
  #  ...
  has_many :purchased_items
  has_many :purchased_transactions

  def is_buyer?
    purchased_items.present?
  end

  # ...
end
</code></pre>
<p>The first thing to notice is that this violates the open / closed principle (open for extension but closed to modification) because you&#8217;ve modified the class. Next, you&#8217;ll notice that naming can get very confusing in places where you&#8217;ve got a person who is on both sides of a transaction. Finally, this code has poor separation of concerns.</p>
<p>Imagine another new requirement: The Person class is now driven by an xml web service, or a non-ActiveRecord class</p>
<p>Now that you can&#8217;t use ActiveRecord and your has_many code doesn&#8217;t work, you have to rewrite all kinds of code, and feature development grinds to a halt.</p>
<h2>Enter DelegateClass</h2>
<p>Let&#8217;s say instead of modifying Person, you extended Person by creating delegate classes, like so:</p>
<pre><code>class Person &lt; ActiveRecord::Base
end

class Seller &lt; DelegateClass(Person)
  delegate :id, :to =&gt; :__getobj__

  def items
    Item.for_seller_id(id)
  end

  def transactions
    Transaction.for_seller_id(id)
  end

  def is_seller?
    items.present?
  end

  def amount_owed
    # =&gt; some fancy math
  end
end

class Author &lt; DelegateClass(Person)
  delegate :id, :to =&gt; :__getobj__

  def articles
    Article.for_author_id(id)
  end

  def comments
    Comment.for_author_id(id)
  end

  def is_author?
    articles.present?
  end

  def can_post_article_to_homepage?
    # =&gt; some fancy permissions
  end
end
</code></pre>
<p>The calls to this involve one extra step, so instead of:</p>
<pre><code>person = Person.find(1)
person.items
</code></pre>
<p>You add:</p>
<pre><code>person = Person.find(1)
seller = Seller.new(person)
seller.items
seller.first_name # =&gt; calls person.first_name
</code></pre>
<p>Now that this is in place, adding a Buyer is as simple as creating a Buyer delegate class like so:</p>
<pre><code>class Buyer &lt; DelegateClass(Person)
  delegate :id, :to =&gt; :__getobj__

  def items
    Item.for_buyer_id(id)
  end

  def is_buyer?
    purchased_items.present?
  end
end
</code></pre>
<p>Now when you need to make Person driven by something other than ActiveRecord::Base, your delegate classes <em>don&#8217;t change at all</em>.</p>
<p>Delegate classes aren&#8217;t the solution to every problem, and certain behavior, such as #reload can be very confusing at first:</p>
<pre><code>person = Person.find(1)
seller = Seller.new(person)
seller.class # =&gt; Seller
seller.reload.class # =&gt; Person
</code></pre>
<p>Another gotcha is that id doesn&#8217;t delegate by default, so you have to add the following line to make sure you get the ActiveRecord id:</p>
<pre><code>  delegate :id, :to =&gt; :__getobj__
</code></pre>
<p>However, delegate classes can go a long way to making your code more supple.</p>
<p>The post <a href="http://pivotallabs.com/delegateclass-rocks-my-world/">DelegateClass rocks my world</a> appeared first on <a href="http://pivotallabs.com">Pivotal Labs</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://pivotallabs.com/delegateclass-rocks-my-world/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using disk: basic (Feed is rejected)
Page Caching using apc
Database Caching using apc
Object Caching 390/408 objects using apc

 Served from: pivotallabs.com @ 2013-05-23 16:32:13 by W3 Total Cache -->