The right answer involves a lot of, "Not quite," with a sprinkling of, "You have a point."
First let's take a literal interpretation. Ruby has class methods and instance methods. Class methods are the methods of the class object. Instance methods are the methods of instances of that class. Inheritance is a relationship between class objects - calling .superclass
on the one class gives you another. However that relationship between classes is NOT strictly speaking a subtype relationship. And therefore the Liskov Substitution Principle applies only to instance methods, not class methods. So having different class methods for classes in an inheritance tree is not a violation of the principle.
You can demonstrate this to yourself by creating independent subclasses of Class
, create an object foo
of the one class. Create an object of the other class, using MyClass2.new(foo)
when you create it to make it inherit from the first. And now you see very explicitly that one class can inherit from another class while those class objects are different types of things.
That's the "not quite" answer. Now for the sprinkling.
In the original usage, a "type" is a contract for what operations are supported on an object. In statically typed languages, the contracts are explicit - the compiler will not let you try to pass an object of the wrong type to function that knows what it wants.
However Ruby uses duck typing. You can pass anything anywhere, and as long as it responds to the right methods or does the right thing, things will work out. This makes the contract of a "type" implicit, not explicit. In particular "type" does not always mean class. (It often does, but not always.) And the real relationship of a type is based on what operations are in the code.
So if your code specifies a contract for what a plugin does, any class that implements that contract is of the right type. If your code does meta-programming and calls .class
to get the class, then starts doing things with the class, class methods on the object ARE part of what makes a type a type. If your code uses introspection to discover all classes whose name follows a given convention and does something with them (people do stuff like this with unit test frameworks) then the naming pattern of the class IS part of your type.
Think of this as, "A type is a contract, and the contract is implicitly defined by what operations are in the code."
If you think about it that way, Ruby's dynamic features allows pretty much anything to become part of the type. And what the Liskov Substitution Principle applies to depends on what dynamic features you choose to use. Furthermore in any complex piece of Ruby software - including its core classes, it is easy to write new code which finds that you're violating the Liskov Substitution Principle.
This sounds scary. What does it mean in practice?
It means that you need to be careful when adding complex metaprogramming to an existing system. It also means that you should be clear for yourself what kinds of software contracts you maintain. And, assuming you're not doinig complex metaprogramming, you need to pay more attention to the Liskov Substitution Principle on instance methods than class ones.