Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

a single class that supports multiple facets/interfaces

1 view
Skip to first unread message

David Garamond

unread,
Jan 20, 2003, 8:00:51 AM1/20/03
to
i want to have a class that can support multiple sets of methods, based
on what the client requests. so far i have written the following python
code. it's ugly/not elegant and i haven't managed to come up with a less
ugly ruby version :-)

<<EOF
class C:
def foo(self):
print "foo"
def bar1(self):
print "bar version 1"
def bar2(self, arg):
print "bar version 2, arg =", arg
def facet(self, whichFacet=1):
if whichFacet == 1:
return C_facet1(self)
elif whichFacet == 2:
return C_facet2(self)
return

class C_facet1:
def __init__(self, c):
self.foo = c.foo
self.bar = c.bar1

class C_facet2:
def __init__(self, c):
self.foo = c.foo
self.bar = c.bar2

c1 = C().facet(1) # client1 picks interface1
c1.foo() # prints "foo"
c1.bar() # prints "bar version 1"

c2 = C().facet(2) # client2 picks interface2
c2.foo() # prints "foo"
c2.bar(123) # prints "bar version 2, arg = 123"
EOF

if a client wants interface 1, then she will get the methods foo and bar
(which is actually C#bar1). if he picks interface 2, then she will get
the methods foo and bar (which is actually C#bar2). for all she cares,
she just wants to know/deal with a single [versatile] class, C. and from
that single class, she can pick several sets of features/interfaces she
needs/wants.

i know that, in ruby, modules are usually used for things like this, but
in my case many of the method names are the same so i don't think
modules are approriate for this. for example, interface1 consists of
foo, bar, baz, abc, and abd. interface2 consists of foo, bar, baz, bab,
bac. in most of the cases all of the interfaces provide the same set of
features, just in a different style or different revision.

any pointers of how i might do something like this in ruby?

--
dave

Jim Weirich

unread,
Jan 20, 2003, 9:21:48 AM1/20/03
to
On Mon, 2003-01-20 at 08:00, David Garamond wrote:
> i want to have a class that can support multiple sets of methods, based
> on what the client requests. so far i have written the following python
> code. it's ugly/not elegant and i haven't managed to come up with a less
> ugly ruby version :-)

Elegant is often in the eye of the beholder. I have this ...

class C
class << self
def facet(n)
facets = [C1, C2]
facets[n-1].new
end
end
def foo
"foo"
end
end

class C1 < C
def bar
"bar version 1"
end
end

class C2 < C
def bar(n)
"bar version 2, arg = #{n}"
end
end

class TestFacet < Test::Unit::TestCase
def test_foo
c = C.facet(1)
assert_equal "foo", c.foo
assert_equal "bar version 1", c.bar
end

def test_facet2_foo
c = C.facet(2)
assert_equal "foo", c.foo
assert_equal "bar version 2, arg = 123", c.bar(123)
end
end

But if the client knows enough to say "c = C.facet(2)", it seems to me
that it wouldn't be any harder to have the client say "c = C2.new".
Then you can drop the the whole facet method in the Class, and the base
class no longer needs knowledge of its child classes (which is a good
thing).

--
-- Jim Weirich jwei...@one.net http://w3.one.net/~jweirich
---------------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

Massimiliano Mirra

unread,
Jan 20, 2003, 11:57:05 AM1/20/03
to
David Garamond <davega...@icqmail.com> writes:

> if a client wants interface 1, then she will get the methods foo and bar
> (which is actually C#bar1). if he picks interface 2, then she will get
> the methods foo and bar (which is actually C#bar2). for all she cares,
> she just wants to know/deal with a single [versatile] class, C. and from
> that single class, she can pick several sets of features/interfaces she
> needs/wants.
>
> i know that, in ruby, modules are usually used for things like this, but
> in my case many of the method names are the same so i don't think
> modules are approriate for this. for example, interface1 consists of
> foo, bar, baz, abc, and abd. interface2 consists of foo, bar, baz, bab,
> bac. in most of the cases all of the interfaces provide the same set of
> features, just in a different style or different revision.
>
> any pointers of how i might do something like this in ruby?

I know that you're avoiding inheritance on purpose, but I'd do it like
this:


class C
def self.new(facet = 1, *args)
if self == C
case facet
when 1 then C1.new(*args)
when 2 then C2.new(*args)
end
else
super(*args)
end
end

def foo
puts "foo"
end

def bar1
puts "bar version 1"
end

def bar2
puts "bar version 2"
end
end


class C1 < C
alias_method :bar, :bar1
end

class C2 < C
alias_method :bar, :bar2
end


c1 = C.new(1) # cilent1 picks interface as defined in C1
c1.foo # prints "foo"
c1.bar # prints "bar version 1"

c2 = C.new(2) # client2 picks interface as defined in C2
c2.foo # prints "foo"
c2.bar # prints "bar version 2"


You can define a class method in C named .facet if you don't want to
touch .new for this purpose, just rename .new.


def self.facet(facet = 1, *args)
if self == C
case facet
when 1 then C1.new(*args)
when 2 then C2.new(*args)
end
else
super(*args)
end
end


And can easily take away the ugly case statement, too:


def self.facet(facet = 1, *args)
if self == C
Object.const_get("#{self}#{facet}").new(*args)
else
super(*args)
end
end


Finally, you might want to make it generally useful by stuffing it
into a mixin:


module Facetable
def facet(facet = 1, *args)
if self == C
Object.const_get("#{self}#{facet}").new(*args)
else
super(*args)
end
end
end

class C
extend Facetable

...
end

class C1 < C
...
end

class C_something < C
...
end

c1 = C.facet(1)
csth = C.facet("_something")


...and so on.

Massimiliano

dbl...@candle.superlink.net

unread,
Jan 20, 2003, 1:04:00 PM1/20/03
to
Hi --

On Mon, 20 Jan 2003, David Garamond wrote:

> i want to have a class that can support multiple sets of methods, based
> on what the client requests. so far i have written the following python
> code. it's ugly/not elegant and i haven't managed to come up with a less
> ugly ruby version :-)

[...]

> i know that, in ruby, modules are usually used for things like this, but
> in my case many of the method names are the same so i don't think
> modules are approriate for this. for example, interface1 consists of
> foo, bar, baz, abc, and abd. interface2 consists of foo, bar, baz, bab,
> bac. in most of the cases all of the interfaces provide the same set of
> features, just in a different style or different revision.
>
> any pointers of how i might do something like this in ruby?

I wouldn't dismiss modules for this purpose. For example:

class C
module Facet1
def bar


puts "bar version 1"
end

end

module Facet2
def bar(arg)
puts "bar version 2, arg=#{arg}"
end
end

def foo
print "foo"
end

def facet(f=1)
extend self.class.const_get("Facet" + f.to_s)
end
end

C.new.facet(1).bar # bar version 1
C.new.facet(2).bar("hi") # bar version 1, arg=hi

Mind you, as Jim pointed out, once you have to know about facet(1) and
all that, you might as well bring it up front, which in my version
would mean doing:

c = C.new.extend(C::Facet1)
c.bar # bar version 1

or equivalent. (extend is very cool :-)


David

--
David Alan Black
home: dbl...@candle.superlink.net
work: blac...@shu.edu
Web: http://pirate.shu.edu/~blackdav


Gennady

unread,
Jan 20, 2003, 1:34:54 PM1/20/03
to
[x86.bison:695]gfb> ruby -v
ruby 1.6.7 (2002-03-01) [i386-solaris2.8]


I have a module that I will use as a mixin:

module Original
def foo
pust "Original#foo()"
end
end

include Original

foo # => Original#foo()

Everything is OK so far. Then I would like to mix another module into
Original that redefines method foo:

module Another
def foo
puts "Another#foo()"
end
end

module Original
include Another
end

foo # => Original#foo()

Here I have a problem -- Original's foo is still being called (and I expect
Another's). The only way to achieve what I want is to redefine module
Another above to:

module Another
def self.append_features(mod)
super mod
mod.module_eval %Q{
def foo
puts "Another#foo()"
end
}
end
end

Am I missing something? Is there a way to go without eval?

Thanks in advance,
Gennady.


David Garamond

unread,
Jan 22, 2003, 3:08:40 AM1/22/03
to
dbl...@candle.superlink.net wrote:
> c = C.new.extend(C::Facet1)
> c.bar # bar version 1
>
> or equivalent. (extend is very cool :-)

thanks. so as i see it, 'extend' is like 'include' but operates at the
object level (while 'include' usually works at the class level), right?
extend _is_ cool :-)

btw, does 'extend' have a significant runtime cost?

--
dave


ts

unread,
Jan 22, 2003, 5:30:52 AM1/22/03
to
>>>>> "D" == David Garamond <davega...@icqmail.com> writes:

D> thanks. so as i see it, 'extend' is like 'include' but operates at the
D> object level (while 'include' usually works at the class level), right?

Well, it's perhaps best if you see that #extend work at singleton level

For example

class A
extend M
end

is *like* if you write

class A
class << self
include M
end
end

a = A.new.extend(M)

is *like*

class << a
include M
end


D> btw, does 'extend' have a significant runtime cost?

no, why ?


Guy Decoux


Gavin Sinclair

unread,
Jan 22, 2003, 11:47:26 AM1/22/03
to
On Wednesday, January 22, 2003, 9:30:52 PM, ts wrote:

>>>>>> "D" == David Garamond <davega...@icqmail.com> writes:

D>> thanks. so as i see it, 'extend' is like 'include' but operates at the
D>> object level (while 'include' usually works at the class level), right?

> Well, it's perhaps best if you see that #extend work at singleton level

> For example

> class A
> extend M
> end


Is

A.extend M

a shortcut for this? Practically it seems to be, and theoretically it
should be, since "class A" sets "self" to "A".

So is there a shortcut for

class A
include M
end

?

The logic above appears to apply, but

A.include M

fails because "private method `include' called for A:Class".


Just wondering if I've got everything right, and if there's anything
else to learn about it.

Gavin


Mauricio Fernández

unread,
Jan 22, 2003, 11:57:28 AM1/22/03
to
On Thu, Jan 23, 2003 at 01:47:26AM +0900, Gavin Sinclair wrote:
> > class A
> > extend M
> > end
>
>
> Is
>
> A.extend M
>
> a shortcut for this? Practically it seems to be, and theoretically it
> should be, since "class A" sets "self" to "A".
>
> So is there a shortcut for
>
> class A
> include M
> end
>
> ?
>
> The logic above appears to apply, but
>
> A.include M
>
> fails because "private method `include' called for A:Class".

You do probably already know it, but for the record, we have
Object#extend (public)
Module#include (private)

The question is why are Module#include and Module#append_features
private?



>
> Just wondering if I've got everything right, and if there's anything
> else to learn about it.
>
> Gavin
>

--
_ _
| |__ __ _| |_ ___ _ __ ___ __ _ _ __
| '_ \ / _` | __/ __| '_ ` _ \ / _` | '_ \
| |_) | (_| | |_\__ \ | | | | | (_| | | | |
|_.__/ \__,_|\__|___/_| |_| |_|\__,_|_| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

People disagree with me. I just ignore them.
-- Linus Torvalds, regarding the use of C++ for the Linux kernel

Gavin Sinclair

unread,
Jan 22, 2003, 12:07:14 PM1/22/03
to
On Thursday, January 23, 2003, 3:57:28 AM, Mauricio wrote:

>> The logic above appears to apply, but
>>
>> A.include M
>>
>> fails because "private method `include' called for A:Class".

> You do probably already know it, but for the record, we have
> Object#extend (public)
> Module#include (private)

> The question is why are Module#include and Module#append_features
> private?


Well, here's some cool code then:

>> module M; def hi; "hi"; end; end
=> nil

>> class A; end
=> nil

>> A.include M
NameError: private method `include' called for A:Class
from (irb):3

>> class Module
>> def inc(*args)
>> include *args
>> end
>> end
=> nil

>> A.inc M
=> A

>> A.new.hi
=> "hi"

Gavin (who's trying to get his Ruby-English ratio up...)


Gavin Sinclair

unread,
Jan 22, 2003, 12:12:58 PM1/22/03
to
On Thursday, January 23, 2003, 4:07:14 AM, Gavin wrote:

> On Thursday, January 23, 2003, 3:57:28 AM, Mauricio wrote:

>>> The logic above appears to apply, but
>>>
>>> A.include M
>>>
>>> fails because "private method `include' called for A:Class".

>> You do probably already know it, but for the record, we have
>> Object#extend (public)
>> Module#include (private)

>> The question is why are Module#include and Module#append_features
>> private?


> Well, here's some cool code then:

.. and some more:

module M
def hi
"hi"
end
end

class B
end

B.include M # => fails: Class#include is private

class Class
def pub(*a) # Because public is private, too.
public *a
end
end

Class.pub :include

B.include M # => succeeds

B.new.hi # => "hi"


Gavin

PS. I think allowing "A.include M" would be nice. It jars that
"extend" is allowed but not "include". I'm not going to make a big
deal of it though.


Mauricio Fernández

unread,
Jan 22, 2003, 12:13:42 PM1/22/03
to
On Thu, Jan 23, 2003 at 02:07:14AM +0900, Gavin Sinclair wrote:
> Well, here's some cool code then:
>
> >> module M; def hi; "hi"; end; end
> => nil
>
> >> class A; end
> => nil
>
> >> A.include M
> NameError: private method `include' called for A:Class
> from (irb):3
>
> >> class Module
> >> def inc(*args)
> >> include *args
> >> end
> >> end
> => nil
>
> >> A.inc M
> => A
>
> >> A.new.hi
> => "hi"
>
> Gavin (who's trying to get his Ruby-English ratio up...)

Me too :)

batsman@tux-chan:/tmp$ irb --prompt-mode simple
>> Module.instance_eval <<EOF
public :include # why not default?
EOF
=> Module


>> module M; def hi; "hi"; end; end
=> nil
>> class A; end
=> nil

>> A.new.hi
NameError: undefined method `hi' for #<A:0x40272a44>
from (irb):6
>> A.include M


=> A
>> A.new.hi
=> "hi"

--
_ _
| |__ __ _| |_ ___ _ __ ___ __ _ _ __
| '_ \ / _` | __/ __| '_ ` _ \ / _` | '_ \
| |_) | (_| | |_\__ \ | | | | | (_| | | | |
|_.__/ \__,_|\__|___/_| |_| |_|\__,_|_| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

Never make any mistaeks.
-- Anonymous, in a mail discussion about to a kernel bug report

Gavin Sinclair

unread,
Jan 22, 2003, 12:59:31 PM1/22/03
to
On Thursday, January 23, 2003, 4:12:58 AM, Gavin wrote:

>> Well, here's some cool code then:

> ... and some more:

.. and finally (I just noticed this in fileutils.rb)

Ever find it annoying when you create a module which is basically
intended to be a namespace/container for a set of functions, and they
don't work because you have to use "public_module_method" or whatever?

Here's another way.

module M
def hi
"hi"
end
end

M.extend M # These

module M # three are
extend M
end

module M # equivalent
extend self
end

M.hi # => "hi"


The code I saw opted for "extend self" after all the methods had been
defined. This had me scratching my head wondering what it meant. A
comment up the top switched the light on. But it could have been
written "extend FileUtils", which would have been a bit clearer, and
it could have been done up the top, with a comment saying all methods
are hearby public module methods.

That's right, it could be up the top. Check this:

>> module M
>> def hi
>> "hi"
>> end

>> extend M
>> def lo
>> "lo"
>> end
>> end
=> nil

>> M.hi
=> "hi"

>> M.lo
=> "lo"

Apparently, using "extend" merely hooks something up behind the
scenes, rather than dumping a lot of information into the target
module. This is not particularly surprising, but it's still nice to
have a language that lets you play around like this and experiment.

Gavin


ts

unread,
Jan 22, 2003, 1:15:00 PM1/22/03
to
>>>>> "G" == Gavin Sinclair <gsin...@soyabean.com.au> writes:

G> Apparently, using "extend" merely hooks something up behind the
G> scenes, rather than dumping a lot of information into the target
G> module.

Well, if you have

module M end
module N end
class A
include M
extend N
end

internally (if you don't modify what do ruby by default) you'll have

A ===> [M] ===> Object
| |
| |
v v
(A) ==> [N] ===> (Object)

Where (A), (Object) are the singleton classes associated with A, Object
[M], [N] are the proxy classes for M, N

This is the same for a module (rather than class A)


Guy Decoux

Sam Roberts

unread,
Jan 25, 2003, 3:56:10 PM1/25/03
to
Quoteing davega...@icqmail.com, on Mon, Jan 20, 2003 at 10:00:51PM +0900:

> i want to have a class that can support multiple sets of methods, based
> on what the client requests. so far i have written the following python
> code. it's ugly/not elegant and i haven't managed to come up with a less
> ugly ruby version :-)

I'm just learning Ruby, but what about the really simple:

class C

class Ca
def foo
...
end
end

class Cb
def foo
...
end
end

def foo
@facet.foo
end

def initialize(type)
if(type)
@facet = Va.new
else
@facet = Vb.new
end

end


Completely untested, but that's what I'd do in C++. Granted C::Ca#foo
isn't really a method of C, so it can't access instance variables of C,
but you could simulate this by having C#foo pass self as an arg to
@facet.foo.

Is this somehow fundamentally bad?

Sam


Tom Sawyer

unread,
Jan 25, 2003, 6:41:00 PM1/25/03
to
On Saturday 25 January 2003 01:56 pm, Sam Roberts wrote:
> Quoteing davega...@icqmail.com, on Mon, Jan 20, 2003 at 10:00:51PM +0900:
> > i want to have a class that can support multiple sets of methods, based
> > on what the client requests. so far i have written the following python
> > code. it's ugly/not elegant and i haven't managed to come up with a less
> > ugly ruby version :-)

likewise untested:

class C

def C.factory(type)
case type
when :a
return Ca
else
return Cb
end
end

class Ca < self
def foo
puts 'override foo'
end
end

class Cb < self
def foo
puts 'override foo'
end
end

def foo
puts 'default foo'
end

end

c = C.factory(:a).new

--
tom sawyer, aka transami
tran...@transami.net


0 new messages