2009-10-08 1 views
2

я сделал пример, ниже которого позволяет завода упаковать объекты с функциональностью, но проблемы в том, что функциональность оторвана от объекта.Каков наилучший способ «украсить объекты функциональностью»?

Моя конечная цель присоединить функциональность, такую ​​как журнал и экономии и дисплей, который действует на специфические свойства, что каждый другой объект имеет.

Как бы я сохранил внешний вид этого примера, но включил такие функции, как «сохранение», который сохраняет данные объекта в базу данных или «журнал», который регистрирует его активность?

using System; 
using System.Collections.Generic; 

namespace FuncAdorn3923 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 

      Customer customer = new Customer(); 
      ObjectFactory.Instance.AdornFunctionality(customer, "add"); 
      Console.WriteLine(customer.CallAlgorithm("add", 64, 36)); 

      Employee employee = new Employee(); 
      ObjectFactory.Instance.AdornFunctionality(employee, "add"); 
      ObjectFactory.Instance.AdornFunctionality(employee, "subtract"); 
      Console.WriteLine(employee.CallAlgorithm("add", 5, 15)); 
      Console.WriteLine(employee.CallAlgorithm("subtract", 66, 16)); 

      Console.ReadLine(); 
     } 
    } 

    public class ObjectFactory 
    { 
     private static ObjectFactory singleton; 

     public void AdornFunctionality(AdornedObject ao, string idCode) 
     { 
      Func<int, int, int> add = (i, j) => i + j; 
      Func<int, int, int> subtract = (i, j) => i - j; 

      switch (idCode) 
      { 
       case "add": 
        ao.LoadAlgorithm(idCode, add); 
        break; 
       case "subtract": 
        ao.LoadAlgorithm(idCode, subtract); 
        break; 
      } 
     } 

     public static ObjectFactory Instance 
     { 
      get 
      { 
       if (singleton == null) 
        singleton = new ObjectFactory(); 
       return singleton; 
      } 
     } 

    } 

    public abstract class AdornedObject 
    { 
     private Dictionary<string, Func<int, int, int>> algorithms = 
      new Dictionary<string, Func<int, int, int>>(); 

     public void LoadAlgorithm(string idCode, Func<int,int,int> func) 
     { 
      algorithms.Add(idCode, func); 
     } 

     public int CallAlgorithm(string idCode, int i1, int i2) 
     { 
      Func<int,int,int> func = algorithms[idCode]; 
      return func.Invoke(i1, i2); 
     } 
    } 

    public class Customer : AdornedObject 
    { 
     public string FirstName { get; set; } 
     public string LastName { get; set; } 
     public int NumberOfProductsBought { get; set; } 
    } 

    public class Employee : AdornedObject 
    { 
     public string FirstName { get; set; } 
     public string LastName { get; set; } 
     public int Age { get; set; } 
    } 

} 
+3

я упускаю что-то или что методы расширения решить эту проблему немного более элегантно? – Joey

+0

Или, возможно, атрибуты. –

+0

На стороне примечание, я считаю, что JIT-экземпляр Singleton не является потокобезопасным. Правильный способ состоит в том, чтобы создать его как часть декларации поля; это гарантировано. –

ответ

2

Я лично рекомендовал бы лучший дизайн, как шаблон посетителя, но за что его стоит сделать, чтобы ваш код работал, отбросив тип безопасности. Используйте Delegate, а не его производных классов Func и Action:

static void Main(string[] args) 
    { 

     Customer customer = new Customer(); 
     ObjectFactory.Instance.AdornFunctionality(customer, "add"); 
     Console.WriteLine(customer.CallAlgorithm("add", 64, 36)); 

     Employee employee = new Employee(); 
     ObjectFactory.Instance.AdornFunctionality(employee, "add"); 
     ObjectFactory.Instance.AdornFunctionality(employee, "subtract"); 
     ObjectFactory.Instance.AdornFunctionality(employee, "save"); 
     Console.WriteLine(employee.CallAlgorithm("add", 5, 15)); 
     Console.WriteLine(employee.CallAlgorithm("subtract", 66, 16)); 
     Console.WriteLine(employee.CallAlgorithm("save")); 

     Console.ReadLine(); 
    } 
} 

public class ObjectFactory 
{ 
    private static ObjectFactory singleton; 

    public void AdornFunctionality(AdornedObject ao, string idCode) 
    { 
     Func<int, int, int> add = (i, j) => i + j; 
     Func<int, int, int> subtract = (i, j) => i - j; 
     Action save =() => Console.WriteLine("{0} has been saved", ao.ToString()); 

     switch (idCode) 
     { 
      case "add": 
       ao.LoadAlgorithm(idCode, add); 
       break; 
      case "subtract": 
       ao.LoadAlgorithm(idCode, subtract); 
       break; 
      case "save": 
       ao.LoadAlgorithm(idCode, save); 
       break; 
     } 
    } 

    public static ObjectFactory Instance 
    { 
     get 
     { 
      if (singleton == null) 
       singleton = new ObjectFactory(); 
      return singleton; 
     } 
    } 

} 

public abstract class AdornedObject 
{ 
    private Dictionary<string, Delegate> algorithms = new Dictionary<string, Delegate>(); 

    public void LoadAlgorithm(string idCode, Delegate func) 
    { 
     algorithms.Add(idCode, func); 
    } 

    public object CallAlgorithm(string idCode, params object[] args) 
    { 
     Delegate func = algorithms[idCode]; 
     return func.DynamicInvoke(args); 
    } 
} 
+0

Почему посетитель вместо декоратора? –

+1

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

1

Это выглядит как классический случай для visitor pattern.

Алгоритмы (посетители) должны быть адаптированы к объектам, которые они украшают (или посещают), или, по крайней мере, адаптированы к некоторому интерфейсу, который реализуют ваши украшенные объекты.

Например, ваш Employee объект может иметь метод, как следующее:

public class Employee: IEmployee { 
    public void Accept(IEmployeeAlgorithm algorithm) { 
     algorithm.Visit(this); 
    } 
} 

IEmployeeAlgorithm объекты будут иметь интерфейс, похожий на этот (они могут так же легко быть Action<Employee> делегаты, или использовать другие подписи в случае необходимости):

public interface IEmployeeAlgorithm { 
    void Visit(IEmployee employee); 
} 

Наконец, если вы хотите, чтобы дать ключи, алгоритмы и вызывать их динамически, вы могли бы сделать это способом, аналогичным тому, что у вас есть сейчас, храня их в IDictionary<string, IEmployeeAlgorithm> участник.

0

Я бы выбрал проект PostSharp. Они допускают такое разделение проблем и позволяют сделать несколько простых способов для этого. Они позволяют вам внешне определять код, который добавляется к классам/свойствам во время выполнения. Я не уверен в ваших конкретных требованиях (или в этом конкретном примере), но вы должны это проверить.