1

What alternative can one use to avoid exposing both events and an interface. I have a class that has a determined life cycle: created, modified, ... clients mainly GUIs need to hook up into the life cycle of that class so the simplest way to do it is to expose events, at the same time I need to move some responsibilities -that happen during the life cycle- out of the class so I came up with this solution:

Interface ILifeCycle
{
    void OnCreated(...);
    void OnModified(...);
    // ...
}

classA
{
    private ILifeCycle lifeCycle;
    /// ...
    public event EventHandler Created(object sender, EventArgs args);
    public event EventHandler Modified(object sender, EventArgs args);
    /// ...

    protected void OnCreated()
    {
        lifeCycle.OnCreated(...);
        if(Created!=null)
        Created(this,EventArgs.Empty);
    }

    protected void OnModified()
    {
        lifeCycle.OnModified(...);
        if(Modified!=null)
        Modified(this,EventArgs.Empty);
    }
    /// ...
}

Doing this I can inject a Logger that implements ILifeCycle, and so move the logging responsibility to its own class, but it feels like it's going to be a lot of repetition. What clean alternatives would you recommend to achieve this?

Patrick
  • 1,717
  • 7
  • 21
  • 28
anouar.bagari
  • 2,084
  • 19
  • 30
  • I think you can use dependency injection in your main class and call either a static log function or inject one as per the example here http://stackoverflow.com/questions/14301389/why-does-one-use-dependency-injection - which would help remove the feeling of repetition. – Mauro Jul 22 '14 at 12:02
  • @Mauro The problem is about dealing with the same concept(reprensted both by an Interface and EventHandler) but under different appearance. – anouar.bagari Jul 22 '14 at 12:14
  • have you considered using the state pattern and move the logging to a base state class? – mtijn Jul 22 '14 at 12:26
  • I am not sure what you are asking. Are you saying you don't want to expose both Event and Interface? – brainless coder Jul 22 '14 at 12:39
  • @activehigh Exactly, `ILifeCycle` is an aggregate form of EventHandler Created(object sender, EventArgs args) and EventHandler Modified(object sender, EventArgs args) so any modification will have an impact in both places – anouar.bagari Jul 22 '14 at 12:49
  • This doesn't look good. As written, class A does *not* implement the interface. So there is no obvious reason for it to also have to fire its own events. Choose between callbacks *or* events, don't do both. – Hans Passant Jul 22 '14 at 12:58
  • @HansPassant The events are there to provide an easy way for GUIs to hook up into the lifecycle of the class without requiring them to provide a whole class that implement LifeCycle especially if they are only interested in some events, the Callback in other way is useful for classes that are interested in the whole LifeCycle, and when I need to give a name to a responsability ex:'Logger' – anouar.bagari Jul 22 '14 at 13:11
  • It is pretty unclear why code that uses the interface would not be treated to the same "easy way". That there can only be *one* object to get these callbacks doesn't sound correct either when you use the word "classes". – Hans Passant Jul 22 '14 at 13:25

2 Answers2

1

In general Interface and Events/Delegates are used for two very different kinds of approach. Lets describe them each first -

Interface: The main purpose of interface is that it enforces some functionality to all implementations of that interface. And whenever you implement it in a subclass, you override the implementation of the super class. For example -

interface IA
{
    void test();
}

class A : IA
{
    public void test(){
    }
}

class B : A 
{
    public void test(){
         //you can only go up by calling base.test(), but cannot move down, because you do not know whether there is an implementation down the tree or not. So you cannot call it.
    }
}

class C : B 
{
    public void test(){
         //you can only go up by calling base.test(), but cannot move down, because you do not know whether there is an implementation down the tree or not. So you cannot call it.
    }
}

As you can see, with interface you can only look back but cannot look forward and assume there will be any more implementations.

Events: Events are created for a different purpose. lets just say you want to give the developers some facility to rely on some activities and do some other activities based on that activities and changes, and most importantly they will be implement this in future. The events will not depend on your implementation, they will just subscribe to it and do something based on that. Whether they exists or not, your own implementation does not change or the behavior of your own code does not changes based on them. In other words, you can only move down the tree. The base class captured the event and then propagates them down the tree.

These are the usual uses of Interface and Events and they are meant to be used that way. But its not completely impossible to code such that it will entirely depend on interface and vice versa, i.e. code entirely dependent on events but that is not the way they are meant to be.

In This Case: In your case, I am thinking you are trying to achieve a modular system that will not depend on each other but again subscribe to events. There are other architectures and patterns for this, specially IOC containers will be a very helpful for you, that will entirely be interface dependent, you will not need events. Some .net IOC containers are AutoFac, Castle.Windsor, MEF

I myself, like MEF the most, here is a post on MEF that I wrote few years back, shows you how you can inject run-time handlers inside a container -

http://mahmudulislam.me/2012/04/20/1a-managed-extensibility-framework-introduction/

BTW, article is a bit old, I am working on updating this one.

Solution with IOC: I am giving a probable solution with MEF -

Interface ILifeCycle
{
  void OnCreated(...);
  void OnModified(...);
  ...
} 

[Export(typeof(ILifeCycle))] //export our classes for injection
classB : ILifeCycle{
   public void OnCreated(...)
   {
       ....
   }

   public void OnModified(...){
   }
}

[Export(typeof(ILifeCycle))] //export our classes for injection
classC : ILifeCycle{
   public void OnCreated(...)
   {
       ....
   }

   public void OnModified(...){
   }
}

classA
{

  [ImportMany] //get all exported classes for injection
  private IList<ILifeCycle> _observers;

  protecetd void OnCreated()
  {
     //use MEF to build composition and then do the following

     foreach(var o in _observers){
        o.OnCreated(...);
     }
  }

  protecetd void OnModified()
  {
     //use MEF to build composition and then do the following

     foreach(var o in _observers){
        o.OnModified(...);
     }
  }
  ...
}

This is a very basic solution. But in your case you might wanna make use of asynchronous programming. Because there is very big difference between Events and Interfaces. By default, events handlers are call in a separate thread and thus it does not halt the caller, but interface implement will hang the caller until the method finishes. So make sure you use asynchronous programming to not block your main process.

Asynchronous Programming with Async and Await (C# and Visual Basic)

brainless coder
  • 6,310
  • 1
  • 20
  • 36
  • Yes Events and Interfaces are different concepts but the can be both used to solve the same probleme, it's just more convinient from a UI perspective to use Events while in my case is preferable for my domain model to use named behaviour(SecurityChecker, Logger...) that intercept those events and here cames the need of an explicite interface, so would give an example of how can we use IOC to achieve that? – anouar.bagari Jul 22 '14 at 14:03
  • @anouar.bagari Updated answer with a section - `Solution with IOC`, its not fool proof it will give you some idea how it can be done with MEF. – brainless coder Jul 22 '14 at 14:18
0

I'm not sure I understood you well but I think you worry about repetitions in different types implementing ILifeCycle. So, you can take advantage of inheritance:

abstract class LifeCycleBase
{
  public void OnCreated(...)
  {
        .....
  }
  public void OnModified(...);
  {
        .....
  }
  ...
}

class LifeCycleLoger : LifeCycleBase
{
  public void OnCreated(...)
  {
     ....
     base.OnCreate();
  }
....
}
Alireza
  • 10,237
  • 6
  • 43
  • 59
  • ILifeCycle is an aggregate form of `EventHandler Created(object sender, EventArgs args)` and `EventHandler Modified(object sender, EventArgs args)` so any modification will have an impact in both sides, that is my concern – anouar.bagari Jul 22 '14 at 12:38