169 похожих чатов

Class Bird { fly () { } } class WaterFowl

extends Bird {
swim () {}
}

class Duck extends WaterFowl {}
class MallardDuck extends Duck {}
class MottledDuck extends Duck {}
class BlackDuck extends Duck {}
class PekinDuck extends Duck {}

class Swan extends WaterFowl {}
// ...

class Goose extends WaterFowl {}
// ...

class GameFowl extends Bird {}

class Pheasant extends GameFowl {}
class Quail extends GameFowl {}
class Partridge extends GameFowl {}
Your system is amazing, all of your pieces fit together perfectly, and all of your classes are super-tiny, because they’re all inheriting the functionality they should be, all the way up the tree.

6 months in, your manager asks you for two new birds:
Emu
Cormorants

What now?
Keeping in mind that practically all Cormorants fly, and feed by diving.
…practically all…

One sub-species of Cormorants, Flightless Cormorants can not fly (as the name might suggest).

You already have a taxonomy of hundreds of classes, in an intricately laid out system.

And your type-system in your code is working perfectly. Just by genus, or by family, or what have you, all of the branching subclasses of that root class (itself a branch of some other thing) can pass right through…

…now what’s going to happen to your taxonomy… the topology of your tree…
…how easy it will be to trust that once your tree gets shuffled and lopsided, that things flow through all of your typed method calls as easily as before? Will you eventually find yourself having to write methods like

public Boolean checkCollision (Bird a, Bird b) { /* ... */ }
What kind of bird? It might be really easy to check if one is flying and the other can’t fly… …but that requires crazy overrides, and basically everything can flow through because you’re just checking “Is it an Object?” in order to accomplish things in your type system, now.

This is frequently how game engines end up.
Everything is an instance of some descendant of GameObject so that worst case, your data can still be passed around.

So if this is the BAD way to look at things, what’s the better way?
Traits (Mix-Ins).

Languages like C++, Go, Scala (and of course, JS) allow you to define multiple sets of functionality to extend onto yourself.

What if, instead of this hierarchy, you had multiple objects with methods that you borrowed, instead?

// not valid JS
trait Flyer {
fly () { }
}

trait Swimmer {
swim () { }
}

class Bird {}

class Duck extends Bird, Swimmer, Flyer { }
class Penguin extends Bird, Swimmer { }
class Emu extends Bird { }

class GoldenRetriever extends Dog, Swimmer { }
Now it all takes care of itself.

We are composing our objects, rather than inheriting deeply.
Our type system now cares about things which we say our object can do, not what we say our object’s ancestry is.
If I had every skill of all of my ancestors, I’d be unstoppable.
More to the point, now my kid and I can have different strengths and weaknesses, and its okay.

When Crockford gives talks these days, talking about “Parasitic Inheritance” or something of the sort, he’s really talking about mix-ins/traits (without any keyword support for them in JS, unlike classes).
FunFunFunction’s mpjme has a great solution to my Duck+Penguin problem, or what he refers to as a MurderRobotDog.

I hope this helps you understand why people are wary around “class”.
It’s not that it can’t be used well, and can’t be used responsibly…
…it’s just that the vast majority won’t, and then they’ll complain about it, when their stuff breaks.

2 ответов

14 просмотров

The last part there, about traits

Dude put this on theDevs blog

Похожие вопросы

Обсуждают сегодня

а через ESC-код ?
Alexey Kulakov
29
30500 за редактор? )
Владимир
47
Чёт не понял, я ж правильной функцией воспользовался чтобы вывести отладочную информацию? но что-то она не ловится
notme
18
У меня есть функция где происходит это: write_bit(buffer, 1); write_bit(buffer, 0); write_bit(buffer, 1); write_bit(buffer, 1); write_bit(buffer, 1); w...
~
13
Недавно Google Project Zero нашёл багу в SQLite с помощью LLM, о чём достаточно было шумно в определённых интернетах, которые сопровождались рассказами, что скоро всех "ибешни...
Alex Sherbakov
5
program test; {$mode delphi} procedure proc(v: int32); overload; begin end; procedure proc(v: int64); overload; begin end; var x: uint64; begin proc(x); end. Уж не знаю...
notme
6
Как передать управляющий символ в открытую через CreateProcess консоль? Собсна, есть процедура: procedure TRedirectThread.WriteData(Data: OEMString); var Written: Cardinal;...
Serjone
6
вы делали что-то подобное и как? может есть либы готовые? увидел картинку нокода, где всё линиями соединено и стало интересно попробовать то же в ddl на lua сделать. решил с ч...
Victor
8
Ребят в СИ можно реализовать ООП?
Николай
33
Подскажите пожалуйста, как в CustomDrawCell(Sender: TcxCustomGridTableView; ACanvas: TcxCanvas; AViewInfo: TcxGridTableDataCellViewInfo; var ADone: Boolean); получить наз...
A Z
7
Карта сайта