在我的设计中实现自定义挂钩的最佳方法

时间:2009-09-02 06:28:13

标签: c# events oop extensibility

我想知道在我的应用程序中插入自定义挂钩的最佳方法是什么。基本上,我的应用程序分为两个程序集:包含所有业务逻辑的Core程序集和包含GUI的UserInterface程序集,控制器类(我使用的是异常的MVC模式callse“被动视图”)和一些帮助类。核心组件也被其他一些应用程序使用。

我公司使用我的申请处理其他公司的订单。所有公司的一般流程都是相同的,但这里和那里都有一些小客户特定的偏差。到目前为止,这些偏差直接进入核心组件,闻起来。我想将这些细节分开,最好将它们封装到每个客户的单个对象中,这样我就有一个中心对象,其中包含我可以放入UserInterface程序集中的所有客户特定细节。

我想过用事件来实现这个目标。通过在我的核心类中添加一些事件,我的控制器类将能够为某些客户提供或取消订阅实现这些偏差的方法。

我可以想到两种方法:手动添加这些绑定或者如果存在异常方法则自动添加它们。对于后者,我正在考虑这样的事情:

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(...));
}

我想我必须使用Reflection来实现这一点,但这对于需要多次执行的操作是否可行?

或者是否有更好的方法来添加自定义挂钩,同时让我在客户基础上集中偏差?

[编辑]完全修改了这个问题。

3 个答案:

答案 0 :(得分:3)

我认为你甚至可以在没有事件的情况下这样做(它们是你想要的结构吗?)。我试图整理一些东西:试着看一下代码(不要过多关注细节),如果你想让我详细说明,请告诉我......: - )

// 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

答案 1 :(得分:1)

如何使用包含所有共享功能的抽象基类,然后添加一些钩子方法(旨在在子类中重写的抽象方法)。您可以在此处添加每个公司的详细信息。这是我想要描述的一些伪代码:

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
    }
}

答案 2 :(得分:0)

所以我已经实现了关于使用事件的想法,到目前为止我对它非常满意。

我能够将每个客户特定的逻辑放在每个客户的单个类中,而我的控制器订阅/取消订阅相应客户的特定功能 - 如果有的话。通过事件提供我的经理类是最容易的部分。

唯一的缺点是,我必须手动添加/删除控制器类中的订阅处理。例如。我的SetStatus - 方法如下所示:

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;
    }
}

这真的不是很优雅,但并不是那么糟糕。