• BeenThere (unregistered)

    Good old historicaly grown frist.

  • Chris (unregistered)

    In all fairness, this sort of inheritance mess can, for the most part, be implemented in something like C#. I've had fun times wading through things like nested factory methods and global variables set by configuration services, working my way down through several layers of interfaces and abstraction just to work out which concrete classes, and which instances of those classes, are being used for various things, with which parameters.

    Then you get Java and the Spring framework. Maybe my limited experiences have been with some horrible abuses of that system, or maybe that's how it's meant to look, but I can't say I've ever enjoyed working with it.

  • trainbrain27 (unregistered)

    TRWTF is scroll bars on two line code snippets.

  • (nodebb)

    While I agree that Chris raises valid points....those situations are almost certainly a result of "leakage".... As an example, if you encounter an interface, you should never need to look behind at the concrete classes. Instead the concrete classes should be independently (and thoroughly) tested to ensure they meet the criteria of the interface always.... This separation of concerns requires some solid work (pun intended) but have a great payoff.

  • (nodebb)

    My mom always warned me to stay well away from snakes.

  • (nodebb) in reply to TheCPUWizard

    My everyday practice it hunting throughout the whole codebase to understand why a global variable changes the way it changes, how I meed it to change to get the needed behavior, and how all of this depends on the configuration dependent code path.

    I'd submit our whole codebase here, but I'm pretty sure that would be illegal, if only because Remy would sue me for the psychological damage.

  • (author) in reply to R3D3

    My lawsuit would get thrown out, because it'd be impossible to prove I didn't have a pre-existing condition.

  • (nodebb) in reply to TheCPUWizard

    I think the concept behind "inheritance considered harmful" is that although the feature is well-intentioned and can be used to make code easier to maintain, in the hands of mere mortal developers it often goes sideways.

  • (nodebb) in reply to TheCPUWizard

    It's even more fun to chase down call stacks when every single implementation is the only implementation of an interface. It makes me want to swap F12 and Ctrl-F12 in Visual Studio (go to highlighted symbol/go to implementation of highlighted symbol), because I never want to look at the interface when I'm debugging something.

  • (nodebb) in reply to PotatoEngineer

    every single implementation is the only implementation of an interface

    TBH, that's the current development trend. Although, there should at least be a "true" implementation and a mock used for automated testing. The lack of the latter could be due to a lack of testing, or maybe they're using a testing technology that builds mocks on the fly.

    If I had to take over a codebase, I'd rather see everything already having an interface than needing to do that as I start testing things.

  • Sole Purpose Of Visit (unregistered) in reply to Jaime

    As Remy observes, "inheritance" can mean many things, most particularly in the case of languages with annotations and such that enable you to access the grobbly bits underneath.

    But, absent multiple inheritance (please, absent it), inheritance is a perfectly simple concept: it's basically an interface with optional concrete default method instantiations. (Yes, it's taken C# until v8.0 to get this far with implementations, but better late than never. Strangely, about four versions later than VB, but I digress.)

    The "considered dangerous" bit comes into play when lazy programmers go more than a single level deep. I don't think you can realistically use Liskov Substitution when you've got LiveThing => Fauna => Vertebrate => Mammal => Dog => Stupid Artificial Yapping Cross Breed In A Gold Lame Purse, but people try to do that. And that's where you tend to get these ludicrous type-based switches in the middle of an inheritance chain.

    I thought the comments were wonderful, by the way. They got more wonderful as they went along.

    Anyhow. You know those things we (of a certain age) used to use as addresses, when kids? Me, 123 My Road, My City, My State, My Country, My Continent, My World, My Solar System, My Galaxy? Well, that's basically what people are doing when they use deep-diving inheritance trees. Nobody ever needs all that information baked in to one spot, and if they do, they should use a look-up table or something. Composition is also good, although in this case composting might be preferable.

    I once had the pleasure of working with a C++ system that almost literally did this same thing: MyDoingThingsClass => DefaultDoingThings => DoingThingsInMyProgram => DoingThingsOnMySystem => DoingThingsOnMyMachineInstance => DoingThingsOnMyHardwareArchitecture.

    They would have added DoingThingsOnMyNetwork, but luckily the thing wasn't networked. Curiously, all it really did was to spit out a bash shell program to run things in batch.

    Guess how much of that hierarchy survived the rewrite ...

  • Randal L. Schwartz (google)

    And people say Python is far easier than Perl? /me chuckles

  • MiserableOldGit (unregistered)

    Couldn't this happen in pretty much any language where a developer can make a stab at inheritance/polymorphism?

    Other than a syntax I'm not that familiar with it looks a lot like what happens when someone dives in thinking this approach is the magic multi-widget solution to everything, except they don't actually really know what their problem is yet. By the time they find out they implemented the whole thing downside up and bass-ackwards it's too late to throw it away and start again. (Un)fortunately we have a developer skilled enough to code forward out of the mess, but not mature/senior enough to go back to the project manager and say he'd made a mistake and needed a couple more days to rework something.

    I'm sure I've seen loads of this, hell, if I'm honest I probably did it a couple of times.

  • (nodebb)

    There's another (maybe) pythonic WTF (which coming from java I fell for it at first time).

    class KnownModelParametricUnivariateFitter(ParametricUnivariateFitter):
    
        _KNOWN_MODEL = True
    

    the _KNOWN_MODEL is the equivalent of a "static" in java and not an instance variable (it should have been declared in the constructor as self._KNOWN_MODEL to be so). I have the feeling the original developer fell for that also and resorted to that juggling constructor wrapping to change some statics on the classes when constructing them...

  • some ron (unregistered)

    While I agree that overengineering is a thing that exists, because I've seen and done it, you have to be careful, before you complain about a library.

    One reason not to complain is that the author internally uses things that you don't know. Those are internal and if the author is better versed in the language than you are, that is not his fault. Otherwise we should all be writing Commodore Basic V2 to have a least common denominator (and it would still be abused horribly).

    You can complain if the author requires the user to use obscure things without properly documenting it. You can also complain if the documentation is lacking, so the only way to find out how to use the library is to dive into its internals.

    You can also complain if the library doesn't do what it is supposed to do or is too slow.

    And finally you can complain, if you can do it better, but only if you cover all the features of the original library. Of course I can write a faster and easier WeibullFitter, if that is all i need. But if I'm writing a GUI application, where the user can select any kind of Fitter, you need some kind of interface.

    Anyway, if you have common operations which wrap some method that is implemented differently for different object, a common choice is using inheritance.

    The problem with "Composition over Inheritance" is that it only holds if you can choose between composition or implementation, but there are enough cases, where composition does not work or is so awkward, that you are basically reimplementing inheritance using composition and a lot of copy&paste&adjust.

    Inheritance is not bad. Can it be abused? Sure. But you can abuse anything and you are prone to do so, if you lack wisdom and knowledge. Unfortunately too many programmers do.

  • Sole Purpose Of Visit (unregistered) in reply to Randal L. Schwartz

    Much though I love Perl, I prefer Python. Perl was basically a bridge between sh in-builts and a proper language. (As you can see from most .rc files in the 1990s and 2000s.)

    As an intuitive programming language, it sucks. Auto-vivifying things that the programmer didn't necessarily expect is absolute evil. And I forget how many contexts you can operate within -- is it five, or six? Hard to keep track of, even though you're normally only in the one.

    Really great libraries though. Whenever I picked one off CSPAN, I pretty much knew what it would do. (Compare and contrast to libraries in other languages today, although in fact Python doesn't do badly.)

    I did find the Schwartzian Transform very useful, though. Not so much in modern languages with lazy evaluation. But really useful in Perl.

  • WTFGuy (unregistered)

    @TheCPUWizard ref

    ... Instead the concrete classes should be independently (and thoroughly) tested to ensure they meet the criteria of the interface always.... This separation of concerns requires some solid work (pun intended) but have a great payoff.

    If only interfaces were semantic contracts, not simply syntactic. You can't actually test that an implementation meets an interface. Absent full code contracting, you can test that an implementation meets an interface to the extent of not exposing it it or using it unsyntactically. Everything about behavior is just a large list of requirements that aren't documented anywhere except the requirements doc and maybe the test suite. Or failing that in or (worse yet by) the actual implementation code in question.

    Further, the need to dig into the innards should not be required if a) it's 3rd party code AND b) it's flawless. If either of those things are not true, it becomes your responsibility to dig as far as it takes to fix the problem you're assigned that is emergent behavior of something somewhere.

  • Airdrik (unregistered)

    Some more python nuance for you: Python has 3 different ways you can declare methods in a class. The default is instance methods which pass the instance into the method as the first parameter, traditionally named "self". Then there are two annotations which can be used to declare static-like methods: @classmethod and @staticmethod. The difference is that @classmethod will pass the class into the method as the first parameter, traditionally named "cls" while @staticmethod doesn't pass in any additional parameters beyond those provided by the caller. An implication of using @classmethod instead of @staticmethod is that something along the way could in theory extend the class and override class methods to provide different behavior.

    However the class in question is an enum and enums in python (that have members defined) are not subclassable (https://docs.python.org/3/library/enum.html#restricted-enum-subclassing), so the use of @classmethod instead of @staticmethod only adds to the clutter and confusion.

  • mushroom farm (unregistered)

    Looking forward to the 10-part series along the same lines for JavaScript. At least core Python supports full-fledged classes. Every time I look at JS/npm I'm like "the world runs on this? we're doomed"

  • (nodebb) in reply to WTFGuy

    If only interfaces were semantic contracts, not simply syntactic.

    They are… but enforcing that from within the language system (compiler, interpreter, whatever) is extremely tricky. Instead, if you have a cow-orker who likes to not follow the semantic contract, enforce the contracts with a Louisville Slugger.

  • Gumpy Gus (unregistered) in reply to Chris

    I quit a job that involved a lot of Spring. It was taking me two weeks to add one integer input field to a form. Then another week to get it to stay put on the page. Life isn't long enough to put up with that kind of nonsense.

  • (nodebb)

    Ugh... My "current" language of the project I inherited is Python, and I also did Python at the gig before that. I don't prefer this language really, and I don't think it lends itself to be easy either. I also really don't like the Python and python library style of documentation. Most of the time I just have to read the source code or step debug to really understand it.

    But, I see this style of work done often in Python. It always seemed that the previous developer discovered a new language feature and just ended up using it, EVERYWHERE. In my current legacy project, the previous developer has made frameworks around almost all of the consumed frameworks. Lots of those are attributed methods. And there are custom exception classes for EVERYTHING, not to mention two categories of exceptions: exceptions, and "usage errors" (basically things like validation exceptions, "value missing", "record already exists", etc...). Well, the custom frameworked framework code will throw exceptions sometimes, things that are "usage error" type exceptions. Well, it's understandable if the exception is one of these, but often a bug report will come in of some requests failing consistently, but normally should work and the conditions for the "usage error" are not applicable. Well, this is usually because a lower exception that was UNEXPECTED is caught by an inner "expected" exception, then caught by a general catch-all the "usage error" exceptions, and continue on.

    As you can probably tell, I waste a lot of time trying to find out what the real exception is.

    When will developers learn, exceptions should be exceptional!

    Don't get me started on how the tests are set up...

Leave a comment on “A Tight Fitter”

Log In or post as a guest

Replying to comment #519831:

« Return to Article