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

发布于 2024-08-04 10:03:05 字数 1087 浏览 9 评论 0原文

我想知道将自定义挂钩插入我的应用程序的最佳方法是什么。基本上,我的应用程序分为两个程序集:一个包含所有业务逻辑的 Core 程序集和一个包含 GUI、控制器类的 UserInterface 程序集(我使用的是异常的 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(...));
}

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

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

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

少年亿悲伤 2024-08-11 10:03:05

我认为你甚至可以在没有事件的情况下做到这一点(它们是你想要的结构吗?)。我尝试整理一些东西:尝试查看代码(不要太关心细节),如果您希望我详细说明,请告诉我...:-)

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

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

鹿! 2024-08-11 10:03:05

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

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

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
    }
}
小红帽 2024-08-11 10:03:05

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

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

唯一的缺点是,我必须手动添加/删除控制器类中的订阅处理。例如,我的 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;
    }
}

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文