Hacker News new | past | comments | ask | show | jobs | submit login

That is what happens when all too commonly people treat OOP as their religion. Prefer encapsulation to inheritance is good advice. Interfaces are very useful if you have more than one implementation (mocks/fakes do not count), but otherwise pointless. Having private data is always a good idea - no matter what your code style is you don't want to have to look over all 10+million lines of code anytime you need to change data. (there is no clear answer to how the size of a circle should be stores - radius, diameter, circumference, area - given one you can easially calculate the others, but there are implications to whatever choice you make and sometimes you will discover late that you made the wrong choice).

> we [snip] use encapsulation." "Yes, but why?"

Simple: because when your program gets large you cannot know everything needed to modify the code. You need places where you can say "something really complex happens here and I must trust it is right" (not to be confused with it is right, only that right or wrong is irrelevant)

OOP is a great answer to the problems of large complex programs. It is not the only answer. It sometimes is the best and sometimes not. Applying it like a religion is wrong.




> Interfaces are very useful if you have more than one implementation (mocks/fakes do not count)

Why not, again?


Because of the cost. Performance (if you optimizer is any good) will be at most a few thousand CPU cycles and so only rarely worth worrying about, though even this can add up if everything is an interface.

The larger cost is maintenance. Every time you want to change/add/remove something you now need to touch not only every place that uses the thing in question, but also all the tests. That is by mocking your are tightly coupling your tests to how your code is implemented instead of the code should do. If there is more than one implementation this is okay as the code can do different things in different situations anyway, but if there is only one then you are adding coupling that isn't useful.

If you have never worked with non-trivial code that is more than 10 years old the above won't make sense. However as code ages you will discover that the original beautiful architecture ideas where always wrong for some need that happened since and so you have had to change things. Mocks make all those changes much harder.


Let me refine:

> mocks/fakes do not count

Why not?


You apparently have never been in the situation of making a trivial change to production code and then have thousands of tests fail because of overuse of mocks.


You contradict yourself here. Interfaces are useful for the same reason having private data is a good idea: separating the contract from the implementation. Interfaces are a description of what the consumer of the interface needs, not a description of some commonality between implementations. Interfaces can be useful even if you have no implementations.


Do you want to pay me several years income to write a book on this subject, including the cost of editors, publishers, and other things I'm not even aware of (I've never written a book). This is a very complex subject but I stand by it as a general rule even though there are some exceptions. In general if there is only one implementation just use it.


Not several years' income, but I might pay you up to $50 for it if it's good.

> In general if there is only one implementation just use it.

This is good advice for people who don't understand what interfaces are for -- which seems to be most people. If you make the mistake of believing that interfaces are for describing the commonalities between similar implementations, then demanding an IFoo for every Foo is indeed a waste. Generally if I see literally the same word twice, but one has an "I" in front of it, it's a flag that the interface might not need to exist. I think this is the advice you're giving here. But you've missed the other important point: interfaces are not "about" their implementations, they're "about" their consumers. The point of an interface is for the consumer to define exactly what it needs to do its job. That's why interfaces can be useful even if there are 0 implementations: it's still a clear definition of what some service or function requires. Assuming that service isn't private/internal, it's a hook that a consumer of your module can implement if they choose.

I'd say the frequency of the following patterns is correlated with different levels of code quality -- sometimes causally, sometimes spuriously.

    1. StringWarbler : IStringWarbler  (worst)
    2. StringWarbler                   (fine)
    3. StringWarbler : IWarbler        (maybe worse or better, but a bit smelly)
    4. StringWarbler : ITextProcessor  (good)
Yes, #2 is fine if all you need to do is warble strings and there's really only one thing that means. That's common and it's fine. #3 is suspect because it looks like it was abstracted along the wrong line. It looks like there were a lot of different "ThingWarblers" and someone tried to define what was similar about them, without really focusing on the consumers of these services. Whereas with #4, it looks like someone identified a need to process text in various ways, and one way to do that is to warble them. It sounds like the interface was created to describe a need, not an implementation. When you do it this way, you start to see classes that implement two or three interfaces as well.

When I see #2 everywhere, I don't think "these classes need to be implementing more interfaces!". Instead I think "the services relying on these classes are probably not doing a good job specifying exactly what they need to do their job, and are therefore brittle against changes in how those needs are fulfilled." Whether that's a problem depends on the exact situation. I feel like your advice is "prefer #2 over #3", which I generally agree with. But the better advice is "ask for what you need to do your job, in the most general form you can", which #2 (in excess) violates.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: