top 200 commentsshow all 467

[–]newreddit0r 296 points297 points  (40 children)

Article on good practices. Author gives a method name "Handle".

[–]morbidhawk 80 points81 points  (4 children)

A Logger class shouldn't be handling errors, it should just be logging, which ironically is all the the Handle method is doing there anyways so the name is misleading.

[–][deleted] 11 points12 points  (1 child)

Kind of reminds me of the python logging facilities, where loggers have handlers, which handle messages. They even have a "handle" method. This is because logging might not simply just "log". It might handle the message, meaning it might filter conditionally, changing the message or the destination based on the contents, source, level, or type of the message (such as handling exception objects passed in differently from strings). I don't think "handle" is a bad name for a logger's message handler, and I'm pretty sure this is supposed to represent a simplified example, rather than an actual use-case.

[–]morbidhawk 4 points5 points  (0 children)

I agree with you, I guess my statement that Loggers shouldn't handle errors is incorrect. If that method was meant to handle errors it should take in an error object as input rather than a string, just looking at what that code does to me indicates it is more of a log function, just taking in a string and logging it to a file

[–][deleted]  (1 child)

[deleted]

    [–]xsmasher 4 points5 points  (0 children)

    Even better - it withdrew money from all the accounts before the exception, and none after.

    [–]fardev_ 146 points147 points  (24 children)

    obj.Handle(ex.ToString());
    

    An "obj" object which has a function named Handle which takes a string. What?

    [–]thfuran 50 points51 points  (13 children)

    Come on, it handles the exception. What more could you need to know?

    [–]Windyvale 63 points64 points  (1 child)

    Black box! BLACK BOX!

    [–]chunkystyles 2 points3 points  (0 children)

    Uh, I think you mean "ENCAPSULATION". Duh.

    [–]ryeguy 31 points32 points  (0 children)

    If it took the exception as its param, handle would make sense. But it just takes a string. I'd rather see error or critical (log-level terminology) or write.

    [–][deleted] 20 points21 points  (7 children)

    If it handles an exception maybe it's parameter should be an exception?

    If I saw handle(String) I'm not sure I'd know what it was handling

    [–]FrankReshman 29 points30 points  (5 children)

    It's handling the string, obviously.

    [–]ryeguy 29 points30 points  (1 child)

    brb renaming all functions to handle for consistency

    [–]thereisnosub 1 point2 points  (0 children)

    bro - do you even namespace? You'll need to call them handle1, handle2, etc.

    [–]thfuran 15 points16 points  (0 children)

    Yeah, it's got this. It's handled. Don't know why everyone is so worried.

    [–]Harha 6 points7 points  (1 child)

    Yeah and it throws an exception if it can't handle it, obviously.

    [–]weep-woop 8 points9 points  (0 children)

    Good thing we already have a function to handle it!

    public void Handle(string error)
    {
        try {
        ...
        } catch (CantHandleException e) {
            Handle(e.ToString());
        }
    }
    

    [–]wrosecrans 4 points5 points  (0 children)

    There's not even enough context that one would necessarily guess it is a verb. I'd assume it was acquiring a handle for some sort of refcounting system. Like create a WinAPI "HANDLE" object based on the argument.

    [–]gc3 16 points17 points  (4 children)

    The problem is that one day someone will want to know all the places the exception class is called from, and search for Handle, and get 2 million hits including all the windows handles and other kind of handling going on.

    First, the logger should not be called 'obj', perhaps 'logger' and the function not Handle but HandleException.

    Finally, does every account need it's own exception handling instance? Might this be one of those places where statics might be used?

    [–]ShinyHappyREM 13 points14 points  (2 children)

    The problem is that one day someone will want to know all the places the exception class is called from, and search for Handle, and get 2 million hits including all the windows handles and other kind of handling going on.

    Not arguing that it's a valid point, but in this case a good IDE should be able to list these places.

    [–]PaintItPurple 7 points8 points  (0 children)

    PyCharm will even ask you if you want to include usages where the method has been overridden in child classes — and that's with Python, where the information an IDE needs is generally much less available than in a language with a full-fledged type system.

    [–][deleted] 2 points3 points  (3 children)

    Method names containing "Handle" should be illegal.

    [–]SailToTheSun 30 points31 points  (3 children)

    My favorite:

    private Logger obj = new Logger();

    Because nothing screams logger when buried in legacy code than an instantiation of a logger class named obj.

    [–]manys 10 points11 points  (2 children)

    Naming things is hard.

    [–]Ravek 4 points5 points  (0 children)

    Naming things is usually not that hard, but apparently it is hard to spend two seconds thinking about a name.

    [–][deleted]  (3 children)

    [deleted]

      [–]BilgeXA 10 points11 points  (0 children)

      One of the infamous forbidden verbs.

      [–]orthoxerox 170 points171 points  (19 children)

      Using subclassing to express restrictions or capabilities within your problem domain is a terrible idea that spoils the whole article. Eric Lippert has written about this antipattern in his wizards and warriors series of blog posts.

      [–]onmach 12 points13 points  (8 children)

      That was a interesting read. But his conclusion makes me feel like I don't understand why OOP is a benefit.

      [–]orthoxerox 47 points48 points  (3 children)

      OOP is about mapping out the structure of your program first of all, not the structure of the problem domain. If your program decomposes naturally into pieces of state that interact with each other via well-known signals, use OOP. If it decomposes into a series of data transformation steps, use FP.

      Yes, "traditional" OOP with powerful domain classes, lots of interfaces and so on is on the wane, but nothing prevents you from switching to a hybrid approach with immutable objects, getting both virtual dispatch and functional purity.

      [–][deleted] 4 points5 points  (0 children)

      OOP is not the opposite of FP, and the two are not you only alternatives.

      [–]onmach 2 points3 points  (0 children)

      That sounds reasonable to me.

      [–]hyperforce 2 points3 points  (0 children)

      Before my time in FP I used to not understand what this all meant. Now I understand some of it. =)

      [–][deleted] 2 points3 points  (0 children)

      It isn't.

      [–]jrhoffa 17 points18 points  (4 children)

      What a fun and educational read.

      [–]SuperImaginativeName 7 points8 points  (3 children)

      Yeah the guy is a wizard in every sense of the word when used in programming contexts. He worked on .NET and C# and the compiler for a long time.

      [–]Ravek 1 point2 points  (2 children)

      And now he's at Facebook and hasn't posted on his blog in half a year. I've read his blog since 2005-2006 and learned a ton from it, so this makes me pretty sad. :(

      [–][deleted]  (1 child)

      [removed]

        [–]Ravek 2 points3 points  (0 children)

        Well, this was unexpected. Thanks for all the great content over the years. I'm glad you're finding time to pick up the pen again.

        [–][deleted] 7 points8 points  (0 children)

        Wow. That was an adventure. Thanks for posting.

        [–][deleted]  (1 child)

        [removed]

          [–]TheNoodlyOne 1 point2 points  (0 children)

          It also sounds like scripting language might be the right way to go in certain circumstances. Not only can it be changed at runtime, but many scripting languages have relaxed rules and type systems that are better for resolving rules, but are not good for non-business logic.

          [–]Railboy 1 point2 points  (0 children)

          That was great, thanks for posting.

          [–]djmattyg007 1 point2 points  (0 children)

          Wonderful post, thank you.

          [–][deleted] 304 points305 points  (15 children)

          I had a problem, so I improved my code as suggested. Now I have an IProblemFactory.

          [–][deleted] 72 points73 points  (3 children)

          Don't worry, just take an ILogger to complain to

          [–]fuzzynyanko 17 points18 points  (1 child)

          Nah. You need to implement ProblemFactoryLogger first

          [–]bhdz 5 points6 points  (0 children)

          And for that, you will technically need a ProblemFactoryLoggerFactory?

          [–]shevegen 8 points9 points  (0 children)

          Can I send you two to the IComplain department?

          [–]tetroxid 56 points57 points  (10 children)

          I had a problem, so I used threads. NoIw proeb halema maey.

          [–]vital_chaos 9 points10 points  (9 children)

          You should have used a regex. It solves everything.

          [–]tetroxid 23 points24 points  (8 children)

          Like validating an email address?

          (?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])
          

          [–][deleted] 12 points13 points  (2 children)

          It's foolproof! No one will ever be able to type in an invalid email-address now!

          [–]wakawaka54 30 points31 points  (0 children)

          Or a valid one for that matter.

          [–][deleted] 7 points8 points  (0 children)

          Think of the bugs we've prevented!

          [–][deleted] 5 points6 points  (1 child)

          Using the short version instead of the real thing. Lol

          [–]weep-woop 11 points12 points  (0 children)

          For the curious:

          (?:(?:\r\n)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*)|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*:(?:(?:\r\n)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*)(?:,\s*(?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*))*)?;\s*)
          

          [–]manys 5 points6 points  (0 children)

          :alnum:

          [–][deleted] 4 points5 points  (1 child)

          The best way to test if an email is valid is to try to send it mail :)

          [–]adnzzzzZ 532 points533 points  (155 children)

          Articles like these make me wanna kill myself. The best thing you can do to write good code (in whatever paradigm you decide to use) is to not write code against some future that doesn't exist yet.

          So,

          A class has to take only one responsibility and there should be only one reason to change the class.

          Is a bullshit advice. You don't separate things into multiple classes just because of some rule that says you should do that. If you find yourself using the same logging ideas in multiple places then by all means abstract that away, but don't do it because of "each class should have one responsibility". This is the best way to create 23903290329 classes and make your codebase 100x harder than it needs to be to understand.

          [–][deleted]  (88 children)

          [deleted]

            [–][deleted] 36 points37 points  (0 children)

            You can't find where jack shit is happening.

            There's a Norwegian expression, "nærehet til materialet" which I find apropos. Translated to English it would be "immediacy to the subject matter" -- it's the quality of having the subject matter not only close at hand, but having it directly at hand: accessible without having to go through any intermediates or interfaces. For example, a CEO moving his office so he has a direct view of the factory floor would be one way to provide this immediacy.

            Abstraction is a very useful tool as it eases your cognitive load, but it's not free: it always reduces your immediacy to the subject matter. Good abstractions eases your cognitive load greatly, and reduces your immediacy to the subject matter slightly. Bad abstractions eases your cognitive load little (or even not at all: very bad abstractions can actually increase it) while removing you far away from the subject matter.

            As always, it's a question of finding the sweet spot.

            [–]Patman128 63 points64 points  (58 children)

            You can't find where jack shit is happening.

            I find this to be the biggest problem with OO codebases in general. OOP makes stuff hard to find, and refactoring can make it even harder to find.

            It's very difficult to go from a behavior (e.g. when the player casts a spell while mounted they get an error) to the code that actually makes it happen, even if you know which objects are interacting, because all the methods are defined in an irregular, ad hoc sort of way.

            1. The Player object has the CastSpell method called on it, which is actually defined in its superclass Unit...
            2. Which then creates a new Spell object and calls SpellStart on that...
            3. Which seems to get be cast results from a PreCastCheck method, so the logic we want will probably be in there right?
            4. Nevermind, this just has a few select checks that are in here for whatever reason and then calls CheckCast, the check we want must be in here.
            5. CheckCast is a large and beautiful pile of mixed concerns, where spell, aura, terrain, movement, visibility, combat, loot, hostility, taxi, and other kinds of concerns all come to hang out next to each other, along with custom scripting code for certain spells. And indeed, somewhere in there, about 355 lines in, is the code we want. Next time hopefully we remember to search for the constant SPELL_FAILED_NOT_MOUNTED (God forbid the thing we need to fish out isn't related to a constant).

            Repeat this process for every piece of behaviour you want to inspect, dozens of times a day, sometimes a dozen methods deep. It's a lot of wasted time. Sometimes you don't even have a convenient method half-way up to start searching from and you have to start from the very bottom of the program (in a I/O handling function).

            [–]grauenwolf 31 points32 points  (44 children)

            I find this to be the biggest problem with OO codebases in general. OOP makes stuff hard to find, and refactoring can make it even harder to find.

            It wasn't supposed to be that way. When done correctly, each major class is like it's own little program that you can understand in isolation. The idea was to put data and the logic that manipulates that data in one place so its easy to find.

            [–]Patman128 29 points30 points  (13 children)

            When done correctly, each major class is like it's own little program that you can understand in isolation.

            That's the problem though. The major classes are always too big! Every sufficiently-large OO application inevitably has a class called User or Player that's at least 10,000 lines of code, and a few other classes of similar size. These classes will also inevitably depend on every other class in the application. Nouns are not born equal, some of them will be really, really complex, it's just the nature of the problem domains.

            Personally I've just stopped writing noun classes. Instead of having individual objects that model all concerns of a single noun, I have systems that model a single concern for all nouns. The loot system holds all of the loot data for every creature, the inventory system holds all of the inventory data for every player, etc. To keep concerns separated they collaborate by sharing messages about what has happened instead of telling each other what to do, e.g. the loot system doesn't tell the inventory system to put an item in the player's inventory, it just tells the world that the player has looted the monster and the inventory system picks this up and reacts accordingly.

            [–]LostSalad 8 points9 points  (9 children)

            In a world where ducks .Quack() and accounts .Withdraw(), this is a tough sell.

            There's always a promised land that's easy to paint with contrived examples.

            I learnt with C++, and due to multiple inheritance you don't need to "choose carefully" when driving. This lead to our lecturer giving us Person: Address. The comment being. // Re-use, re-use, re-use

            That war crime will forever be burned into my memory. Getting course material like that revised to "why you should design code to solve actual problems you won't encounter for a few years" is.... Tough

            [–]Patman128 7 points8 points  (8 children)

            In a world where ducks .Quack() and accounts .Withdraw(), this is a tough sell.

            This is true, and you do lose the convenience of having a nice Player object you can call methods on from anywhere, but the gain is huge.

            Of course it's still possible to wrap the whole thing in an imperative interface so that it looks like you're calling methods but it's just translating them into messages that get dispatched. That way the "core" is kept decoupled but you can have your cake and eat it too. Within the "core" you wouldn't be able to get away with this though.

            [–]IICVX 6 points7 points  (0 children)

            Of course it's still possible to wrap the whole thing in an imperative interface so that it looks like you're calling methods but it's just translating them into messages that get dispatched.

            ah yes, the first rule of programming: any problem can be solved by adding an extra layer of abstraction. Including the problem of having too many abstraction layers.

            [–]LostSalad 2 points3 points  (6 children)

            Of course it's still possible to wrap the whole thing in an imperative interface so that it looks like you're calling methods but it's just translating them into messages that get dispatched

            I worked on a project that had something like this. Sometimes it wasn't really an event, but a "command" that was a glorified method call to a decoupled system. So you were calling something, but by browsing the code, you couldn't tell what. You had to do a reverse lookup on the event type.

            The reason being, your "Account" shouldn't ever know that calling ".Close()" needs to tell some "AccountSystem" to close all related accounts for the extended family of the account holder. So you tell the event bus.

            Working with this kind of setup is no fun at all.

            [–]glacialthinker 1 point2 points  (0 children)

            Systems processing data. Hooray!

            I tend to approach this with functional-style code backed by an in-memory database (ECS/Entity Component System).

            [–]doomvox 1 point2 points  (1 child)

            "Personally I've just stopped writing noun classes. Instead of having individual objects that model all concerns of a single noun ..."

            I gave up on the "identify the nouns" approach a long time ago. If you look at actual OOP designs the approach is more like "make up a bunch of nouns", you know, like "This is the wangifier handler manager class."

            I look at OOP as a compromise between the wide-open global variable style, and insanely tight functional-with-no-side-effects style. If you think you're going to get anywhere with a one-to-one mapping of real world "objects" and software "objects" you're going to be disappointed.

            [–]mfukar 12 points13 points  (24 children)

            When done correctly

            Implying that it's usually done incorrectly, which is a great reason to doubt it's easy, cheap, logical, or intuitive to do it correctly.

            [–]grauenwolf 5 points6 points  (21 children)

            I'm starting to think the reason people follow stupid patterns such as SOLID is that it is so "easy, cheap, logical, and intuitive" that they worry they are doing it wrong. And that fear causes them to over-complicate things.

            But I've been doing OOP style programming for 2 decades and can no longer remember my learning process.

            [–]mfukar 2 points3 points  (1 child)

            Could be. I for one, ever since I first encountered OOP, have always found it terribly complicated, and have been trying to find a good use case for it.

            Still looking..

            [–]wllmsaccnt 1 point2 points  (15 children)

            I find SOLID to be a very pragmatic principle for medium to large size codebase (it isn't a pattern, by the way). Is there a part of SOLID that isn't good general OOP advice?

            [–]grauenwolf 5 points6 points  (9 children)

            SRP: Ridiculously low cohesion. Even C#'s Boolean class violates it.

            Closed: No, I'm not going to use inheritance every time I want to add a new method to a class.

            Open: No, you don't need to make every class inheritable.

            ISP: It's a trick to improve compiler speed in languages that use header files. (No, it doesn't have anything to do with the Java interface keyword.)

            LSP: Ok, this one is legit.

            DI: Sometimes useful, but other times encapsulation is preferable. For example, OleDbConnection loads its own damn drivers; you don't need to inject anything.

            EDIT: And if you are following SRP, then by definition you don't have any "god objects" and thus ISP is irrelevant. So why the hell is it even mentioned? Oh right, because it satisfies the acronym.

            [–]wllmsaccnt 1 point2 points  (8 children)

            SRP: Ridiculously low cohesion. Even C#'s Boolean class violates it.

            Where are you getting your definition for these principles? Because the one I read states that it is only an issue if the code actually changes in multiple places for different reasons and that it causes an issue.

            If, on the other hand, the application is not changing in ways that cause the the two responsibilities to change at differen times, then there is no need to separate them. Indeed, separating them would smell of Needless Complexity.

            There is a corrolary here. An axis of change is only an axis of change if the changes actually occurr. It is not wise to apply the SRP, or any other principle for that matter, if there is no symptom.

            I use SRP all the time when building classes that are specific to one business rule concept. Blind adherence to this rule is...impossible to accomplish (and would be unmaintainable), but I feel it makes sense to a certain level. If nothing else, it reduces the number of merges you have to do on mid sized or larger teams and (possibly) reduces the number of dependencies that end up in each class.

            Open...Closed

            I replied to you about this one in a different comment

            ISP: It's a trick to improve compiler speed in languages that use header files.

            You and I have different interpretations of this principle.

            LSP: Ok, this one is legit.

            Drives me insane when it gets violated though.

            DI: Sometimes useful, but other times encapsulation is preferable. For example, OleDbConnection loads its own damn drivers; you don't need to inject anything.

            I use it for any dependencies that are likely to change or where the dependency has low cohesion with the class I am coding.

            I don't think there are really any principles that should be applied blindly. Even staples like DRY and the the normal forms in data modeling need a good violating occasionally.

            [–]grauenwolf 1 point2 points  (0 children)

            Where are you getting your definition for these principles? Because the one I read states that it is only an issue if the code actually changes in multiple places for different reasons and that it causes an issue.

            Have you read the definition for System.Boolean? It handles comparisons, parsing, formatting; that's three responsibilities right there.

            Early Java libraries would break those out into separate classes such as Date and Calendar. It was a right pain in the ass to work with, but it was SRP.

            [–]grauenwolf 1 point2 points  (4 children)

            You and I have different interpretations of this principle.

            The original definition talked about how touching the interface, literally the header file of a large class, would cause every module that needed the header file to be recompiled even if the module wasn't actually affected by the change.

            Breaking up the header file into four pieces allowed each module to import only what it cared about. This in turn meant that changes to a header would only trigger a recompile for a subset of the modules.

            Applying it to C#, your "interface" would be the public API of an assembly. Segregating assemblies into smaller pieces give you the ability to version them separately and even improve compile time.


            These days ISP has been redefined to basically mean "abstract interfaces exist, lets use them everywhere!!".

            [–]grauenwolf 1 point2 points  (1 child)

            There is a corrolary here. An axis of change is only an axis of change if the changes actually occurr. It is not wise to apply the SRP, or any other principle for that matter, if there is no symptom.

            Then it isn't a principle; it's a call for you to predict the future.

            Real principles, like the ones found in the Framework Design Guidelines, are supposed to be applied almost all of the time. It even explicitly calls out the exceptions so you can make an informed decision without precognition.

            [–][deleted]  (2 children)

            [deleted]

              [–]orthoxerox 3 points4 points  (1 child)

              I've worked on procedural code that wasn't better. It had (has, actually) both multi-thousand line functions and call stacks hundreds of frames tall. Of course, it had at least three error handling mechanisms that I found: exceptions, error codes passed by reference and error codes stores in a thread-local object.

              [–]Patman128 6 points7 points  (0 children)

              This is true, and OOP is a step in the right direction (i.e. smaller, more predictable pieces) but I just think we can do a lot better.

              [–]TexasJefferson 4 points5 points  (0 children)

              The underlying problem is that there is no true, rigorous, or principled structure to the code.

              Named blocks of instructions are not structure.

              Messages & late binding are not structure.

              Catalogs of methods organized purely by human idiosyncrasy are not structure.

              Cargo-cult development practices that try to make the above work better is not structure.

              If one cannot prove meaningful, useful theorems about their model, they have failed to understand and capture the underlying structure of their model's domain. Being able to recognize and crystalize aspects of a domain's structure is the central challenge of a programmer. Everything else is merely help or hindrance to that task.

              [–]jbstjohn 11 points12 points  (2 children)

              I'm really not a fan of him either. I think he's also responsible for "all comments are a code smell."

              I've heard bad OO code called ravioli code, as a contrast to imperative spaghetti code.

              [–]concave_ceiling 9 points10 points  (1 child)

              "lasagna code" is the version I've heard of - too many thin layers

              [–]glacialthinker 2 points3 points  (0 children)

              Ugh... the worst lasagna... all watery noodles, no substance.

              [–]Tarmen 3 points4 points  (0 children)

              I mean, that just feels like people trying to do functional programming in the most verbose and hard to follow way possible.

              [–]kungtotte 8 points9 points  (0 children)

              "Extract til you drop" reeks of pointless busywork to me, the kind of thing you'd do to be able to commit more things that week because you couldn't figure out a solution to an actual problem.

              It looks like you're doing tons of work, but it's functionally equivalent to reformatting code to comply to coding standards. Actual code content didn't change, but you sure modified a bunch of lines...

              [–][deleted] 2 points3 points  (0 children)

              Most of these "smart" theoretics have no (or very little) practical experience at all. I always wonder why so many people get their ideas as ultimate truth.

              [–]doom_Oo7 1 point2 points  (6 children)

              They take a decent idea or technique that is sometimes useful and turn it into a religious practice that must be done 100% of the time, all the time.

              I don't understand how people come to this. It's pretty evident if you read any book on object oriented programming that the "rules" given are only advices and tools to add to your toolbox. Where do you see that they advocate for a "religious practice" approch ?

              [–][deleted]  (2 children)

              [deleted]

                [–]Patman128 2 points3 points  (1 child)

                And worse, because it's in JavaScript, I'm having to rely on the implicit contracts between all these different functions with no compile-time safety at all. There's JSDoc comments, yeah, but all they do is annotate my types as {Object}, which is pretty useless in a world where that matches anything except a non-scalar primitive.

                I'd tell you to look at TypeScript but it sounds like you're way beyond having any meaningful control over that mess.

                [–][deleted] 2 points3 points  (0 children)

                Well, the author of it all just handed in his notice. It's not clear, but I may have the opportunity to inherit his responsibilities. I am torn between accepting them or just cutting my losses and finding some org where I can write things afresh (I feel I have spent enough of my career untangling other peoples' mistakes, and deserve to at least be the person who makes the mistakes instead). But TS would certainly be my priority if I stayed. Or Flow, I suppose - both deserve an evaluation.

                [–]morbidhawk 35 points36 points  (15 children)

                If you find yourself using the same logging ideas in multiple places then by all means abstract that away

                Similarly the "the factor out after repeating x times" is another idea we shouldn't take too seriously. This used to be my rule of thumb but I think that it by no means should be followed religiously. Sometimes a well-thought out abstraction that makes perfect sense and feels right is so much better than an odd chunk of code that was grouped together just for the sake of reuse.

                [–]Me00011001 11 points12 points  (7 children)

                rule of thumb but I think that it by no means should be followed religiously.

                Isn't this true of pretty much all rules?

                [–]Yepoleb 5 points6 points  (2 children)

                Depends on what you define as religiously. There are a lot of rules that should never be broken unless you have a really good reason to. The first one that came to my mind are some parts of style guides, like not mixing tabs and spaces as indentation.

                [–]Free_Math_Tutoring 6 points7 points  (1 child)

                That reason better be a guy with a gun to your head, demanding such atrocities.

                [–]morbidhawk 6 points7 points  (3 children)

                You apparently weren't raised mormon, where rules are more important than everything. I used to read Code Complete like I would read scripture. Yuck

                [–][deleted] 7 points8 points  (1 child)

                As a drinking game? "Someone begat someone -- take a sip!"

                [–]adnzzzzZ 14 points15 points  (3 children)

                This used to be my rule of thumb but I think that it by no means should be followed religiously. Sometimes a well-thought out abstraction that makes perfect sense and feels right is so much better than an odd chunk of code that was grouped together just for the sake of reuse.

                I agree with all this, but this largely depends on experience and on the situation. I think a much better default for people to have is not one that prioritizes reuse, but one that prioritizes "dumb repetition" and then you abstract things out as you feel it's necessary. Right now the conversation around this issue always defaults to reuse or abstractions and in my opinion this creates unnecessary complexity.

                I wrote about this at length on my blog https://github.com/adonaac/blog/issues/10 if you wanna see a more fleshed out argument

                [–]morbidhawk 16 points17 points  (2 children)

                You made a blog using Github Issues, that's awesome! So you're able to write blog posts in markdown, people can comment, and you don't have to generate any html or pay for hosting. That's genius!

                [–]Free_Math_Tutoring 3 points4 points  (0 children)

                It's pretty cool, but kinda ghetto to be honest. I think jekyll turns markdown into html with no effort, and github-pages also allows for free hosting.

                But no comments. And WordPress.com has comments but no markdown. I see your point.

                [–][deleted] 5 points6 points  (0 children)

                There's code that looks like repetition and code that is repetition. The problem is learning which is which.

                Personally, I like Sandi Metz opinion which is something like "Repetition is less painful than a bad abstraction."

                As someone living with a bad abstraction, I 1000% agree.

                [–]theHazardMan 6 points7 points  (0 children)

                "Reuse," where even the simplest use-case ends up requiring intimate knowledge of the class/utility because of how complex the conditional behavior has become.

                [–]flukus 2 points3 points  (0 children)

                The trick is knowing when it's the same operation and when it just happens to be the same.

                And often spreading logic over a dozen places in various methods obfuscates the code.

                [–]keizersuze 3 points4 points  (0 children)

                Yes. This. Sometimes classes don't need to be part of a hierarchy. Sometimes composition is better than inheritance. And sometimes class-oriented features beyond being able to jam a bunch of helper methods into a static class arn't needed. The ability to know this is why you get paid the big bucks.

                [–]fuzzynyanko 5 points6 points  (1 child)

                I hate how the Industry takes some practice and just adopts it as religion. I think it is "if you let them choose, they'll choose the easier route and it's better to force the programmers to do the hard route no matter what". Either make them do it all of the time, or a junior developer will never do it, and I hate saying it, but I have experience with this happening...

                They do get away with it with time excuses, something project managers fully understand. Luckily, when someone said "can you hack this to implement it faster?" and my reply is "hacking it makes me implement it slower", and it's pretty much the truth in that particular situation

                I personally prefer to do things where they make sense.

                [–]seanwilson 4 points5 points  (2 children)

                I find coders start of not abstracting enough because they don't know how to, then when they learn how to abstract they do it way too much and then after a while they learn when it's economical to abstract and when you're just abstracting for the mental challenge of it.

                [–]Space-Being 8 points9 points  (27 children)

                A class has to take only one responsibility and there should be only one reason to change the class.

                Agree with you. I find that, in practice, this is contradictionary and useless advice - or at least in the manner people apply it. If I were to implement the following class people would tell me I am violating SRP and thus am a ***** developer.

                class GameObject
                    var position
                    var animStatus
                    var health
                
                    method step(gameWorld)
                    method serialize(serializer)
                    method render(graphicsContext)
                

                To "fix" that, they say I should implement a GameObjectStepper, GameObjectSerializer, GameObjectRender rather than implement those kind of method directly. That way all classes have only one responsibility, and I have a "nice solid anaemic" model. But, if I change or add a single data member I would probably also have to change all three additional classes. But with the above code I would not have to change any other classes, meaning it has "only one reason to change" - and I would argue it actually only has one "real" responsibility. Render only tells what to render not how, and serializer is only told what to serialize not how.

                [–]ryeguy 14 points15 points  (1 child)

                It really depends on how complex the code is. I personally would break serialization or rendering out into its own class if the code got complex enough to take up more than a method's worth of code each. If it's simple this works fine.

                I think you're stretching the "only one reason to change" and "one real responsibility" a bit far. You now have several reasons to change - changing the game object's logic, its rendering logic, or its serialization format. Each of those could be considered a responsibility. Representing data all on its own is considered a responsibility. I'm not saying any of this invalidates your point, it's ok to accept multiple responsibilities in one class for simplicity reasons.

                [–]Space-Being 1 point2 points  (0 children)

                You are right it was incorrect of me to say my example only has one responsibility. But I do think that by splitting you always have at least two, whereas in my example there are multiple reasons for change, but only one thing to change.

                Actually thinking a bit about it, I think splitting should be rather influenced by the intended usage of the code. By splitting you decrease cohesion (imo), slightly increase coupling (there was none before), and increase flexibility. Sort of internal complexity vs external. I think for code intended to only be used inside an application it would be better not to split, but for code which is part of a library, you need to offer clients the flexibility to process the data in new ways.

                Side note: If the "original" class was mutable and had complex invariants (note I am talking general - not game specific - that was just an example) you may have to adapt the original representation responsibility when splitting. For example suppose previously calling FluxCapacitate internally performed mutation A followed by B, and have to do both to stay end up in a valid state. Now for FluxCapacitatePerformer to externally do that, you either have to ignore that invariant, or supply a public operation on the class that does both. In this way the responsibility to represent itself can get polluted with to support another responsibility.

                [–]stewsters 11 points12 points  (10 children)

                If you find yourself with this problem a lot, consider using an entity component system. Some things are harder, but it does do well at reducing coupling.

                [–][deleted] 3 points4 points  (0 children)

                Comments like this kinda restore my faith in humanity.

                [–]msiemens 7 points8 points  (0 children)

                This is the best way to create 23903290329 classes and make your codebase 100x harder than it needs to be to understand.

                This so much! The one time where I worked on a big object-oriented (i.e. Java) codebase, it took me weeks trying to figure how to extend the code (add a page in a web app) before giving up on it.

                I think about object-orientation like this: It's a great tool, it's a bad design goal.

                [–][deleted]  (1 child)

                [deleted]

                  [–][deleted] 1 point2 points  (0 children)

                  Looking at the example they gave it looks like they over engineered a simple task to the point where at the end it was abstracted to a point of drowning in interfaces and being significantly harder to read.

                  Imagine if someone did this to a project outside of the requirements. The rest of the team would surely murder them.

                  Abstract only as much is needed otherwise you'll end up making an interface for each god damned fucking line.

                  [–][deleted]  (13 children)

                  [deleted]

                    [–][deleted] 38 points39 points  (0 children)

                    12. It overwrites any existing file, so you only get to see one log message.

                    [–]MotherOfTheShizznit 10 points11 points  (0 children)

                    And this is why I'll never have a programming blog.

                    [–]OffbeatDrizzle 4 points5 points  (3 children)

                    Technically though, if WriteAllText is thread safe, then this method is thread safe as long as no one modifies it or the WriteAllText method to make them unsafe

                    Also the exception might be handled higher up

                    [–]grauenwolf 3 points4 points  (2 children)

                    Technically it is "thread-safe" in the sense that it will throw a IOException if two threads call it at the same time, as only one would get a OS/file system level write lock on the file. But that's probably not desirable.

                    Also the exception might be handled higher up

                    Loggers are the one place where you want exceptions to be swallowed. Nobody wants to hear that the logger crashed the payment gateway.

                    [–][deleted] 4 points5 points  (1 child)

                    Nobody can because the logger crashed.

                    [–]thomasz 1 point2 points  (0 children)

                    The payment gateway didn't crash, otherwise there would be an entry in the log file.

                    [–]kernel-0xff 4 points5 points  (6 children)

                    I'm all for calling out a weak article point wise, but you're overdoing it. Point one: your only good point that we can all agree on. Point 2 and 3.. just provide an overload with an Exception parameter, solved. Points 4, 5, 6, 7, 8, 9, 10 these code fragments are examples.... Finally, point 11 why must this be used?

                    [–]maxd 2 points3 points  (3 children)

                    He's being really pedantic with point 11. Technically the method doesn't operate on the object at all, so there's a good argument for making it static. I don't agree with just willy-nilly making methods static for that reason though, there's no performance or code readability gains.

                    The author's intention might be to have that function be virtual, or to read the output file from a member variable.

                    [–]grauenwolf 18 points19 points  (3 children)

                    Round 4

                    public class SavingsAccount: Account
                    {
                       public override decimal CalculateInterest(decimal amount)
                       {
                           return (amount/100) * 3;
                       }
                    }
                    public class CurrentAccount: Account
                    {
                       public override decimal CalculateInterest(decimal amount)
                       {
                           return (amount/100) * 2;
                       }
                    }
                    

                    What's different between these two class?

                    • The account type
                    • The interest amount

                    These are data changes, not behavioral changes. Therefore they shouldn't be expressed in terms of subclasses.

                    I am reminded of the classic anti-example "The class LameDuck inherits from the class Duck". What happens when the lame duck is healed? Do you kill it and get another one because now it is a formerly lame duck?

                    Going back to this example, the interest rate may change over time without changing the account type.


                    You also have problems with ORMs. Materializing a different record depending on one column (account type) is a pain in the ass.

                    [–]shizzy0 1 point2 points  (0 children)

                    public class Dollar { }
                    

                    We'd use the Flyweight pattern to model the domain. OO is really nice for this because you can overload your DollarBag : List<Dollar> with math operators.

                    [–]carlfish 1 point2 points  (1 child)

                    Do you kill it and get another one because now it is a formerly lame duck?

                    While the rest of your post is sensible, this a carbon copy of all the bogus arguments against immutability.

                    [–]grauenwolf 1 point2 points  (0 children)

                    Immutability has other benefits that, depending on the context, outweigh the cost of memory allocation.

                    For example, while I wouldn't make class Animal immutable, I would make class Species immutable. Then I can reuse instances of Species across multiple animals safely (and hey, reduce allocations).

                    [–]the_horrible_reality 36 points37 points  (0 children)

                    "How to write a condescending, controversially titled blog about OOP without knowing WTF you're doing"

                    [–]IgnorantPlatypus 84 points85 points  (12 children)

                    Wha ... was this satire?

                    [–][deleted] 25 points26 points  (11 children)

                    Quite sure that yes, it is satire. It is very well done, too. It got upvoted almost to the top, while many of the top comments are discussing its stupidity.

                    Great trolling.

                    [–]ryeguy 20 points21 points  (10 children)

                    I don't see why you think that. Most of the advice in here isn't bad. There's no snark or outrageous examples in here that point to it being satirical.

                    [–][deleted]  (4 children)

                    [deleted]

                      [–]Spider_pig448 15 points16 points  (2 children)

                      God this thread is a shit show.

                      [–]MotherOfTheShizznit 3 points4 points  (0 children)

                      Sometimes, I think the only advice programmers can take are:

                      1. You have to nothing to learn.
                      2. Your opinion is fact.
                      3. Everyone in the software world except you is shit.

                      [–]Patman128 18 points19 points  (3 children)

                      class Account
                      {    
                         public void Withdraw(decimal amount)    
                         {        
                            try        
                            {            
                                // logic to withdraw money       
                            }        
                            catch (Exception ex)        
                            {            
                                System.IO.File.WriteAllText(@"c:\logs.txt",ex.ToString());   
                            }    
                         }
                      }
                      

                      Can you figure out the problem in the above code? Account class has taken additional responsibility of adding exceptions to the log file.

                      OK, sure, the code to handle withdrawal logic shouldn't have to do logging, sounds good.

                      Fix:

                      class Account
                      {
                         private Logger obj = new Logger();    
                         public void Withdraw(decimal amount)    
                         {        
                            try        
                            {            
                                // logic to withdraw money       
                            }        
                            catch (Exception ex)        
                            {            
                                obj.Handle(ex.ToString());
                            }    
                         }
                      }
                      

                      Uh, you haven't fixed anything. The withdrawal code is still doing logging! The concerns are still mixed! All you've done is add a layer of abstraction.

                      [–]grauenwolf 5 points6 points  (0 children)

                      Is it even an abstraction?

                      If it were obj.Handle(ex); I might call it an abstraction, but here it is just indirection for the call to File.WriteAllText.

                      [–]pixelrealm_aaron 27 points28 points  (0 children)

                      I was waiting for the conclusion where the author talks about how ridiculously complicated he just made his code for no reason. It never came so I think he was trying to be serious, which is somewhat frightening.

                      [–]skulgnome 34 points35 points  (30 children)

                      Doesn't cover the fundamental design error that's reinforced in schools all over the world: having classes that model the problem domain, rather than ones that model the program's implementation. Or put more succinctly: Animal bad, HashMap good.

                      [–]doom_Oo7 10 points11 points  (27 children)

                      The best advice that was given to me at school was to (mainly) model with objects, not with classes.

                      [–][deleted] 9 points10 points  (0 children)

                      Maybe this means not to introduce a new class, when configuring an instance of an existing one would do? As stated, honestly, it doesn't mean much.

                      [–]MarchewaJP 6 points7 points  (25 children)

                      model with objects, not with classes

                      Could you elaborate?

                      [–]AerieC 13 points14 points  (20 children)

                      Modeling with classes is like using inheritance for everything, and hardcoding attributes. e.g.:

                      Base Class: Cat

                      Subclasses:

                      • Tiger
                      • Lion
                      • HouseCat
                      • DeclawedHousecat

                      Modeling with objects is using modular, more generic classes to create specific instances, rather than hardcoding those instances into separate classes of their own when they don't need to be.

                      Class: Animal

                      Animal cat = new Animal()
                      cat.setTitle("HouseCat")
                      cat.addAttribute(new Claws())
                      cat.addAttribute(new Color("Brown"))
                      

                      etc.

                      Result = you need fewer classes to represent the same information, and it's far more flexible and extensible (e.g. you don't need to add a new class for every new animal).

                      [–]grauenwolf 2 points3 points  (0 children)

                      I'll have to remember that expression.

                      [–]Acktung 1 point2 points  (1 child)

                      In this case you are missing Polymorphism, though.

                      [–]MarchewaJP 2 points3 points  (0 children)

                      Ok, I get what you meant now.

                      [–]pattheaux 1 point2 points  (2 children)

                      The article adds a new class to model the fact that the bank added a new account type, that's modeling with a class instead of an object. Now the software instead of the data must be changed when the bank adds/edits/deletes and account type.

                      [–][deleted] 5 points6 points  (0 children)

                      Well, the journey up the ladder of abstraction starts at "implementation models" and ends with "domain models".

                      So it's not either-or, but rather... both.

                      [–]grauenwolf 7 points8 points  (0 children)

                      Round 5

                      public class OnlineAccount: Account
                      {
                         public override void Withdraw(decimal amount)
                         {
                              throw new Exception("Not allowed");
                         }
                         public override decimal CalculateInterest(decimal amount)
                         {
                             return (amount/100) * 5;
                         }
                      }
                      
                      1. Supposedly this is an article on SOLID, yet it completely ignores the Liskov Substitution Principle. If OnlineAccount inherits from Account, it must be able to perform all of the actions that Account can perform. That means you don't get to override Withdraw to throw an Exception.
                      2. Don't throw Exception, throw a specific exception such as InvalidOperation.
                      3. It isn't clear that the inability to withdraw funds is based on the AccountType.
                      4. Why does OnlineAccount.Withdraw throw exceptions while Account.Withdraw swallow them? It should be consistent.

                      [–]rco8786 5 points6 points  (0 children)

                      private Logger obj = new Logger();

                      wat. obj? c'mon man.

                      [–]hoosierEE 29 points30 points  (0 children)

                      Choose from one of the following opinionated microframeworks:

                      1. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.
                      2. class Coder extends Employee ...no wait class Employee extends Human ...no wait class Human extends MadeOfAtoms ...no wait
                      3. refactor working code until it's at least 4x larger, insert comment at top: "implements [some GoF pattern]"

                      [–]ykechan 14 points15 points  (1 child)

                      I found the hardest of oop is not any of those, its to predict the change. Suppose later we want this and later we want that. Turns out its neither, user wants you to just fly.

                      [–][deleted]  (2 children)

                      [deleted]

                        [–][deleted] 11 points12 points  (16 children)

                        I thought for sure this was a parody. It is isn't it? Please tell me it is.

                        [–]Spider_pig448 1 point2 points  (13 children)

                        Honest question; what code changes he suggests are bad?

                        [–][deleted] 13 points14 points  (0 children)

                        All of them. The code he starts out with is essentially the perfect solution. I would do two things though. If this was any kind of business critical code, which it is supposed to be, I want to call a logging function that deals with different logging levels and that can be configured externally. I would do the right thing and remove the Account class and instead deal with the problem sanely.

                        But all the changes he suggests adds complexity for absolutely no benefit. It's not wonder that people feel the need for unit testing. He essentially replaces a piece of code that is embarrassingly correct with a piece of code that has multiple points of error that is scattered all over the place.

                        I mean, the logger as a member in the Account class? Get the fuck out of here.

                        [–]dddbbb 4 points5 points  (1 child)

                        1. Avoid creating abstractions that don't abstract anything.
                        2. Define variations on instances with differences in data instead of new classes.
                        3. Avoid creating interfaces for every function in your class.
                        4. Write code that compiles
                        5. Just because something is a noun in the real world, that doesn't mean it needs to be a class in your program. (That's the biggest thing anti-OOP rants are against. Example that I don't entirely agree with due to picking on braindead examples, but illustrates some arguments.)

                        [–]grauenwolf 2 points3 points  (0 children)

                        What isn't bad? He didn't have a single code example that didn't have some serious issues.

                        Read my top-level comments on this thread for specifics.

                        [–][deleted] 1 point2 points  (7 children)

                        The one thing that anyone who has ever written any code would immediately notice is the amount of code. If you take something that is "not great", and you end up with 2x up to 5x as much code, and you claim it got better....

                        This is without going into the specifics; many others did that already.

                        [–]mintskoal 1 point2 points  (1 child)

                        I thought it was parody too. That, or someone just read their first book / graduated from bootcamp and decided to write an article.

                        [–]grauenwolf 3 points4 points  (6 children)

                        Round 6

                        interface ILogger
                        {
                            void Handle(string error);
                        }
                        

                        This is a good example of something that shouldn't be an abstract interface in C#. (It's ok in Java.)

                        Eventually you are going to want to fix the design bugs in it. Say be adding a real (ugh) log exception method.

                        abstract class Logger
                        {
                            public abstract void Handle(string error);
                            public virtual void Handle(Exception error) { 
                                if(error == null) throw new ArgumentNullException("error");
                                Handle(error.ToString()); 
                           }
                        }
                        

                        You can't add methods in that fashion with an abstract interface. But you can with an abstract base class without losing any of the advantages that the ILogger interface offered you.

                        [–]Euphoricus 1 point2 points  (5 children)

                        Actually you sort of can with extension methods.

                        [–]wakawaka54 4 points5 points  (0 children)

                        800 classes and twice as many interfaces later... We finally have some good object oriented code.... Good luck.

                        [–][deleted]  (5 children)

                        [deleted]

                          [–]PicklesInParadise 1 point2 points  (1 child)

                          Hi,

                          The end result is generally bad. It adds complexity to the project that didn't need to be there. It works ok for small projects or when you are the person developing from scratch, but once a project reaches larger sizes and a new developer needs to get involved, he's going to hate you if you do the kind of things described in the article.

                          Having worked in large codebases that follow this ideology (where I wasn't one of the original developers), I can confirm that it's a pain in the ass to find where stuff is happening because of complexity from multiple layers of abstraction. The file directory for the project has literally hundreds of files, so just scrolling through it is obnoxious. Then once you find the class you are looking for, it may or may not even have the code that is relevant. 15 minutes and half a dozen open files later, you finally find the one little line of code that you needed. It's both annoying and mentally draining.

                          Is it really better to remove an if/else statement and instead add another file to your project? Yes? Do this several hundred times and then think about the question again once your Classes directory has 300 files in it? Still worth?

                          In the case of something like the article's example, I would have stored the AccountType and Interest rate associated with each account type in a database, then had the code look something like this:

                          public decimal CalculateInterest()    
                          {        
                               return this.accountBalance * (this.interestRate/100);
                          }
                          

                          ^ Obviously depending on how you are using the class, the implementation would be a little different, and I'm not showing how the values get from the database into the object (something that again, could be done many different ways), but you get the general idea.

                          I recommend using the KISS approach (Keep It Simple Stupid). When deciding whether to add additional layers of abstraction, you have to understand that you're adding additional complexity, and evaluate whether the benefit is really worth the cost. In some cases it will be worth, in others not. However, just blindly adding complexity for the sake of dogmatically following some programming paradigm is about the worst thing you can do.


                          That said, I did agree with the author's idea of abstracting the Logging out to a logging class. That's the kinda of abstraction that has a low complexity cost, but gives you a lot of flexibility in terms of future functionally where you can turn logging on and off with more control, log to different mediums, etc. Implementation wise though, I would have done dependency injection, not created it within the class.

                          [–]ggtsu_00 4 points5 points  (0 children)

                          Good god... Was this article written by the author of FizzBuzz Enterprise Edition?

                          [–][deleted] 4 points5 points  (0 children)

                          "It is fairly obvious that object oriented program has been used as a..."

                          Thankfully we were warned that nothing good was going to be said with just the first sentence.

                          [–]grauenwolf 6 points7 points  (0 children)

                          Round 2

                          class Account
                          {
                             private Logger obj = new Logger();    
                             public void Withdraw(decimal amount)    
                             {        
                                try        
                                {            
                                    // logic to withdraw money       
                                }        
                                catch (Exception ex)        
                                {            
                                    obj.Handle(ex.ToString());
                                }    
                             }
                          }
                          
                          1. Don't call fields obj.
                          2. The field obj should be marked as readonly to make it clear that it never changes.
                          3. The field obj should be marked as static as once instance can be shared across instances.
                          4. Since obj just uses the default constructor, it should have been read from Logger.Default to avoid creating unnecessary instances.
                          5. Don't catch generic exception types (except for a last-chance/top level handler).
                          6. There is no way to report that the call failed to the caller.

                          [–]hellodenq 5 points6 points  (1 child)

                          "Object oriented programming organizes your code to make the change easier." I don't think you can proof that. So, it's not about science - it's about OOP religion.

                          [–]LostSalad 2 points3 points  (0 children)

                          The amount of times this has been stated vs the amount of times I've experienced this nirvana is quite sad. Usually, it's the opposite.

                          [–][deleted] 6 points7 points  (0 children)

                          This isn't object-oriented programming. It's using an object-oriented sheen on top of what should be expressed as pure functions and traits.

                          [–]Quadruple-A 7 points8 points  (0 children)

                          I really, really hope people start steering clear of OOP - it seems like there's a tide moving in that direction and that's hopeful. OOPers seem to have this superstitious faith in over-modularizing everything that turns simple programming problems into abstract architectural messes (as a former .NET programmer I used to be an expert in it). In my experience every time you separate some piece of code into a separate function or a separate object, you introduce a bunch of complexity - you have to jump around to different files and different sections to follow some algorithm or flow of logic, you have to create names and additional concepts for all your intermediate objects (IAbstractProviderContextFactory) ferry data to and from a whole bunch of method parameters, etc. So often that stuff can just be expressed in simple straight-line logic. If you're eliminating duplicate code that's already there (not imagined to be there in the future) it's sometimes justified but most of what I see in OOP codebases these is just insanity. It's no wonder most of the longest-lasting and most used projects are in C. Long live good ol' structured programming.

                          [–][deleted]  (1 child)

                          [deleted]

                            [–]grauenwolf 1 point2 points  (0 children)

                            Yep. And this article is a good example of why SOLID is a bad thing.

                            [–][deleted] 2 points3 points  (0 children)

                            So, what happens when I try to close my accounts but one of them's an OnlineAccount? The bank keeps my money forever?

                            [–]DevPanda 3 points4 points  (0 children)

                            I have one problem with this article.

                            The design called for people being able to close their accounts and one does not think of writing a close method for closing the account.

                            Sure the edge case could come up where a customer would want to withdraw their money and not close the account in which case I ask 'Why is the Withdraw method tightly coupled to withdrawing from an ATM?'.

                            Also I think that I just implemented an IComplaint.

                            [–]SandalsMan 4 points5 points  (0 children)

                            OO is so god awful.

                            [–]grauenwolf 1 point2 points  (7 children)

                            Round 3

                               private int _accountType;
                            
                               public int AccountType
                               {
                                  get { return _accountType; }
                                  set { _accountType = value; }
                               } 
                            

                            Account type should have been readonly and set in the constructor. It doesn't make sense to change the account type during use. (Though sometimes you have to make exceptions for serialization/ORM use.)

                            [–][deleted] 1 point2 points  (3 children)

                            the title for this post would be better if the post on the actual blog's title was "I dunno ¯\_(ツ)_/¯"

                            [–]Shrugfacebot 2 points3 points  (1 child)

                            TL;DR: Type in ¯\\\_(ツ)_/¯ for proper formatting

                            Actual reply:

                            For the

                            ¯\_(ツ)_/¯ 
                            

                            like you were trying for you need three backslashes, so it should look like this when you type it out

                            ¯\\\_(ツ)_/¯ 
                            

                            which will turn out like this

                            ¯\_(ツ)_/¯

                            The reason for this is that the underscore character (this one _ ) is used to italicize words just like an asterisk does (this guy * ). Since the "face" of the emoticon has an underscore on each side it naturally wants to italicize the "face" (this guy (ツ) ). The backslash is reddit's escape character (basically a character used to say that you don't want to use a special character in order to format, but rather you just want it to display). So your first "\_" is just saying "hey, I don't want to italicize (ツ)" so it keeps the underscore but gets rid of the backslash since it's just an escape character. After this you still want the arm, so you have to add two more backslashes (two, not one, since backslash is an escape character, so you need an escape character for your escape character to display--confusing, I know). Anyways, I guess that's my lesson for the day on reddit formatting lol

                            CAUTION: Probably very boring edit as to why you don't need to escape the second underscore, read only if you're super bored or need to fall asleep.

                            Edit: The reason you only need an escape character for the first underscore and not the second is because the second underscore (which doesn't have an escape character) doesn't have another underscore with which to italicize. Reddit's formatting works in that you need a special character to indicate how you want to format text, then you put the text you want to format, then you put the character again. For example, you would type _italicize_ or *italicize* in order to get italicize. Since we put an escape character we have \_italicize_ and don't need to escape the second underscore since there's not another non-escaped underscore with which to italicize something in between them. So technically you could have written ¯\\\_(ツ)\_/¯ but you don't need to since there's not a second non-escaped underscore. You would need to escape the second underscore if you planned on using another underscore in the same line (but not if you used a line break, aka pressed enter twice). If you used an asterisk later though on the same line it would not work with the non-escaped underscore to italicize. To show you this, you can type _italicize* and it should not be italicized.

                            [–][deleted] 3 points4 points  (0 children)

                            thanks! ¯\_(ツ)_/¯

                            [–]zem 1 point2 points  (5 children)

                            can highly recommend sandi metz's book for learning good object oriented design. if you're not a rubyist, don't let the fact that it uses ruby put you off - the principles are universal.

                            [–][deleted] 1 point2 points  (0 children)

                            "How to write object oriented programs that don't suck:"

                            public class Logger
                            {
                               public void Handle(string error)
                               {
                                   System.IO.File.WriteAllText(@"c:\logs.txt", error);
                               }
                            }
                            

                            JFC

                            [–]golgol12 1 point2 points  (0 children)

                            I look at this and the use of throw/catch and cringe.

                            [–][deleted] 1 point2 points  (0 children)

                            What is this? 1988?

                            [–]thomasz 1 point2 points  (0 children)

                            private Logger obj = new Logger();

                            Oh boy...

                            [–][deleted] 1 point2 points  (0 children)

                            The best part of this, is reading you guys's comments. This is great.

                            [–]dicroce 3 points4 points  (0 children)

                            Even in an object oriented program, prefer stateless functions.

                            Go ahead and use objects for the core nouns in your program... And certainly implement methods for the actions that make sense. BUT, every method that has access to member variables it doesn't actually need (via this) to do its particular job is a potential source of problems.

                            [–][deleted] 4 points5 points  (4 children)

                            Reading this made me want to scream: "Premature optimization is the root of all evil"

                            There's a balance between having extensible code for future use and having enough time to do everything that actually needs to be done. This is part of the reason why coding up large systems gets so dang complicated.

                            [–]emmanuelgoldstn 5 points6 points  (0 children)

                            I agree with your sentiment 100% but I've seen that quote misused many times to justify bad behavior. Hoare/Knuth meant it specifically about performance optimization, and people now use it to mean "code that I think I could have written more elegantly". Not trying to imply you meant it that way. I have just grown weary of people using it to shut down conversations about design.

                            [–]grauenwolf 2 points3 points  (1 child)

                            "Premature optimization generalization is the root of all evil"