- Isn't the (biological) concept of inheritance built at it's core around the idea of "generations"? How does that make any sense in OOP?
- Why do Java beginner courses still teach Inheritance like it was 1995 with all the bells and whistles and dogs and cats and mammals?
- Why do they teach Inheritance and later introduce "favor composition to inheritance"? Doesn't this just confuse everyone?
- Why does Inheritance have first-class syntax support in an OOP language like Java ("extends") whereas Composition usually needs to be "engineered" using some more complex patterns, e.g. DI?
Most of it is probably for historical reasons, i guess.
In PHP, surely you can use traits.
irb(main):001:0> "bar".foo
Traceback (most recent call last):
4: from /usr/bin/irb:23:in `<main>'
3: from /usr/bin/irb:23:in `load'
2: from /Library/Ruby/Gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
1: from (irb):1
NoMethodError (undefined method `foo' for "bar":String)
Did you mean? for
irb(main):002:0> class String
irb(main):003:1> def foo
irb(main):004:2> "foobar!"
irb(main):005:2> end
irb(main):006:1> end
=> :foo
irb(main):007:0> "bar".foo
=> "foobar!"
irb(main):008:0>
Yes! Let's modify the core library String class on the fly to add new functions to it.There are things about ruby that truly scare me if my goal was to write secure and reasonable code.
Ruby isn’t alone in supporting this functionality (you can fiddle with JavaScript prototypes, for instance), but I think it is unique how much it is encouraged (at least in Rails world). I think extension methods are a much better model to achieve something similar.
The "why Scheme didn't do this": First-class environments - http://funcall.blogspot.com/2009/09/first-class-environments...
why Ruby did (and wasn't a good idea): Ruby Conf 2011 Keeping Ruby Reasonable https://youtu.be/vbX5BVCKiNs
Consider where you can slip in a block invocation and the following code:
def mal(&block)
block.call
block.binding.eval('a = 43')
end
a = 42
puts a
mal do
puts 1
end
puts a
and that that means that you've got full access to be able to inspect and manipulate all of the variables in scope at the time of the invocation. While that example is rather obvious, it can be done much more subtly too.The power of ruby to do meta programming and by extension some really neat DSLs also provides it with some dangerous tools that are otherwise rather difficult to track down.
Of course, in Smalltalk you might have #trim defined by two different packages and they might conflict. Then what? Maybe you have to manage that complexity. However, it's not much different from having conflicting functions in the global namespace in C, but actually easier to resolve in a given situation.
irb(main):001:1* module Awesomeness
irb(main):002:1* def awesome? = :yes
irb(main):003:0> end
=> :awesome?
irb(main):004:0> ?Ô.awesome?
(irb):4:in `<main>': undefined method `awesome?' for "Ô":String (NoMethodError)
from /Users/someone/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/irb-1.4.1/exe/irb:11:in `<top (required)>'
from /Users/someone/.asdf/installs/ruby/3.1.2/bin/irb:25:in `load'
from /Users/someone/.asdf/installs/ruby/3.1.2/bin/irb:25:in `<main>'
irb(main):005:0> ?Ô.extend(Awesomeness).awesome?
=> :yes
irb(main):005:0> ?Ô.awesome?
(irb):5:in `<main>': undefined method `awesome?' for "Ô":String (NoMethodError)
from /Users/someone/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/irb-1.4.1/exe/irb:11:in `<top (required)>'
from /Users/someone/.asdf/installs/ruby/3.1.2/bin/irb:25:in `load'
from /Users/someone/.asdf/installs/ruby/3.1.2/bin/irb:25:in `<main>'AFAIK,that's the only big downside to multiple inheritance.
I think the main reason is that DI is seen as an orthogonal concept by language designers, so you'd need new two first-class features in a language.
If you do composition without injection (e.g.: by having new ChildObject() in the constructor), you don't really require that many more lines of code compared to inheritance.
class Car {
private Engine engine;
public Car() {
engine = new Engine();
}
public void drive() {
engine.start();
System.out.println("Driving...");
engine.stop();
}
}
Of course it's much less flexible and less testable than composition + injection, but not that inflexible when compared to inheritance. And first-class support for only that would make the feature a bit useless without DI...OOP inheritance forms a tree, and that is the language of trees (parent, child, ancestor, descendant, sibling).
> Why do Java beginner courses still teach Inheritance like it was 1995
> Why do they teach Inheritance and later introduce "favor composition to inheritance"? Doesn't this just confuse everyone?
Codecademy's course introduces inheritance in module 9 of 11, and calls it "deeper object-oriented" feature: https://www.codecademy.com/learn/learn-java
> Why does Inheritance have first-class syntax support in an OOP language like Java ("extends") whereas Composition usually needs to be "engineered" using some more complex patterns, e.g. DI?
Class members absolutely have first class syntax.
DI is composition with IoC container.
Scala and (apparently) PHP call them traits too, among many others [1] -- Rust wasn't the first here.
[1] https://en.wikipedia.org/wiki/Trait_(computer_programming)