---
title: it's all about behavior
teaser: You need a new class when you have new behavior.
tags: web,rails
author: Jared Carroll
published_on: 2007-04-14
---

How do you know when you need a class?  What tells you, "I think I need to
create another class"?  The answer is: behavior.  Not state, behavior.

Here's an example:

```ruby
class Order < ActiveRecord::Base
end
```

And our `orders` table:

    orders (id, customer, item, price, created_on)

That customer column is a `varchar` that contains the customer's name.  I want
to keep it simple, so an  `Order` contains an `item` name and its total `price`.

Our client then requests a feature, they want to be able to read and update
their existing customers' names.  Ok, no problem:

```ruby
class Order < ActiveRecord::Base

  def self.find_all_customers
  end

  def self.update_customer(new_name, old_name)
  end

end
```

Now our `Order` class is dealing with customer related behavior.  This will get
ugly.  Obviously, we're missing a class: `Customer`:

```ruby
class Order < ActiveRecord::Base
  belongs_to :customer
end

class Customer < ActiveRecord::Base
  has_many :orders
end
```

We get the #find and #update methods for free from `ActiveRecord::Base`.

And our `orders` table is now:

    orders (id, item, price, created_on, customer_id)

And our new `customers` table is:

    customers (id, name)

This is a very simple example.  The key thing is that we didn't introduce that
`Customer` class until our client asked for a featured related to customers.  We
had new behavior and instead of making the `Order` class handle both order and
customer responsibilities we introduced a new class.  Now our responsibilities
are cleanly separated and each of our classes are responsible for 1 thing,
`Order` for orders and `Customer` for customers.

Another example:

```ruby
class Company < ActiveRecord::Base
end
```

And our `companies` table:

    companies (id, street, city, state)

Now our client asked that when viewing a `Company`, they'd like its address
plotted on a Google map, no problem.  Google maps, like any mapping <abbr
title="Application Programming Interface">API</abbr>, needs the latitude and
longitude of the address to plot.  So we create a method on `Company` to handle
that:

```ruby
class Company < ActiveRecord::Base

  def geocode_address
    # return latitude and longitude
  end

end
```

Hmmm, why is the `Company` class doing address geocoding?  Seems like I'm
missing something.  Of course, an `Address` class.  Let's change it:

```ruby
class Company < ActiveRecord::Base

  composed_of :address,
    :mapping => [%w(street street),
                 %w(city city),
                 %w(state state)]

end

class Address

  attr_reader :street, :city, :state

  def initialize(street, city, state)
    @street, @city, @state = street, city, state
  end

  def geocode
    # return latitude and longitude
  end

end
```

That's better.  I don't want my `Company` objects doing geocoding, that should
be an `Address` object's responsibility (even our method names are better
`company.geocode_address` vs. `company.address.geocode`; 1 word, short and to
the point).

Once again by reflecting on our class's responsibilities we discovered missing
key objects.  By focusing on behavior we end up with small objects, each doing 1
responsibility and our responsibilities are distributed where they belong.  When
someone says "hey I think geocoding is not working right", is a new developer
going to look for the geocoding responsibility in the `Company` class?  Of
course not, the application is geocoding an address, so its logical to look for
it in an `Address` class.

THIS IS OBJECT-ORIENTED (OO) PROGRAMMING.  Objects are all about behavior,
discovering objects and distributing responsibilies results in maintainable,
easy to understand systems.  Systems in which each object has very few methods,
each method is short, each class has 1 responsibility and there is very little
conditional logic.  There are no "god" classes – huge classes that do a large
number of totally un-related responsibilities (like our `Order` handling
`Customer` responsibilities or our `Company` doing geocoding). Many developers
do not realize the fundamental concept of behavior in OO programming and as
result spend their time progamming procedurally in languages that support
classes and think they're doing OO programming because they're creating classes.
They never make the "jump" to objects and never get to realize that code can be
a lot more elegant and programming can be a lot more enjoyable than wading
through nested "if" statements.

There is no problem or no domain complex enough that objects can not simplify.
"It's a complicated problem" is not an excuse for convoluted, long,
conditional-logic laden code.  You need to challenge yourself and think how to
simplify the problem because your code is not going to be maintainable.

A common complaint among people new to objects is that its hard to track down
where the actual work takes place.  This is true, by distributing
responsibilities, several messages may need to be sent to several different
objects; this means you may have to open up more than 1 file.  This is the
beauty of objects, the hardest problem can be solved very easily by a group of
objects each doing their 1 simple responsibility but collectively solving a
complex problem.

## It is not about state

State (i.e. instance variables, attributes, etc.) is not a reason to create a
new class.  For example:

```ruby
class Patient < ActiveRecord::Base
end

class Age

  attr_reader :age

  def initialize(age)
    @age = age
  end

end
```

And our `patients` table:

    patients (id, name, age)

That `Age` class has no behavior, just state.  This class is un-necessary and
adds nothing to the system.  These types of classes are usually referred to as
"dumb data holders", they're just state and getters and setters – they have no
interesting behavior.

This style of modeling is common among data-modelers/DBAs.  What is in a
database?  State.  That's it, there's no behavior in a database.  There's just
tables and data.  The database is usually controlled by a "god" class called a
database management system (DBMS).  These are all the functions that handle
query parsing and execution, transaction management, concurrency, etc.  So a
database is a collection of functions (DBMS) reading and writing a bunch of
data.  This sounds familiar.  It's called procedural programming, the kind you
first learned in school using C or BASIC.

So when modeling a system a data-modeler/DBA will probably first attempt to be
really <abbr title="Don't Repeat Yourself">DRY</abbr> and normalize the schema.
The schema for our above `Order` example becomes:

    orders (id, item, price, created_on, customer_id)

    customers (id, name, address_id)

    addresses (id, street, city_id)

    cities (id, name, state_id)

    states (id, name)

Then they'll turn each of those into classes:

```ruby
class Order < ActiveRecord::Base
  belongs_to :customer
end

class Customer < ActiveRecord::Base
  has_many :orders
  belongs_to :address
end

class Address < ActiveRecord::Base
  belongs_to :city

  def geocode
    # return latitude and longitude
  end

end

class City < ActiveRecord::Base
  belongs_to :state
end

class State < ActiveRecord::Base
end
```

`Customer`, `Order` and `Address` are nice and worth having, however `City` and
`State` add nothing and are simply "dumb data holders".  "But wait `City` and
`State` have all the interesting <abbr title="Create Read Update
Delete">CRUD</abbr> behavior they get for free from `ActiveRecord::Base`,
they're not dumb data holders", they do have the interesting <abbr title="Create
Read Update Delete">CRUD</abbr> behavior but our client only asked to geocode an
address, they did not ask to <abbr title="Create Read Update Delete">CRUD</abbr>
cities and states; therefore its un-necessary.

Data modeling and object modeling are 2 different things.  Data modeling comes
from the database world and tends to focus on state.  Object modeling focuses on
behavior.

Although these examples are in Ruby, the language makes no difference.  Object
modeling is object modeling, if a language supports classes then these examples
would be just about the same in any language.

Only create classes when new behavior is asked for and there's currently no
logical place for it.  Do not cram more and more behaviors into existing
classes, soon these classes will be very large, with many methods and wildly
different responsibilities; these "god" classes should be a clear indication
that thinking about behavior (i.e. thinking in objects) is not happening.

## What about join models in Rails?

So I said not to create classes that don't have any interesting behavior i.e.
"dumb data holders", so you explain the following example to me:

```ruby
class User < ActiveRecord::Base
  has_many :memberships
end

class Membership < ActiveRecord::Base
  belongs_to :user
  belongs_to :group
end

class Group < ActiveRecord::Base
  has_many :memberships
end
```

And our `memberships` table:

    memberships (id, user_id, group_id, active)

In this case `active` is a boolean allowing a `User`'s `Membership` to be
activated/deactivated.  It's 1 attribute, no interesting behavior and there's a
class for it.  I would argue that this is a limitation of Rails and its
inability to update attributes in a join table i.e. if we had a `groups_users`
join table instead of a `Membership` model.  This is a trade-off Rails
developers have to deal with.
