3

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

我们公司使用我的应用程序来处理来自其他公司的订单。所有公司的一般流程都是相同的,但这里和那里存在特定于客户的小偏差。截至目前,这些偏差已直接实施到核心组件中,闻起来很臭。我想将这些细节分开,最好将它们封装到每个客户的单个对象中,这样我就有一个包含所有客户特定细节的中心对象,我可以将其放入 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(...));
}

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

或者有没有更好的方法来添加自定义钩子,同时让我以客户为基础集中偏差?

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

4

3 回答 3

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

高温高压

于 2009-09-02T17:19:57.233 回答
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
    }
}
于 2009-09-02T06:32:07.403 回答
0

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

我能够将所有客户特定的逻辑放在每个客户的一个类中,而我的控制器订阅/取消订阅相应客户的特定功能——如果有的话。为我的经理课程提供活动是最简单的部分。

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

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

这确实不是很优雅,但也没有那么糟糕。

于 2009-09-02T14:18:20.623 回答