2009-09-02 1 views
3

Мне интересно, что лучше всего вставить в мое приложение вставки для настройки. В принципе, мое приложение разделено на две сборки: A Core сборка, содержащая всю бизнес-логику и сборку 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 для достижения этой цели, но это жизнеспособное для операций, которые должны быть сделаны во много раз?

Или есть ли лучшие способы добавить крючки настройки, в то же время лишая меня централизации отклонений на основе клиентов?

[EDIT] Полностью пересмотренный вопрос.

ответ

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

НТН

+0

Эй, Фабрицио! Прежде всего, спасибо за продуманный пример, я очень ценю это. Как вы думаете, каковы преимущества вашего подхода к событиям? Я также думал о чем-то подобном, но не любил понятие пустых методов в классе BaseCustomization. Одно из преимуществ, которое я вижу, - это интерфейс для всех настроек. –

+0

Я реализовал ваш подход, и мне это нравится! Это разумно и позволяет избежать некоторых проблем, которые мой подход имел. Большое вам спасибо за ваше время, вы действительно помогли мне! –

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

Но не повлияет ли это на абстрактные методы?Не похоже, что нет никаких случаев, когда общей функциональности недостаточно. Это больше похоже на то, что есть некоторые случаи, когда мне нужно добавить специфику, но большую часть времени общая функциональность просто прекрасна. Кроме того, мой объект-менеджер - это singleton, что означает, что я не могу просто создать несколько (производных) объектов и поменять их с помощью шаблона стратегии. –

+0

@ Майкл Барт - Тогда просто укажите конкретную реализацию метода крюка вместо того, чтобы сделать его абстрактным. В принципе, точка, которую я получаю, - это базовое наследование, которое должно решить вашу проблему. –

+0

У меня есть точка. Но, увы, это потребовало бы, чтобы я переписал другие части моего Приложения, поскольку он был разработан с учетом отдельных центральных классов диспетчера, которые предлагают функциональность и состояние состояния (заказов, в случае OrderManager). Расширение этого дизайна путем добавления определенного OrderManager для каждого клиента осложняло бы дизайн, поскольку мне нужно было бы синхронизировать состояние между этими объектами-менеджерами перед каждым свопом. Поэтому я хотел бы придерживаться отдельных уникальных экземпляров объекта-экземпляра, что исключает наследование как решение. Спасибо, в любом случае! –

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

Что на самом деле не очень элегантно, но это не так уж плохо.