重构对象列表以实现业务规则(Refactoring a list of objects to implement a business rule)

我需要重构以下类:

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

I need to refactor the following class:

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

I need to make sure that VacationWeeks depends only on YearsWithCompany, and I am loading the mapping from the database. So far I have come up with this:

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

This class implements what I want, but it still has one vulnerability: different instances of EmployeeNew in the same collection may have been created with different instances of vacationWeeksTable.

All instances of EmployeeNew in the same collection must refer to the same vacationWeeksTable.

The application I am refactoring uses lots of List all over the system, and we need to be able to modify YearsWithCompany and Salary, yet to guarantee that only one vacationWeeksTable is used per List. These lists are iterated several times; its elements are modified in each iteration.

Here is my imperfect solution. Suggestions are welcome:

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

最满意答案

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

它结合了Factory和FlyWeight模式的某种形式(可以在您的场景中删除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); } } } }

I use the following to create and init some of the classes that need default and shared behaviour. Maybe if you can refactor it will help:

It is some form of the Factory and FlyWeight patterns combined (the flyweight part can be removed in your scenario), which in addition has a concept of class Type shared handlers.

I simplified and removed some stuff that you wont need but there is more to remove, I added comments.

Usage would be: (app init)

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

The whenever you create a Employee class:

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

It takes only a WeakReference to the classes so you don't have to worry about GC.

Each employee will have the shared vacationWeeksTable setup on creation, without the possibility to change it after from outside if not using the factory class.

You could change the vacation table for all running instances of Employee at any moment in the runtime of the app with:

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

Sample implementation of Employee:

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

Code pattern Implementation:

IBaseClass : interface that each of my classes that are creatable through a factory implement

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

Utility generic classes:

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

Factory class with slight modification to handle your scenario:

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

更多推荐