Live data from Hacker News

99 Bottles of OOP now available in Python

sandimetz.com

81–90 of 93 posts

Re: 99 Bottles of OOP now available in Python

#81
post #78

Earlier quoted context omitted.

After making those interfaces, the moment you need to do something that breaks those interfaces, you suddenly have a headache. I wrote a program in college that daemonizes by being an instance of a daemon class. Years later, people wanted the option to not daemonize. With C, this would be easy. With the way that I did things according to OOP principles in C++, I need to delete all of the code related to starting the…

Your problem was a misuse of inheritance, not encapsulation or interfaces.

Misuse of inheritance is often the biggest generator of criticism of inheritance, sadly.

People use the wrong tool for the job, or use it incorrectly, and then blame the tool. It's like using a hammer to play drums, obliterating the drum set, then ranting against hammers.

Re: 99 Bottles of OOP now available in Python

#82
post #68

Earlier quoted context omitted.

But I've read the book, and her solution to the "bottles of beer" problem involves encoding all the logic into an elaborate class hierarchy! I'm not rabidly anti-OOP, but the point at which I turn against it is when the pursuit of "properly" modelling your domain with objects obscures the underlying logic. I feel like this book reaches that point. This is her stance on polymorphism: > As an OO practitioner, when you…

Well, the entire book is focused on solving a laughably trivial problem, any solution is going to feel excessive. The elaborate object hierarchy that she uses would obviously feel different in real world, complicated domain. I found the excerpt in the book and I don't see her mentioning traditional class-level polymorphism (of the Java kind) anywhere around it. What SM generally advocates for is using OBJECT hierarch…

I should clarify that the elaborate class hierarchy in the book is inheritance-based. When there's one bottle of beer on the wall, she instantiates `new BottleNumber1()`, which inherits from `BottleNumber` and overrides the method `container()` to return the singular "bottle" rather than the plural "bottles" which the base class's `BottleNumber::bottle()` would return (in the javascript edition, at least).

Re: 99 Bottles of OOP now available in Python

#83
post #66

Earlier quoted context omitted.

> OOP is an industry of its own which generates a ton of incidental complexity. And that "ton" is still miniscule compared to front-end development which almost completely eschews OOP and has 10x more incidental complexity. I guess my point is that, while OOP's incidental complexity is large, it's still insignificant compared to other technology stacks which developers are showing a great appetite for anyway. Things…

Is your argument that 1. its ok to add incidental and unnecessary complexity 2. so long as it's less complex than your most complex component? Because that's a formula we can all agree leads no where good nor productive.

> Is your argument that

> 1. its ok to add incidental and unnecessary complexity

> 2. so long as it's less complex than your most complex component?

That is not my argument.

Re: 99 Bottles of OOP now available in Python

#85
post #78

Earlier quoted context omitted.

After making those interfaces, the moment you need to do something that breaks those interfaces, you suddenly have a headache. I wrote a program in college that daemonizes by being an instance of a daemon class. Years later, people wanted the option to not daemonize. With C, this would be easy. With the way that I did things according to OOP principles in C++, I need to delete all of the code related to starting the…

Your problem was a misuse of inheritance, not encapsulation or interfaces.

That is exactly what I said you could say:

> You could say that I just did not do it right, but that is the problem. You need to know precisely what the future will want to do it right and that is never possible to know in advance.

Every time inheritance causes a headache, you can call it a misuse of inheritance, but that is only obvious after you have been to the future.

Re: 99 Bottles of OOP now available in Python

#86

Earlier quoted context omitted.

Your problem was a misuse of inheritance, not encapsulation or interfaces.

Misuse of inheritance is often the biggest generator of criticism of inheritance, sadly. People use the wrong tool for the job, or use it incorrectly, and then blame the tool. It's like using a hammer to play drums, obliterating the drum set, then ranting against hammers.

OOP is a tool that causes people to hang themselves with it by design. The only way to avoid misusing it is to be to the future to see the impact of every single decision involving it to make only the ones that do not cause problems.

Re: 99 Bottles of OOP now available in Python

#87
post #82

Earlier quoted context omitted.

Well, the entire book is focused on solving a laughably trivial problem, any solution is going to feel excessive. The elaborate object hierarchy that she uses would obviously feel different in real world, complicated domain. I found the excerpt in the book and I don't see her mentioning traditional class-level polymorphism (of the Java kind) anywhere around it. What SM generally advocates for is using OBJECT hierarch…

I should clarify that the elaborate class hierarchy in the book is inheritance-based. When there's one bottle of beer on the wall, she instantiates `new BottleNumber1()`, which inherits from `BottleNumber` and overrides the method `container()` to return the singular "bottle" rather than the plural "bottles" which the base class's `BottleNumber::bottle()` would return (in the javascript edition, at least).

Inheritance is not a banned practice. It should just be your second choice when there's a better path through composition. Do you see one here? Interfacing to address Liskov's substitution is a perfectly valid reason to "extend" in many older languages, since their inheritance and interface mechanism are conflated. The way it's done here is fine. Single parent, shallow and only for the purpose of overriding and specializing.

Also, the real issue SM is trying to address is actually single responsibility and open-close, which aren't just an OO thing.

As you'll design your own libraries' functional APIs, you'll have to decide whether to publish fewer functions, with a rich set of behaviors controlled through the passing of (many) parameters (and conditionals in the function body); or take a finer grain approach with multiple functions that abide as much as possible to single responsibility and only take few input about the state. I'd bet that the former will quickly raise complaints, by both maintainers and users alike, because of all the ifs and buts typically associated with it.

For the same reasons, you don't want your methods to have divergent behavior based on state. You want multiple types.

Re: 99 Bottles of OOP now available in Python

#88
post #87
post #82

Earlier quoted context omitted.

I should clarify that the elaborate class hierarchy in the book is inheritance-based. When there's one bottle of beer on the wall, she instantiates `new BottleNumber1()`, which inherits from `BottleNumber` and overrides the method `container()` to return the singular "bottle" rather than the plural "bottles" which the base class's `BottleNumber::bottle()` would return (in the javascript edition, at least).

Inheritance is not a banned practice. It should just be your second choice when there's a better path through composition. Do you see one here? Interfacing to address Liskov's substitution is a perfectly valid reason to "extend" in many older languages, since their inheritance and interface mechanism are conflated. The way it's done here is fine. Single parent, shallow and only for the purpose of overriding and speci…

> Inheritance is not a banned practice.

Maybe it should be. Go and Rust do not provide implementation inheritance, and I think that's for the best. Few language features have led to so much spaghetti code.

> It should just be your second choice when there's a better path through composition. Do you see one here?

I don't think this logic should be split over a graph of objects at all. This is highly cohesive code; it shouldn't be factored apart. If Metz made it clear that this was an example just for the purposes of illustration, that'd be one thing. However, the stance taken is "this is good code, and you should try to write all your code like this." It's not, though, and you shouldn't.

Re: 99 Bottles of OOP now available in Python

#89

Earlier quoted context omitted.

In my experience, the mess created using OOP is harder to untangle than the mess created using a functional approach. With the latter, it's simpler to replace a different function for any part of the logic, and the data is always just data; whereas with OOP the method is usually tied up with shared state and other functions.

> In my experience, the mess created using OOP is harder to untangle than the mess created using a functional approach. OP complained about accidental complexity, not subjective takes on how hard it is to refactor code. Even so, anyone who has any cursory experience with TypeScript projects that follow a functional style can tell you without any doubt whatsoever that functional style is incomparably harder to refacto…

> Even so, anyone who has any cursory experience with TypeScript projects that follow a functional style can tell you without any doubt whatsoever that functional style is incomparably harder to refactor than any "enterprise-grade" OOP.

My experience differs greatly. One can see functional style has taken off in the TS world, particularly with the popularity of React, so I suspect I'm not alone.

Re: 99 Bottles of OOP now available in Python

#90
post #88
post #87

Earlier quoted context omitted.

Inheritance is not a banned practice. It should just be your second choice when there's a better path through composition. Do you see one here? Interfacing to address Liskov's substitution is a perfectly valid reason to "extend" in many older languages, since their inheritance and interface mechanism are conflated. The way it's done here is fine. Single parent, shallow and only for the purpose of overriding and speci…

> Inheritance is not a banned practice. Maybe it should be. Go and Rust do not provide implementation inheritance, and I think that's for the best. Few language features have led to so much spaghetti code. > It should just be your second choice when there's a better path through composition. Do you see one here? I don't think this logic should be split over a graph of objects at all. This is highly cohesive code; it…

> Maybe it should be. Go and Rust do not provide implementation inheritance, and I think that's for the best. Few language features have led to so much spaghetti code.

I agree, but there are reasons inheritance becomes problematic. Just throwing the baby is not helpful. Go, Elixir, Rust are all relatively young languages and although they did away with inheritance, they make use of interfaces/protocols/traits, hinting at that idea very much worth preserving. That is, regardless of which language you work with, if you have access to facilities that can make the concept of interface work decently, use them. Older languages (like Ruby, Python, Java) tended to use the inheritance mechanism to accomplish the same.

> If Metz made it clear that this was an example just for the purposes of illustration

She did. Perhaps read the book's introduction. She explains why she wrote a whole book that uses a banal example as a teaching tool to illustrate how SOLID should map to real world OOP. Her painstakingly going through refactoring and providing reasons for each decisions is not, in fact, to teach us to write code that generates a song.

> I don't think this logic should be split over a graph of objects at all [...] However, the stance taken is "this is good code, and you should try to write all your code like this." It's not, though, and you shouldn't.

Fair enough, but these are your very own and very isolated opinions. As an OO skeptic myself, I'll side with others like me, along with the FP enthusiasts, who originally approached this book with reserve, but came out with positive impressions.

Regarding the object graph, whether in Go, Elixir, or Rust, "No Bottle", "One Bottle", "Six Pack", and "Many Bottles" are distinct things and should be represented accordingly. Conflating them is a violation of principles that also apply in those languages. A very common, yet equally banal example that should put the debate to rest about this is the trifecta: Shape.Area(), Square.Area(), and Circle.Area(). Of course, it remains the programmer's prerogative whether they indulge with if/else in their implementation, but it should still be considered the exception rather than the rule.

Post reply on HN