First off, I'm going to assume that you've verified that optimisation is actually needed. Much has been said on the perils of premature optimisation, so I'll just say that if you haven't already, then check that this code actually needs optimising (ie. it's responsible for a significant portion of the program's execution time, or, for libraries, it's significantly slower than the things it's going to be used alongside).
Having made sure that this optimisation is necessary, there are a few issues to consider. The first thing is to consider who will use it:
- Some users don't care about optimisation. Having multiple methods provides them with multiple options in a place where one option would be fine. You're making them think more than they might otherwise have to, which is to be avoided - a programmer's brain is their most important resource; you should not eat into it lightly.
- Some users may be less skilled or less experienced - they might not know about optimisation, or might not understand the difference between the methods. Presenting them with several options will confuse them, and they might use the wrong one, or just give up and go find another library.
To generalise the previous two points, if it's up to the user to optimise, then there will be some users who don't. This means you're limiting the performance of your library in a lot of its applications. If you can make your code faster, you'll probably want to make it faster for everyone who uses it.
It's also worth considering how the code will be used:
- Your different methods might end up having different names, depending on your language's restrictions on method naming and argument types. This means that users have to remember which method goes with which type. This is more complicated than just knowing that collections have a
concat
method.
- Hiding the optimisations away makes your library more useable - the user doesn't need to make any type checks themselves, or use different methods in different places. They can forget about performance and focus on functionality, using
concat
everywhere and knowing that the optimisations will be handled for them.
In general, I'd definitely advise making the optimisations implicit, tucked "under the hood". In most cases, a user should be required to understand as little as possible about the internal workings of your code in order to use it. They'll need to know what it does, but the details of how it does it shouldn't affect them.
The main argument I'd make in favour of letting the user access the internal methods is that they may be able to select the correct method themselves without needing the type checks you're making, simply by knowing from the structure of the logic what type the object will be at that point. If you want to leave this possibility open, then one approach I've seen work quite well is to have a layered namespace structure. This depends on what your language allows, and the nature of the methods in question (might not work with your situation) but in general, it works something like this:
- A standard namespace,
MyLibrary
, which contains all the top-level functions with their checks and implicit optimisations. The majority of your users will just use this, and leave your code to optimise things.
- A deeper namespace,
MyLibrary.Core
or something like that, which exposes some more of the internal methods. The standard namespace is mostly a wrapper to hide this deeper namespace from users who are happy to let your code handle the optimisations. Users who really want to squeeze a little extra performance can use this namespace to give them access to the internal methods, so they can handle the optimisation themselves.