Dependency Management

Hi Rails developers,

I would appreciate some assistance. Please study the following code and
provide your reasoning for whether you agree or disagree with the
following
statements.

I understand the code is not specific to Rails, however I am
specifically
interested in Rails developer’s perspective. Thanks!

car.rb

class Car
def initialise(engine)
@engine = engine
end

def start
@engine.start
end
end

engine.rb

class Engine

def start

puts ‘Engine started’

end

end

  • Car is not dependent on, and therefore not coupled to Engine

  • Car is dependent on an object that responds to “start”

  • Car may or may not be initialised with an Engine object

  • Car uses constructor-based dependency injection

Matthew Riley wrote in post #1141041:

Hi Rails developers,

I would appreciate some assistance. Please study the following code and
provide your reasoning for whether you agree or disagree with the
following
statements.

I understand the code is not specific to Rails, however I am
specifically
interested in Rails developer’s perspective. Thanks!

car.rb

class Car
def initialise(engine)
@engine = engine
end

def start
@engine.start
end
end

engine.rb

class Engine

def start

puts ‘Engine started’

end

end

  • Car is not dependent on, and therefore not coupled to Engine

  • Car is dependent on an object that responds to “start”

  • Car may or may not be initialised with an Engine object

  • Car uses constructor-based dependency injection

The example is a little too simple to comment but i can agree with your
statements.

  • Car is dependent on an object that responds to “start”.

This i think is the trickiest one. If only the engine class has a method
called ‘start’ then Car is effectively coupled to Engine. Also if
additional public interface methods get added to Engine, and Car starts
to call those:
engine.gear_up
engine.stop
then Car again becomes more and more coupled to the operations of
Engine.

Also if Car starts to manage other components, and calling their public
interfaces, thus having high fan out, then you also get tight coupling.

So again the point is, this example is still too simple to judge, and
the design pattern that will make the most sense for it is dependent on
how the rest of the application will flow.

On Wed, Mar 26, 2014 at 10:32 AM, masta Blasta [email protected]
wrote:

If only the engine class has a method
called ‘start’ then Car is effectively coupled to Engine.

Since one could theoretically create another class that supports
#start (and your later examples of #gear_up and #stop, probably
delegating #gear_up to a Transmission-like class), I think it’s still
effectively decoupled. In particular, when testing the Car, one
could create it with a MockEngine that’s only used in the tests.


Dave A., freelance software developer (details @ www.Codosaur.us);
see also www.PullRequestRoulette.com, Blog.Codosaur.us, www.Dare2XL.com

Hi, thanks for taking the time to respond.

I understand the example is a simple one, but based on that simple
example,
would you agree that since the Car class does not explicitly use the
Engine
class, the two classes are completely unrelated?
Also, when you say, “Car again becomes more and more coupled to the
operations of Engine”, are you saying that Car becomes more coupled to
the
concrete implementation of Engine, or to it’s “interface”?

Matthew Riley wrote in post #1141041:

I understand the code is not specific to Rails, however I am
specifically
interested in Rails developer’s perspective. Thanks!

Perspective on what? As you demonstrate, Dependency Injection (DI) is a
really simple concept, with a really simple implementation, in dynamic
(“Duck Typed”) languages like Ruby.

Are you asking to verify what you have shown is “Dependency
Inject/Inversion of Control?” If so then sure it is.

Are you asking if Rails developers spend a lot of time worrying about
“Dependency Injection” or creating elaborate frameworks to support DI?
That would be a pretty resounding “NO”.

Given that a high percentage of DI usage is related to testability, it
might do you good to look at one of the popular testing frameworks used
by Rails developers, for example:

https://github.com/rspec/rspec

You won’t see a lot of talk in there about DI or IoC mumbo-jumbo. You’ll
see the stuff that’s actually important, like test doubles, mocks,
stubs, etc. Ruby is a dynamically typed language without all those pesky
static type dependencies. Take advantage of that in whatever way makes
sense.

Hi, thanks for taking the time to respond.

I’m experienced with Ruby, but new to Rails. I’m familiar with RSpec.

Ruby certainly does make DI really, really easy.
Ruby also makes it really easy to test code without the need for IoC.

IoC offers other benefits aside from testability. One obvious example is
loose coupling.
Loose coupling reduces the risk that changes impact seemingly unrelated
areas of the system.
Ruby in itself cannot offer that benefit, nor can Rails.

Would you agree that regardless the language or framework, loose
coupling
is a valuable engineering practise?
If not, why not? If so, how do you accomplish loose coupling in Rails?

Look forward to your reply, thanks.

Hi Dave, thanks for taking the time to respond.

Of the two examples below, which do you believe is more testable?

Example 1:

class Car
def initialise(engine)
@engine = engine
end

def start
@engine.start
end
end

Example 2:

class Car
def initialise
@engine = Engine.new
end

def start
@engine.start
end
end

On Thu, Mar 27, 2014 at 3:48 AM, Matthew Riley
[email protected] wrote:

Of the two examples below, which do you believe is more testable?

Example 1:

class Car
def initialise(engine)
@engine = engine
end

Example 2:

class Car
def initialise
@engine = Engine.new

end

Definitely #1, as it will accept any sort of engine-like thing you
care to put in it, so you can use a mock or stub or other kind of
fake, even without relying on a faking library to monkey with the item
(or worse yet, with Engine).

I might be tempted to make a slight change, though: declare the
initializer as “def initialise(engine = Engine.new)”, so you don’t
have to explicitly pass in an Engine in the default case. Yes, that
re-couples them somewhat, that’s the downside. If you can be sure
that Car always has access to Engine, like if they’re all part of the
same gem you’re distributing, this is probably acceptable. In fact,
you could do something like:

def initalize(options={})
@engine = options[:engine] || Engine.new
@transmission = options[:transmission] || Transmission.new
@horn = options[:horn] || Horn.new

and so on

end

or if you want to really delve into funky Ruby, maybe even do some
metaprogramming to DRY up that pattern, like:

def initalize(options={})
[:engine, :transmission, :horn].each do |sym|
option = options[sym]
klazz = Kernel.const_get(sym.capitalize)
self.instance_variable_set(sym, option || klazz.new)
end
end

That might be more worthwhile if you have a long list of options to pass
in.

-Dave


Dave A., freelance software developer (details @ www.Codosaur.us);
see also www.PullRequestRoulette.com, Blog.Codosaur.us, www.Dare2XL.com

If Engine were to inherit ActiveRecord, would that affect your decision
to
set a default instance?
In such a case, Car would be effectively coupled to both Engine and
ActiveRecord.
Also, do you find Rails developers in general care about loose coupling?

On Fri, Mar 28, 2014 at 6:04 AM, Matthew Riley
[email protected] wrote:

If Engine were to inherit ActiveRecord, would that affect your decision to
set a default instance?

It depends. Theoretically, the more baggage something drags in, the
less you want to be coupled to it. Kinda like with people. :wink:

But in reality, well… see below.

In such a case, Car would be effectively coupled to both Engine and
ActiveRecord.
Also, do you find Rails developers in general care about loose coupling?

In my experience, most Rails devs aren’t going to take the time to
decouple from AR. Rails is very opinionated, and one of its opinions
is that your models (at least the ones that get stored in the
database) should inherit from AR::Base. Yes, it helps makes your
tests much faster, swapping out databases much easier, etc. if you can
cleanly separate the concerns of business logic versus storage. But
it’s not the golden path. The tutorials don’t tell you how. It’s not
easy. It takes some advanced thinking to even care about it, let
alone actually implement it. So… while many devs will care deeply
about not coupling their own models to each other, most seem
perfectly OK with, or at least resigned to, having most of them
coupled to AR::Base.

I’ve read some stuff on how to decouple the models from AR and still
have the benefits it brings, and tried to do it in a couple little
exercises, but still don’t have a good intuitive feel for the process,
especially what step is next and why. Most of what I’ve read, makes
several leaps where I can see the benefit in hindsight, but still
don’t see/understand any signs pointing in the immediate directions.

-Dave


Dave A., freelance software developer (details @ www.Codosaur.us);
see also www.PullRequestRoulette.com, Blog.Codosaur.us, www.Dare2XL.com

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs