Как лучше всего «украшать объекты функциональностью»? - PullRequest
2 голосов
/ 08 октября 2009

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

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

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

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 ]

2 голосов
/ 08 октября 2009

Я бы лично порекомендовал лучший шаблон дизайна, такой как шаблон посетителя, но за то, чего он стоит, вы можете заставить свой код работать, отказавшись от безопасности типов. Используйте 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);
    }
}
1 голос
/ 08 октября 2009

Это похоже на классический случай для шаблона посетителя .

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

Например, у вашего 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 голосов
/ 08 октября 2009

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

...