重构对象列表以实现业务规则

时间:2011-05-27 14:23:57

标签: c# linq refactoring

我需要重构以下类:

public interface IEmployee
{
    int VacationWeeks { get; }
    int YearsWithCompany { set; get; }
    double Salary { set; get; }
}

public class Employee : IEmployee
{
    private readonly int vacationWeeks;

    public Employee(int vacationWeeks)
    {
        this.vacationWeeks = vacationWeeks;
    }

    public int VacationWeeks
    {
        get { return vacationWeeks; }
    }

    public int YearsWithCompany { set; get; }
    public double Salary { set; get; }
}

我需要确保VacationWeeks仅依赖于YearsWithCompany,并且我正在从数据库加载映射。到目前为止,我已经想出了这个:

public class EmployeeNew : IEmployee
{
    private Dictionary<int,int> vacationWeeksTable;

    public EmployeeNew(Dictionary<int, int> vacationWeeksTable)
    {
        this.vacationWeeksTable = vacationWeeksTable;
    }

    public int VacationWeeks
    {
        get { return vacationWeeksTable[YearsWithCompany]; }
    }

    public int YearsWithCompany { set; get; }
    public double Salary { set; get; }
}

这个类实现了我想要的东西,但它仍然有一个漏洞:同一个集合中的EmployeeNew的不同实例可能是用vacationWeeksTable的不同实例创建的。

同一集合中所有EmployeeNew实例必须引用相同的vacationWeeksTable。

我正在重构的应用程序在整个系统中使用大量的List,我们需要能够修改YearsWithCompany和Salary,但是要保证每个List只使用一个vacationWeeksTable。这些列表被重复多次;每次迭代都会修改其元素。

这是我不完美的解决方案。欢迎提出建议:

// this class does two things, which I do not like
    public class EmployeeList : IEnumerable<IEmployee>, IEmployee
    {
        private Dictionary<int, int> vacationWeeksTable;
        private List<EmployeeSpecificData> employees;
        private int currentIndex;
        private EmployeeSpecificData CurrentEmployee
        {
            get { return employees[currentIndex]; }
        }

        public IEnumerator<IEmployee> GetEnumerator()
        {
            for (currentIndex = 0; currentIndex < employees.Count; currentIndex++)
            {
                yield return this;
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public int VacationWeeks
        {
            get { return vacationWeeksTable[YearsWithCompany]; }
        }

        // this is ugly repetitive code I don't like
        public int YearsWithCompany
        {
            get { return CurrentEmployee.YearsWithCompany; }
            set { CurrentEmployee.YearsWithCompany = value; }
        }

        // this is ugly repetitive code I don't like
        public double Salary
        {
            get { return CurrentEmployee.Salary; }
            set { CurrentEmployee.Salary = value; }
        }


    }

2 个答案:

答案 0 :(得分:2)

创建一个包含Dictionary的类。创建或获取此新类的实例将以一致的方式加载字典。然后你的BO可以获取类的实例,从而确保它们都使用相同的数据(因为包含列表的类知道如何使用适当的数据集加载自身)。

答案 1 :(得分:2)

我使用以下内容来创建和初始化一些需要默认和共享行为的类。也许如果你能重构它会有所帮助:

它是FactoryFlyWeight模式的某种形式组合(可以在您的场景中删除flyweight部分),此外还有类Type共享处理程序的概念。

我简化并删除了一些你不需要的东西,但还有更多东西需要删除,我添加了评论。

用法是:(app init)

Dictionary<int,int> vacationWeeksTable = new Dictionary<int,int>();
// fill the table
Factory<Employee>.Init(vacationWeeksTable);

无论何时创建Employee类:

// remove grouping in the factory class to remove this null
Employee em = Factory<Employee>.Create(null);

只需要对类进行WeakReference,因此您不必担心GC。

每位员工在创建时都会设置共享的vacationWeeksTable,如果不使用工厂类,则无法在外部更改。

您可以随时在应用程序的运行时中更改所有正在运行的Employee实例的休假表:

// this will call the method registered for SetInitialdata on all instances of Employee classes.
// again remove grouping to remove that null
Factory<Employee>.Call(EventHandlerTypes.SetInitialData, null, vacTable);

员工的示例实施:

class Employee : IBaseClass
{
    private Dictionary<int, int> vacationWeeksTable;

    public virtual void RegisterSharedHandlers(int? group, Action<IKey, int?, EventHandlerTypes, Action<object, SharedEventArgs>> register)
    {
        group = 0; // disable different groups
        register(new Key<Employee, int>(0), group, EventHandlerTypes.SetInitialData, SetVacationWeeksTable);
    }

    public virtual void RegisterSharedData(Action<IKey, object> regData)
    {
        // remove this from factory and interface, you probably dont need it
        // I have been using it as a FlyWeight data store for classes.            
    }

    private void SetVacationWeeksTable(object sender, SharedEventArgs e)
    {
        vacationWeeksTable = e.GetData<Dictionary<int, int>>();
    }

}

代码模式实施:

IBaseClass:我可以通过工厂实现的每个类的接口

public enum EventHandlerTypes
{
    SetInitialData // you can add additional shared handlers here and Factory<C>.Call - it.
}

public class SharedEventArgs : EventArgs
{
    private object data;

    public SharedEventArgs(object data)
    {
        this.data = data;
    }

    public T GetData<T>()
    {
        return (T)data;
    }
}

public interface IBaseClass
{
    void RegisterSharedHandlers(int? group, Action<IKey, int?, EventHandlerTypes, Action<object, SharedEventArgs>> regEvent);
    void RegisterSharedData(Action<IKey, object> regData);
}

实用程序泛型类:

public interface IKey
{
    Type GetKeyType();
    V GetValue<V>();
}

public class Key<T, V> : IKey
{
    public V ID { get; set; }
    public Key(V id)
    {
        ID = id;
    }

    public Type GetKeyType()
    {
        return typeof(T);
    }

    public Tp GetValue<Tp>()
    {
        return (Tp)(object)ID;
    }
}

public class Triple<T, V, Z>
{
    public T First { get; set; }
    public V Second { get; set; }
    public Z Third { get; set; }
    public Triple(T first, V second, Z third)
    {
        First = first;
        Second = second;
        Third = third;
    }
}

工厂类稍加修改以处理您的场景:

   public static class Factory<C> where C : IBaseClass, new()
    {
        private static object initialData;
        private static Dictionary<IKey, Triple<EventHandlerTypes, int, WeakReference>> handlers = new Dictionary<IKey, Triple<EventHandlerTypes, int, WeakReference>>();
        private static Dictionary<IKey, object> data = new Dictionary<IKey, object>();

        static Factory()
        {
            C newClass = new C();
            newClass.RegisterSharedData(registerSharedData);
        }

        public static void Init<IT>(IT initData)
        {
            initialData = initData;
        }

        public static Dt[] GetData<Dt>()
        {
            var dataList = from d in data where d.Key.GetKeyType() == typeof(Dt) select d.Value;

            return dataList.Cast<Dt>().ToArray();
        }

        private static void registerSharedData(IKey key, object value)
        {
            data.Add(key, value);
        }

        public static C Create(int? group)
        {
            C newClass = new C();
            newClass.RegisterSharedHandlers(group, registerSharedHandlers);
            // this is a bit bad here since it will call it on all instances
            // it would be better if you can call this from outside after creating all the classes
            Factory<C>.Call(EventHandlerTypes.SetInitialData, null, initialData);
            return newClass;
        }

        private static void registerSharedHandlers(IKey subscriber, int? group, EventHandlerTypes type, Action<object, SharedEventArgs> handler)
        {
            handlers.Add(subscriber, new Triple<EventHandlerTypes, int, WeakReference>(type, group ?? -1, new WeakReference(handler)));
        }

        public static void Call<N>(EventHandlerTypes type, int? group, N data)
        {
            Call<N>(null, type, group, data);
        }

        public static void Call<N>(object sender, EventHandlerTypes type, int? group, N data)
        {
            lock (handlers)
            {
                var invalid = from h in handlers where h.Value.Third.Target == null select h.Key;

                // delete expired references
                foreach (var inv in invalid.ToList()) handlers.Remove(inv);

                var events = from h in handlers where h.Value.First == type && (!@group.HasValue || h.Value.Second == (int)@group) select h.Value.Third;

                foreach (var ev in events.ToList())
                {
                    // call the handler
                    ((Action<object, SharedEventArgs>)ev.Target)(sender, arg);
                }
            }
        } 
    }