What OOP principles, if any, don't apply or apply differently in a dynamically typed environment as opposed to a statically-typed environment (for example Ruby vs C#)? This is not a call for a Static vs Dynamic debate, but rather I'd like to see whether there are accepted principles on either side of that divide that apply to one and not the other, or apply differently. Phrases like "prefer composition to inheritance" are well known in the statically-typed OOP literature. Are they just as applicable on the dynamic side?
For instance, in a dynamically typed environment, it would seem that the granularity of coupling goes no further than the level of the method. In other words, any given function call only couples the caller to that particular interface, which any class could possibly satisfy -- or to put it another way, anything that quacks like that particular duck.
In Java, on the other hand, the granularity of coupling can go as high as the package. Not only does a particular method call establish a contract with another class/interface, but also couples it into that classes/interface's package/jar/assembly.
Do differences like this give rise to different principles and patterns? If so have these differences been articulated? There's a section in the Ruby Pickaxe book that goes in this direction a bit (Duck Typing/Classes Aren't Types), but I'm wondering if there's anything else. I'm aware of Design Patterns in Ruby but haven't read it.
EDIT -- It has been argued that Liskov doesn't apply the same in a dynamic environment as it does in a static environment, but I can't help thinking that it does. On the one hand there is no high-level contract with an entire class. But don't all calls to any given class constitute an implicit contract that needs to be satisfied by child classes the way Liskov prescribes? Consider the following. The calls in "do some bar stuff" create a contract that needs to be attended to by child classes. Isn't this a case of "treating a specialized object as if it were a base class?":
class Bartender
def initialize(bar)
@bar = bar
end
def do_some_bar_stuff
@bar.open
@bar.tend
@bar.close
end
end
class Bar
def open
# open the doors, turn on the lights
end
def tend
# tend the bar
end
def close
#clean the bathrooms
end
end
class BoringSportsBar < Bar
def open
# turn on Golden Tee, fire up the plasma screen
end
def tend
# serve lots of Bud Light
end
end
class NotQuiteAsBoringSportsBar < BoringSportsBar
def open
# turn on vintage arcade games
end
end
class SnootyBeerSnobBar < Bar
def open
# replace empty kegs of expensive Belgians
end
def tend
# serve lots of obscure ales, porters and IPAs from 124 different taps
end
end
# monday night
bartender = Bartender.new(BoringSportsBar.new)
bartender.do_some_bar_stuff
# wednesday night
bartender = Bartender.new(SnootyBeerSnobBar.new)
bartender.do_some_bar_stuff
# friday night
bartender = Bartender.new(NotQuiteAsBoringSportsBar.new)
bartender.do_some_bar_stuff