views:

111

answers:

3

Hey SO,

I am wondering what's the best way to insert customization hooks into my application. Basically, my application is split into two assemblies: A Core assembly containing all the business logic and a UserInterface assembly containing the GUI, controller classes (I am using a deviant MVC pattern callse "Passive View") and some helper classes. The core assembly is used by some other applications as well.

My application is used by our company to handle orders from other companies. The general process is the same for all companies, but there are small deviations here and there that are customer-specific. As of now those deviations are implemented straight into the core assembly, which smells. I want to separate those specifics, best encapsualte them into a single object per customer so I have a central object containing all customer specific details which I can put into the UserInterface assembly.

I thought about using events to achieve this. By adding some events in my core classes, my controller classes would be able to subsribe or unsubscribe methods implementing those deviations for certain customers.

I can think of two ways of doing this: Adding those bindings manually or let them being added automatically, if the deviant methods exist. I'm thinking about something like this for the latter:

foreach (Order order in form.SelectedOrders) {
    CustomerExtension customer = customerExtensions[order.Customer];

    if(Exists(customer.StatusChanging(...)) // Pseudo Code!
            OrderManager.StatusChanging += new StatusChangingEventHandler(customer.StatusChanging(...));

    order.SetStatus(newStatus);

    if(Exists(customer.StatusChanging(...)) // Pseudo Code!
            OrderManager.StatusChanging -= new StatusChangingEventHandler(customer.StatusChanging(...));
}

I guess I have to use Reflection to achieve this, but is this viable for operations that need to be done many times?

Or are there better ways to add customization hooks while leting me centralize the deviations on a customers-basis?

[EDIT] Completely revised the question.

+1  A: 

How about using an abstract base class that contains all of the shared functionality and then add in some hook methods (abstract methods intended to be overriden in subclasses). This is where you can add the specifics for each company. Here is some pseudo code for what I'm trying to describe:

abstract class SomeBaseClass
{

   public void DoSomething()
   {
       ...
       somethingElse();
       ...
   }

   abstract void somethingElse();

}

public class CompanyA : SomeBaseClass
{
    void somethingElse()
    {
       // do something specific
    }
}

public class CompanyB : SomeBaseClass
{
    void somethingElse()
    {
       // do something specific
    }
}
Taylor Leese
But wouldn't this enforce the abstract methods to be overriden? It's not like there aren't any cases where the general functionality isn't sufficient. It's more like there are some cases where I need to add specifics, but most of the time the general functionality is just fine. Also, my Manager object is a singleton which means I can't simply make several (derived) objects and swap them using the strategy pattern.
Michael Barth
@Michael Barth - Then just provide a concrete implementation for the hook method instead of making it abstract. Basically, the point I'm getting at is some basic inheritance should be able to solve your problem.
Taylor Leese
I got your Point. But alas this would require me to rewrite other parts of my Application as it was designed with single, central Manager classes in mind, which both offer functionality and hold state (of the orders, in case of the OrderManager). Extending this design by adding a specific OrderManager for each customer would complicate the design as I would need to synchronize the state between those Manager Objects before each swap. So I would like to stick with single, unique Manager object instances, which excludes inheritance as solution. Thanks anyway!
Michael Barth
You can implement this with very little change to your manager: the Manager is coded entirely in terms of the BaseClass. at te point where the customer-specific classes are created the Manager invokes a Factory with a method **makeCustoemr()** that's where all the differentiation happens. This is a classic OO design, Taylor's advice seems good to me. Try it, see how simple Manager's life is.
djna
But what about the state of my Manager object? It would be distributed over all derived objects. Sure, the state is also synchronized with a DB in background, but this would necessitate that before each swap I would need to commit and reload the other object which I am sure would cause a performance impact as my manager objects caches the data. E.g. if I set the status of 3 orders with 3 different customers, then there would be 3 commits and synchronizations by one single action. This does not sound desirable to me.
Michael Barth
I've extended my question with my latest insights, maybe you could comment on that one. That being said, I don't want to appear bullheaded, it's just that I see problems with your proposed solution in my design which your present answers couldn't alleviate.
Michael Barth
A: 

So I've implemented my idea about using events and I am pretty satisfied with it so far.

I was able to put all my customer-specific logic within a single class per customer, while my controller subscribe/unsubscribe the corresponding customer's specific functions -- if there are any. Furnishing my Manager classes with events was the easiest part.

Only drawback is, I have to add/remove the subscription handling in the controller classes by hand. E.g. my SetStatus-method looks like this:

foreach (Job job in form.SelectedJobs) {
    // Subscribe customer-specific methods to events
    switch (job.Customer.Code) {
            case "VNR":
                    jobManager.JobStatusChanged += new JobManager
                        .JobStatusChangedHandler(Vnr.OnStatusChanged);
                    break;
            case "LS":
                    jobManager.JobStatusChanged += new JobManager
                        .JobStatusChangedHandler(Ls.OnStatusChanged);
                    break;
    }

    jobManager.SetStatus(job, status);

    // Unsubscribe customer-specific methods from events
    switch (job.Customer.Code) {
            case "VNR":
                    jobManager.JobStatusChanged -= new JobManager
                        .JobStatusChangedHandler(Vnr.OnStatusChanged);
                    break;
            case "LS":
                    jobManager.JobStatusChanged -= new JobManager
                        .JobStatusChangedHandler(Ls.OnStatusChanged);
                    break;
    }
}

Which really isn't very elegant, but it's not that bad.

Michael Barth
+1  A: 

I think you could even do it without events (are they the structure you want?). I tried to put together something: try having a look at the code (without caring too much about the details) and let me know if you'd like me to elaborate... :-)

// Begin personalization assembly (one of many)-----------------------

/// <summary>
/// Here you could use an attribute to allow clean reflection
/// </summary>
// [CustomerSpecific("Acme")]
public class AcmeCustomization : BaseCustomization
{
    public override void OnStatusChanged()
    {
        base.OnStatusChanged();
        // do what you need to customize
    }
}
// End personalization assembly (one of them)-------------------------

// Begin core assembly -----------------------------------------------
public interface ICustomization
{
    void OnStatusChanged();
}

/// <summary>
/// This class is optional of course, but can be useful
/// </summary>
public class BaseCustomization : ICustomization
{
    public virtual void OnStatusChanged()
    {
        // intentionally empty
    }
}

class CustomizationFactory
{
    public ICustomization GetCustomization(string order)
    {
        // Here you could
        // - hardcode (as you did in your solution)
        // - use reflection (various ways)
        // - use an external mapping file
        // - use MEF (!)
        // and then
        // - do instance caching
        // - whatever...

        // I'm lazy ;-)
        return null;
    }
}

class OrderManager
{
    private ICustomization _customization = null;

    public void SetStatus(string order, int status)
    {
        // Do some work
        this.OnStatusChanged();
        // Do some other work
    }

    protected void OnStatusChanged()
    {
        if (_customization != null)
        {
            _customization.OnStatusChanged();
        }
    }

    public void SetCustomization(ICustomization customization)
    {
        _customization = customization;
    }

    public void ClearCustomization()
    {
        _customization = null;
    }
}
// End core assembly -------------------------------------------------

class Program
{
    static void Main(string[] args)
    {
        CustomizationFactory factory = new CustomizationFactory();
        OrderManager manager = new OrderManager();

        // here I'm just pretending to have "orders"
        var orders = new string[] { 
            "abc",
            "def"
        };

        const int newStatus = 42;

        foreach (var order in orders)
        {
            manager.SetCustomization(factory.GetCustomization(order));
            manager.SetStatus(order, newStatus);
            manager.ClearCustomization();
        }
    }
}

HTH

Fabrizio C.
Hey Fabrizio! First of all, thanks for the elaborate example, I really appreciate it. What do you think are the advantages of your approach to events? I also thought about something like that, but disliked the notion of empty methods in the BaseCustomization class. One advantage I can see is, you've got an interface for all customizations.
Michael Barth
I've implemented your approach and I love it! It's smart and avoids some problems my approach had. Thank you very much for your time, you really helped me!
Michael Barth