After reading this mulitple implementations answer by @stevengj, I implemented a slightly more complex strategy. Using a type hierarchy allows the “interface” to comprise more than a single method.
I now have a method optimize(f, ::AbstractDistributionFitOptimizer, lower, upper)
that calls the actual optimization routine. By specializing this method, different implementations can be used.
Method DistributionFits.set_optimizer(::AbstractDistributionFitOptimizer)
configures the Optimizer used by the package.
Again, Requires.jl is used to automatically set the default, if the Optim.jl package is in scope.
I am happy about critical feedback and advice on a Julian way of managing project dependencies in a way that higher-level code does not depend on lower-level code.