Operator overloading and different types

前端 未结 5 1506
轮回少年
轮回少年 2021-01-04 09:33

I have a class Score which is going to be heavily used in comparisons against integers. I was planning on overloading the == operator to enable these comparisons as per th

5条回答
  •  星月不相逢
    2021-01-04 10:08

    tl;dr In the grand scheme of things; no, it's not a sensible idea.

    1. The fact that one feels the need to ask or search for this question is already telling.
    2. The fact that few people seem to care indicate that there might be a way to simply avoid the situation where you need to ask the question to begin with.
    3. These last two points should at least compel you to reconsider a few things, however unrelated they might seem to be.

    Consider assigning types to all inputs to your software component as soon as possible and outside of your domain logic. This way, what we commonly call the "domain model" never has to worry about this.

    In your case, that means wrapping all scores in Score objects. Because that's what they are. The need to make exceptions is always at least dubious and qualifies as a code smell.

    The next step is to view your "domain model" as nothing special -- just another set of modules. See all those (too often nasty) I/O interfaces that use tangible primitives as fulfilling a distinct/separate role. This will naturally (and almost magically) reduce the individual complexity of all your components (e.g. classes).

    One side-effect of this is that you might discover this very question to become nonsensical. "Why would I ever need to compare a score to a primitive integer if I'm not even sure that integer is a score? And if I'm sure, then why didn't I say so before (e.g. by boxing it)?"

    Going even further, you might realize many of your algorithms don't even need to know how a score is kept: it's likely that the majority of them only need to be able to compare them to see if they are equal or to see which is higher. I'm not saying the flexibility of modifying how you keep score will be useful in the future, but as a rule if it shouldn't matter then there's no need to tie code to an implementation just for being cute. Perhaps surprisingly, that also applies to primitives such as integers, and even the very concept of a number (which you don't need to care about in all cases where all you need is to compare two scores). The SOLID principles provide a good guide until you can develop intuitions for yourself.

    Counter: One can always bring the performance argument to argue against this line of thinking. In my experience, I found the JIT will optimize and inline whatever comes in the way of performance (especially with trivial things like equality comparisons) and when it doesn't there's almost always a way to abstract performance-critical code in a sane way. Don't hesitate to seal your types if dispatching is an issue.

    After measuring and finding a bottleneck in a correct design that can't be fixed by an alternative but equally sane design, then one needs to be honest about the fact that they hit a language limitation, in which case one should either consider extracting that code and writing it in a more appropriate language or hack around with the existing language but comment the hell out of that code.

    To conclude, I'll mention that I actually did exactly what you suggested in the past, and I can only be grateful that the code in question never got into production. Do it once and it doesn't seem harmful, but do it all over the place and that uneasy feeling settles in and only generates frustration until you finally realize why it's wrong (per the considerations above). If you're curious, I did this for types that represented entity keys that almost always wrapped a single primitive. Again, it seemed like a sensible idea and it might still seem like it to you, but I strongly believe it isn't now even though I wouldn't hear about it before. Maybe it's one of those things you need to burn yourself with before it finally sinks in.

提交回复
热议问题