Let's say I want to implement interfaces using classes (so I can do interface checking with the simple Object#kind_of?). There will be components (which will also be classes). A component can implement one or more interfaces. Method names from different interfaces might clash (e.g. IFoo#quack vs IBar#quack) and we need to be able to choose which interface we want to use from a component.
What's the best way to implement this? My current attempt:
--- BEGIN OF CODE ---
# components will derive from the Component class, to share
# some common code
class Component
# @implements will contain a list of interfaces that this component
# implements
def Component.implements; @implements end
def implements; self.class.implements end
def Component.implements?(itf); @implements.include? itf end
def implements?(itf); self.class.implements? itf end
# @implementors is a hash, containing the class that implements a
# certain interface.
def Component.implementors; @implementors end
def implementors; self.class.implementors end
# will return an instance of implementor class
def use_interface(itf, *args)
raise ArgumentError unless implements? itf
raise NotImplementedError unless implementors[itf]
implementors[itf].new(*args)
end
end
# IHasFeet is an interface
class IHasFeet
# s is speed
def walk(s); raise NotImplementedError end
# d is the name of a dance
def entertain(d); raise NotImplementedError end
end
# IHasMouth is an interface
class IHasMouth
# sp is speed, str is string
def talk(sp, str); raise NotImplementedError end
# s is the name of a song
def entertain(s); raise NotImplementedError end
end
# Human is a component. It implements IHasFeet and IHasMouth.
class Human < Component
@implements = [IHasFeet, IHasMouth]
class Human_IHasFeet < IHasFeet
def walk(s); puts "Walking..." end
def entertain(d); puts "Can't dance!" end
end
class Human_IHasMouth < IHasMouth
def talk(sp, str); puts str end
def entertain(s); puts "Singing #{s}..." end
end
@implementors = {IHasFeet => Human_IHasFeet,
IHasMouth => Human_IHasMouth}
end
# Bear is a component. It implements IHasFeet and IHasMouth.
class Bear < Component
@implements = [IHasFeet, IHasMouth]
class Bear_IHasFeet < IHasFeet
def walk(s); puts "Walking..." end
def entertain(d); puts "Dancing #{d}..." end
end
class Bear_IHasMouth < IHasMouth
def talk(sp, str); puts "R O A R!" end
def entertain(s); puts "Can't sing!" end
end
@implementors = {IHasFeet => Bear_IHasFeet,
IHasMouth => Bear_IHasMouth}
end
# test
p Bear.implements # -> [IHasFeet, IHasMouth]
p Bear.implements? IHasMouth # -> true
bear = Bear.new
p bear.implements # -> [IHasFeet, IHasMouth]
p bear.implements? IHasFeet # -> true
b = bear.use_interface(IHasFeet)
p b.kind_of? IHasFeet # -> true
b.walk(1) # -> Walking...
b.entertain("russian dance") # -> Dancing russian dance...
b = bear.use_interface(IHasMouth)
b.entertain("o canada") # -> Can't dance!
--- END OF CODE ---
The code feels very mechanic and contains a lot of plumbing, but it's pretty much what I wanted (my requirements):
a) a component can implement multiple interfaces;
b) I can pick interfaces from components without fear of conflict;
c) my other code can later check whether something has a feet or a mouth, without explicitly checking its component class (Human or Bear).
Any better suggestion?
···
--
dave