What is Ruby for "pimpl"?

J

John Carter

A useful dependency cutting tool in C++ is the pimpl idiom.
http://www.gotw.ca/gotw/024.htm

I trying to imagine a ruby translation of the idiom.

Dependency Injection?



John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : (e-mail address removed)
New Zealand

The universe is absolutely plastered with the dashed lines exactly one
space long.
 
J

Jim Weirich

A useful dependency cutting tool in C++ is the pimpl idiom.
http://www.gotw.ca/gotw/024.htm

I trying to imagine a ruby translation of the idiom.

Wow, this takes me back. I haven't done C++ code in quite some time.
Hmmm ... a ruby version of pimpl? There is the obvious hack where you could
move the entire implementation to a separate object and just delegate/forward
method calls to the implementation (and using method_missing tricks can make
this much easier than the C++ version).

But that begs the question of /why/ we should use the pimpl idiom. To quote
from the GotW page: "In C++, when anything in a class definition changes
(even private members) all users of that class must be recompiled. To reduce
these dependencies, a common technique is to use an opaque pointer to hide
some of the implementation details."

There's the nub of the matter. In Ruby, nothing needs recompiled if a class
changes. Using pimpl in Ruby has all the disadvantages of the C++ version
(indirection, extra objects) and none of the benefits.
Dependency Injection?

Actually, I see DI as more closely related to the /other/ compile dependency
reduction technique often used in C++: Abstract Base Classes (which I
generally used instead of the pimpl idiom). And again, in Ruby, through the
"magic" of duck typing, the ABC idiom is trivial.
 
J

Jamis Buck

John said:
A useful dependency cutting tool in C++ is the pimpl idiom.
http://www.gotw.ca/gotw/024.htm

I trying to imagine a ruby translation of the idiom.

Dependency Injection?

Well, there isn't really a "compile" phase for Ruby, so changing any
interface of any class never requires a recompile. As long as the change
only affects non-published interfaces of the class, the clients of the
class will continue unaffected, blissfully ignorant of your refactoring.

Dependency injection solves a different set of problems than the pimpl
idiom (which is really only necessary for C++).

Does that come close to answering your question? Let me know if I've got
wide of the mark.

- Jamis
 
J

John Carter

I trying to imagine a ruby translation of the pimpl idiom.

Let me be a bit more explicit about what I'm thinking about.

I ran a unit test that failed on a syntax error in another module/class.

Looking at the error message I realized that class A used B which used C
which used D which under certain error conditions that could never occur
while testing A, used E.

This left me feeling my program was too coupled. Why did the syntax of a
module that I wasn't using block me?

In C++ I could have hidden the implementation of E behind a pimpl, and all
would have been sweet.

So thinking out loud...
-------file A.rb------------------------------------------------------
require 'FixMe'

class A
def foo
dostuff()
rescue BadThingsHappening => details
b = FixMe.new( details, x, y, z)
b.make_it_all_better
end

def bah
end
end
----------------------------------------------------------------------
If I feed this to Ruby it insists that FixMe must be defined.

So if I do....
----------------------------------------------------------------------
require 'A'
class C
def initialize
a = A.new
a.bah
end
end
----------------------------------------------------------------------
Even though I never invoked foo, as soon as I "require 'A'", I must
have FixMe initialized.

So how about....
----------------------------------------------------------------------
class A
def A.helper( b_class)
@@helper = b_class
end

def foo
dostuff()
rescue BadThingsHappening => details
b = @@helper.new( details, x, y, z)
b.make_it_all_better
end

def bah
end
end
----------------------------------------------------------------------
This way I could have

-File C.rb---------------------------------------------------------------------
require 'A'
class C
def initialize
a = A.new
a.bah
end
end
----------------------------------------------------------------------
and C can be used without FixMe being defined and another script that did use foo do something like...

-File other.rb--------------------------------------------------------
require 'FixMe'
require 'A'
A.helper( FixMe)
a = A.new
a.foo
----------------------------------------------------------------------
Or even more self contained...
----------------------------------------------------------------------
class SimpleHelper
def initialize(details)
@Details = details
end

def make_it_all_better
raise "I can't make '#{details}' better, I'm too dumb"
end
end

class A
@@helper = SimpleHelper

def A.helper( b_class)
@@helper = b_class
end

def foo
dostuff()
rescue BadThingsHappening => details
b = @@helper.new( details, x, y, z)
b.make_it_all_better
end

def bah
end
end
----------------------------------------------------------------------

But this still requires a concrete definition of a Helper, so a simpler version might be..

----------------------------------------------------------------------
class A
@@helper = nil

def A.helper( b_class)
@@helper = b_class
end

def foo
dostuff()
rescue BadThingsHappening => details
raise details unless @@helper
b = @@helper.new( details, x, y, z)
b.make_it_all_better
end

def bah
end
end
----------------------------------------------------------------------

That looks good, and has simplified my tests, but has complicated the
client code for A.

Sorry for thinking out loud, excuse me for existing.



John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : (e-mail address removed)
New Zealand

The universe is absolutely plastered with the dashed lines exactly one
space long.
 
J

Jim Weirich

Sorry for thinking out loud, excuse me for existing.

No need to apologize. Just didn't see where you were going with this one.
On Mon, 29 Nov 2004, John Carter wrote:
Looking at the error message I realized that class A used B which used C
which used D which under certain error conditions that could never occur
while testing A, used E.

This left me feeling my program was too coupled. Why did the syntax of a
module that I wasn't using block me?

The simplest thing to do would just move the require so that it executes only
when it is really needed.
-------file A.rb------------------------------------------------------
class A
def foo
dostuff()
rescue BadThingsHappening => details
require 'FixMe' # Move the require to here
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,260
Messages
2,571,038
Members
48,768
Latest member
first4landlord

Latest Threads

Top