library.inversion.examples.md 8.4 KB

Usage Examples

Overview

Practical examples demonstrating Inversion IoC container features including basic registration, lifecycle management, field injection, multi-registration, and scope-local registrations.

Basic Registration and Resolution

using Inversion;

public interface ILogger : Object {
    public abstract void log(string message);
}

public class ConsoleLogger : Object, ILogger {
    public void log(string message) {
        stdout.printf("[LOG] %s\n", message);
    }
}

public static int main(string[] args) {
    var container = new Container();
    
    // Register implementation as interface
    container.register_singleton<ConsoleLogger>((scope) => new ConsoleLogger())
        .as_type(typeof(ILogger));
    
    // Create scope and resolve
    var scope = container.create_scope();
    var logger = scope.resolve<ILogger>();
    logger.log("Hello, World!");
    
    return 0;
}

Lifecycle Patterns

Singleton (Application-Wide)

public class Configuration : Object {
    public string db_path { get; set; }
}

container.register_singleton<Configuration>((s) => {
    var config = new Configuration();
    config.db_path = "/data/app.db";
    return config;
});

// Same instance across all scopes
var scope1 = container.create_scope();
var scope2 = container.create_scope();
var config1 = scope1.resolve<Configuration>();
var config2 = scope2.resolve<Configuration>();
// config1 == config2

Scoped (Per-Request)

public class UserSession : Object {
    public string user_id { get; set; }
}

container.register_scoped<UserSession>((s) => new UserSession());

// Same instance within scope
var scope = container.create_scope();
var session1 = scope.resolve<UserSession>();
var session2 = scope.resolve<UserSession>();
// session1 == session2

// Different instance in different scope
var other_scope = container.create_scope();
var session3 = other_scope.resolve<UserSession>();
// session1 != session3

Transient (Per-Resolution)

public class RequestHandler : Object {
    public void handle(string request) {
        stdout.printf("Handling: %s\n", request);
    }
}

container.register_transient<RequestHandler>((s) => new RequestHandler());

var scope = container.create_scope();
var handler1 = scope.resolve<RequestHandler>();
var handler2 = scope.resolve<RequestHandler>();
// handler1 != handler2

Field Injection

public interface IRepository : Object {
    public abstract string get_data();
}

public interface IHandler : Object {
    public abstract void handle(string request);
}

public class UserService : Object {
    // Dependencies injected during construction
    private IRepository repo = inject<IRepository>();
    private IHandler handler = inject<IHandler>();
    
    public void process() {
        var data = repo.get_data();
        handler.handle(data);
    }
}

// Register dependencies
container.register_singleton<MemoryRepository>((s) => new MemoryRepository())
    .as_type(typeof(IRepository));
container.register_singleton<ConsoleHandler>((s) => new ConsoleHandler())
    .as_type(typeof(IHandler));
container.register_scoped<UserService>((s) => new UserService());

// Resolve - dependencies injected automatically
var scope = container.create_scope();
var service = scope.resolve<UserService>();
service.process();

Multiple Implementations

public interface IValidator : Object {
    public abstract bool validate(Object obj);
}

public class EmailValidator : Object, IValidator {
    public bool validate(Object obj) {
        stdout.printf("Validating email...\n");
        return true;
    }
}

public class PhoneValidator : Object, IValidator {
    public bool validate(Object obj) {
        stdout.printf("Validating phone...\n");
        return true;
    }
}

// Register multiple implementations
container.register_singleton<EmailValidator>((s) => new EmailValidator())
    .as_type(typeof(IValidator));
container.register_singleton<PhoneValidator>((s) => new PhoneValidator())
    .as_type(typeof(IValidator));

// Resolve all implementations
var scope = container.create_scope();
var validators = scope.resolve_all<IValidator>();

validators.iterate((obj) => {
    var validator = (IValidator) obj;
    validator.validate(data);
});

Inject All Pattern

public class ValidationService : Object {
    // Inject all IValidator implementations
    private Lot<IValidator> validators = inject_all<IValidator>();
    
    public bool validate_all(Object obj) {
        foreach (var validator in validators) {
            if (!validator.validate(obj)) {
                return false;
            }
        }
        return true;
    }
}

Scope-Local Registrations

public class RequestContext : Object {
    public string request_id { get; set; }
    public string path { get; set; }
}

// Container has application-wide services
var container = new Container();
container.register_singleton<Database>((s) => new Database());

// Each request creates a scope with request-specific context
void handle_request(string request_id, string path) {
    var scope = container.create_scope();
    
    // Register request-specific context (only visible in this scope)
    scope.register_local_scoped<RequestContext>((s) => {
        var ctx = new RequestContext();
        ctx.request_id = request_id;
        ctx.path = path;
        return ctx;
    });
    
    // Handlers can resolve RequestContext
    var handler = scope.resolve<RequestHandler>();
    handler.process();
}

Custom Factory with Dependencies

public class OrderService : Object {
    private IOrderRepository repo;
    private ILogger logger;
    
    public OrderService(IOrderRepository repo, ILogger logger) {
        this.repo = repo;
        this.logger = logger;
    }
}

container.register_scoped<OrderService>((scope) => {
    // Resolve dependencies from scope
    var repo = scope.resolve<IOrderRepository>();
    var logger = scope.resolve<ILogger>();
    return new OrderService(repo, logger);
});

Multiple Interface Registration

public interface IRepository : Object {
    public abstract Object? find(int id);
}

public interface ICache : Object {
    public abstract void cache(Object obj);
}

public class CachedRepository : Object, IRepository, ICache {
    public Object? find(int id) { return null; }
    public void cache(Object obj) { }
}

// Register as both interfaces
container.register_singleton<CachedRepository>((s) => new CachedRepository())
    .as_type(typeof(IRepository))
    .as_type(typeof(ICache));

// Can resolve by either interface
var scope = container.create_scope();
var repo = scope.resolve<IRepository>();
var cache = scope.resolve<ICache>();
// Both point to same CachedRepository instance

Error Handling

try {
    var service = scope.resolve<IMyService>();
    service.do_work();
} catch (ContainerError.NOT_REGISTERED e) {
    stderr.printf("Service not registered: %s\n", e.message);
} catch (ContainerError.CYCLE_DETECTED e) {
    stderr.printf("Circular dependency: %s\n", e.message);
} catch (ContainerError.ILLEGAL_LIFECYCLE_COMBINATION e) {
    stderr.printf("Lifecycle error: %s\n", e.message);
} catch (Error e) {
    stderr.printf("Error: %s\n", e.message);
}

Web Application Pattern

public class ApplicationController : Object {
    private static Container container;
    
    static construct {
        container = new Container();
        
        // Application-wide services
        container.register_singleton<Database>((s) => new Database("app.db"));
        container.register_singleton<Cache>((s) => new RedisCache());
        
        // Per-request services
        container.register_scoped<UserSession>((s) => new UserSession());
        container.register_transient<RequestHandler>((s) => new RequestHandler());
    }
    
    public void handle_request(Request req, Response res) {
        var scope = container.create_scope();
        
        // Register request context
        scope.register_local_scoped<RequestContext>((s) => {
            return new RequestContext(req.id, req.path);
        });
        
        try {
            var handler = scope.resolve<RequestHandler>();
            handler.handle(req, res);
        } catch (Error e) {
            res.error(500, e.message);
        }
    }
}

These examples demonstrate Inversion IoC patterns including basic registration, lifecycle management, field injection with inject(), multi-registration with resolve_all(), and scope-local registrations for per-request contexts.