Hacker News new | ask | show | jobs
by vips7L 673 days ago
That OO refactor isn’t actual OO. The tell tale sign is that it is named by what it does rather than what it is (verb vs noun) and the -or ending in the name [0]. It’s just a function masquerading as a class.

The better refactor to introduce OO concepts would have been to introduce an isAdult function on the user class and maybe a formatted function. This + the functional refactor probably would have made for the best code.

    return users.filter(u => u.isAdult())
      .map(u => format(u)); // maybe u.formatted()

[0] https://www.yegor256.com/2015/03/09/objects-end-with-er.html
2 comments

> u.isAdult()

Being adult is not a property of the user but of the jurisdiction that the user is in. In some places or some purposes it is 18 but it could be, e.g., 21 for other purposes.

If you software is not going to just run on the USA it is not a good idea to implement isAdult in the user but in a separated entity that contains data about purpose and location.

With proper OO you could still implement that on the user object.

    boolean isAdult() {
        return this.age >= this.location.ageOfAdulthood();

        // or this.location.isAdult(this.age);  pick your poison!
    }

…anyway it’s just an example of how to introduce OO concepts. As everything in programming it depends
Just having some fun with bikeshedding here: Yeah, that could work but IMO in a big/international system the responsibility should ideally live elsewhere, since:

* You may need to determine adulthood for a different jurisdiction than where the person currently resides. Their citizenship may be elsewhere, or you may be running a report that expects "adulthood" to be by some other region's standards, etc.

* Sometimes the underlying kind of adult-need wanted is slightly different, like for consuming alcohol or voting.

* There may be a weird country or province has laws that need additional factors, like some odd place where it's a different age-cutoff for men and women.

Yes this is just extreme bike-shedding at this point. But none of this is impossible with more OO principles, like interfaces:

    class User {
        // Convenience function to check if the user is an adult in their current location
        boolean isAdult() {
            return this.location.isAdult(this);
        }


        boolean isOfDrinkingAge() {
            return this.location.isOfDrinkingAge(this);
        }
    }

    interface Location {
        boolean isAdult(User u);
        boolean isOfDrinkingAge(User u);
    }

    class WeirdLawsLocation implements Location {
        boolean isAdult(User u) {
            return switch (u.gender()) {
                case MALE -> u.age() >= 16;
                case FEMALE -> u.age() >= 18;
            }     
        }

        boolean isOfDrinkingAge(User u) {
            return u.age() >= 21
        } 
    }
In the hypothetical that you want to check somewhere the user is not currently:

    class SwedenLocation implements Location {
        boolean isAdult(User u) {
            return u.age() >= 18;
        }

        boolean isOfDrinkinAge(User u) {
            return u.age() >= 18;
        }
    }
    var sweden = new SwedenLocation();
    sweden.isOfDrinkingAge(user);
That feels like unnecessary levels of indirection to provide a method that shouldn't be on the User anyway.

    j = Jurisdiction.fromUserLocation(user);
    j.isOfDrinkingAge(user);
> that shouldn't be on the User anyway.

That’s just your opinion. It’s ok to provide convenience functions. I see no difference between the amount of indirection in our implementations, except mine is in the more natural place and you don’t have to know how to get a location or jurisdiction to answer the question: “is this user an adult?”. Knowing that it uses a location or jurisdiction is an implementation detail that you shouldn’t couple yourself to.

Cheers mate, I think I’m done moving goal posts for this conversation :)

On a side note, this discussion really made me realize how useful concise method bodies would be in Java: https://openjdk.org/jeps/8209434
Wow, thanks for proving that OOP was a mistake.
I challenge you to do it better then.

Also from the guidelines:

> Please don't post shallow dismissals, especially of other people's work. A good critical comment teaches us something.

> Being adult is not a property of the user but of the jurisdiction that the user is in.

It's a function of both. And I'd argue it's really mainly a function of the person: Independently of jurisdiction, there's at least a rough global consensus what "being adult" means, and most jurisdictions set rather similar (many of them, identical) limits.

A four-year-old isn't an adult anywhere; a fourty-year-old is everywhere.

What about a pure function that can take anything that has an age as input? Well obviously that wouldn't work for a cat but it's just an example. It requires typescript and I'm not sure how to name the file it would go in, but I think it's interesting to consider this duck-typing style.

    function isAdult({age}: {age: int}) {
        return age >= 18
    }
ps: I replaced const by function because I don't like the IDE saying I can't use something before it is defined. It's not a bug it's an early feature of javascript to be able to use a function before it is defined. Code is just easier to read when putting the caller above the callee.
That wouldn’t be object oriented. In OO you tend to want to ask an object about itself, Yegor talks a bit about this in his book Elegant Objects.

What you are proposing is just functions or data-oriented programming; which is fine if that’s your thing, but I’d be weary because of the reasons you outline above. Can a book be an adult? What about a tv show? Or recipe from the 9th century? isAdult really only applies to users and really belongs on that object.