3. Factories and Dependency Injection

This chapter covers NLSQ’s factory functions and protocol-based dependency injection patterns for composing custom optimization pipelines.

3.5. Chapter Overview

Factory Functions (10 min)

create_optimizer() and configure_curve_fit() for runtime composition.

Protocols (10 min)

Interface contracts for loose coupling.

Adapters (5 min)

Protocol implementations that bridge components.

Dependency Injection (10 min)

Composing pipelines with injected dependencies.

3.6. Why Factories and DI?

  1. Flexibility: Configure behavior at runtime

  2. Testing: Inject mocks for testing

  3. Extension: Add custom components without modifying core

  4. Decoupling: Components don’t know each other’s implementations

3.7. Quick Examples

from nlsq.core.factories import create_optimizer, configure_curve_fit

# Factory function creates configured optimizer
optimizer = create_optimizer(global_optimization=True, diagnostics=True)
popt, pcov = optimizer.fit(model, x, y)

# Configured fit function with preset defaults
my_fit = configure_curve_fit(ftol=1e-10, xtol=1e-10)
popt, pcov = my_fit(model, x, y)

# Protocol-based injection
from nlsq.interfaces import CurveFitProtocol


class MyCustomFitter:
    def curve_fit(self, f, xdata, ydata, **kwargs):
        # Custom implementation
        pass


fitter: CurveFitProtocol = MyCustomFitter()