Giter VIP home page Giter VIP logo

Comments (5)

sonbua avatar sonbua commented on August 18, 2024

Steps:

  1. Given a class that implements some business logic
public class BizHandler : IHandler<int, int> {}
  1. Write an interceptor, which implements IInterceptor<THandler, TIn, TOut>
public class Debugger<THandler, TIn, TOut> : IInterceptor<THandler, TIn, TOut>
    where THandler : IHandler<TIn, TOut>
{
    public TOut Intercept(IHandler<TIn, TOut> handler, TIn input, Func<TIn, TOut> next)
    {
        // intercept handler's execution here
    }
}
  1. Register above interceptor with the IoC container as collection.
  2. Add the handler to a composite handler in the constructor
public class Composite : Handler<int, int>
{
    public Composite(IServiceProvider serviceProvider)
        base(serviceProvider)
    {
        AddHandler<BizHandler>();
    }
}

Under the hood, the DefaultInterceptionStrategy will

  • resolve all interceptors which are resolvable as IInterceptor<BizHandler, int, int>
  • decorate the BizHandler with the interceptors
  • then add the decorated BizHandler to the end of the chain

If you want to roll your own interception strategy

  1. write a class that implements IInterceptionStrategy
  2. pass a strategy object to the AddHandler method, e.g. AddHandler<BizHandler>(myInterceptionStrategy)

from responsibilitychain.

sonbua avatar sonbua commented on August 18, 2024

Reopen due to feature #5

from responsibilitychain.

sonbua avatar sonbua commented on August 18, 2024

Doing interception work within the AddHandler method (and therefore constructor) is rarely a good idea.
So interception-related code will be removed from the code base.
References:
https://blog.ploeh.dk/2011/03/03/InjectionConstructorsshouldbesimple/
https://vuscode.wordpress.com/2009/10/16/inversion-of-control-single-responsibility-principle-and-nikola-s-laws-of-dependency-injection/
http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/

from responsibilitychain.

sonbua avatar sonbua commented on August 18, 2024

As of the latest release which implements above comment, all nested handlers are now being exposed in the constructor as the dependencies, and are resolved by IoC container or just manually newing up them.

Example

// A parser, which basically converts something like "1h 30m" to 90 minutes
IHandler<string, int> parser = new WorkLogParser(
    new WorkLogValidator(
        new WorkLogMustNotBeNullOrEmptyRule(),
        new ThereShouldBeNoUnitDuplicationRule(),
        new UnitsMustBeInDescendingOrderRule()
    ),
    new IndividualUnitParser(
        new WeekParser(),
        new DayParser(),
        new HourParser(),
        new MinuteParser()
    )
);

We've seen another problem with this approach though. Most of the time, the Handle method of a nested handler, e.g. WeekParser, is not virtual, i.e. not overrideable, so that it is not possible for the IoC container to create a callback hook (think about aspect-oriented programming).

To overcome this, there were two approaches being proposed:

  1. Inject a list of IHandler<TIn, TOut> instead of individual concrete handlers. There are two issues with this approach:
    1.1. The number and the order of dependencies are not obvious to the composite handler. You register a list of nested handlers to a composite in a separate class (Autofac module, Windsor installer, composition root, or whatever). It makes it hard to follow, thus hard to maintain. The number and the order of dependencies are the business logic of the composite handler, thus it should be visible within the composite itself, not elsewhere.
    1.2. As a consequence, it might lead to a situation, in which a unit test passed, yet it causes exception in production due to different object graph's setup.
  2. The second approach is to explicitly declare all handlers (or just relevant ones) virtual, and intercept these handlers by creating dynamic proxies on the fly. This would be a repetitive and error-prone task.

We couldn't find a clean way to inject an intercepted handler into the constructor. To realize callback hooks, it is necessary for the IoC container to have a chance to intercept the resolved THandler within the AddHandler method. This interception process should be

  • invisible as if interception feature wouldn't exist. It should not require framework's user to have additional configuration on their side.
  • not tightly-coupled to any interception mechanism

One main disadvantage with this approach is that it depends on a global singleton instance, such as InterceptionStrategy.Current. It makes it impossible to run unit tests in parallel if there are some tests which change the global strategy.

There is another advantage with this approach though.

In C#, all methods are non-virtual by default. On the contrary, all methods in Java are virtual by default.

You can imagine a composite handler with its nested handlers exposed via composite's constructor as an API, which is non-virtual by default. This is the C# way.

And then there are interceptors, which suddenly make all (or just several) nested handlers "virtual" without touching the handlers' source code. Isn't it a nice problem to have?

from responsibilitychain.

sonbua avatar sonbua commented on August 18, 2024

Resolved with commit 4a1e6db.

from responsibilitychain.

Related Issues (13)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.