从方法中删除条件 "new" 关键字依赖项
Removing conditional "new" keyword dependency from a method
我目前正在学习 DI 和 IoC 原理。所以,我偶然发现了这样一种情况,我有一个方法具有无法通过构造函数注入的依赖项。另外,我不能将它作为方法参数传递,因为该实例的创建是有条件的+它只能用它自己的参数实例化。这是我的 Employee 和 WorkYear classes 的超级简化版本:
public abstract class Employee
{
private List<WorkYear> _workYears;
// other private fields....
protected Employee(IDependency1 dep, etc...)
{
WorkYears = new List<WorkYear>();
// other components initialization....
}
public IEnumerable<WorkYear> WorkYears
{
get => _workYears.AsReadOnly();
private set => _workYears = value.ToList();
}
public void StartWorking(DateTime joinedCompany)
{
List<PayPeriod> periods = // Calculating periods...
WorkYear year = WorkYears.FirstOrDefault(y => y.CurrentYear == joinedCompany.Year);
if (year == null)
{
// Here is the problem:
year = new WorkYear(joinedCompany.Year, this, periods);
AddYear(year);
}
else
{
// Logic when year not null
}
year.RecalculateAllTime();
}
public void AddYear(WorkYear workYear) => _workYears.Add(workYear);
// More code...
}
public class WorkYear
{
public WorkYear(int currentYear, Employee employee, List<PayPeriod> periods)
{
Employee = employee;
EmployeeId = employee.Id;
CurrentYear = currentYear;
PayPeriods = periods ?? new List<PayPeriod>();
foreach (PayPeriod period in PayPeriods)
{
period.WorkYear = this;
period.WorkYearId = Id;
}
}
// More code...
}
如您所见,如果 Employee 还没有,我只需要一个新的 WorkYear 实例。我发现了一个建议使用简单工厂 class 来解决类似问题的线程。这样的解决方案可以工作,但是我如何处理没有这些参数就无法实例化 WorkYear 的参数?
很高兴看到有关如何解决此问题的示例。
我不完全确定你想做什么,或者为什么“新的工作年需要抽象”。你会看到我们真的不能走得太远。但是让我们来看看:
我们可以定义一个通用接口:
public interface IFactory<T, TData>
{
T Create(TData data);
}
然后描述我们的数据并实现:
public class WorkYearData
{
WorkYearData(int currentYear, Employee employee, List<PayPeriod> periods)
{
CurrentYear = currentYear;
Employee = employee;
Periods = periods;
}
public int CurrentYear { get; }
public Employee Employee { get; }
public List<PayPeriod> Periods { get; }
}
public class WorkYearFactory : IFactory<WorkYear, WorkYearData>
{
public WorkYear Create(WorkYearData data)
=> new WorkYear(data.CurrentYear, data.Employee, data.Periods);
}
最后更新员工
//Then
year = new WorkYear(joinedCompany.Year, this, periods);
//Now
year = _factory.Create(new WorkYearData(joinedCompany.Year, this, periods));
如您所见,我们实际上并没有抽象太多,只是添加了间接。
这是使用界面隐藏真实对象的替代方法。
创建一个界面来代表您的工作年限:
public interface IWorkYear
{
int CurrentYear { get; }
void RecalculateAllTime();
}
让你的 WorkYear
class 实施它:
public class WorkYear : IWorkYear
{
//Implementation code here
}
创建一个接口来表示提供者:
public interface IWorkYearProvider
{
IWorkYear CreateNewWorkYear(int year, Employee employee, List<PayPeriod> periods);
}
让你的 Employee
class 使用 IWorkYear
接口而不是实际对象:
public abstract class Employee
{
private List<IWorkYear> _workYears;
//Notice the dependency injection here
public Employee(IWorkYearProvider workYearProvider)
{
WorkYearProvider = workYearProvider;
}
//Reference to the dependency
public IWorkYearProvider WorkYearProvider { get; private set; }
public IEnumerable<IWorkYear> WorkYears { get => _workYears.AsReadOnly(); private set => _workYears = value.ToList(); }
public object Id { get; internal set; }
public void StartWorking(DateTime joinedCompany)
{
List<PayPeriod> periods = null; // Calculating periods...
IWorkYear year = WorkYears.FirstOrDefault(y => y.CurrentYear == joinedCompany.Year);
if (year == null)
{
// Use the dependency to create the object
year = WorkYearProvider.CreateNewWorkYear(joinedCompany.Year, this, periods);
_workYears.Add(year);
}
else
{
// Logic when year not null
}
year.RecalculateAllTime();
}
// More code...
}
我创建的几个测试 classes 来说明:
public class SampleEmployee : Employee
{
public SampleEmployee(IWorkYearProvider provider) : base(provider) { }
}
public class SampleWorkYearProvider : IWorkYearProvider
{
public IWorkYear CreateNewWorkYear(int year, Employee employee, List<PayPeriod> periods)
{
IWorkYear workYear = null;
workYear = new WorkYear(year, employee, periods);
return workYear;
}
}
示例用法:
//Create your provider
IWorkYearProvider provider = new SampleWorkYearProvider();
//Inject it as a dependency
var employee = new SampleEmployee(provider);
//Call the method
employee.StartWorking(DateTime.Today);
创建现在被抽象出来了。
我更愿意建议您重新考虑您的设计。抽象出 StartWorking
.
中的整个逻辑可能更有意义
首先,使用 WorkYear
的实例注入您的代码不会使您的代码独立于 WorkYear
,因为 Employee
仍然知道这个 class(如list List<WorkYear> WorkYears
例如),因此,如果你需要 DI,你应该与代表此 class 的接口进行对话,例如 IWorkYear
(这里的列表将改为 List<IWorkYear> WorkYears
)。
现在注入和实例化:
创建工厂 class 如下:
public static class MyFactory
{
public static object Create(Type classType, object[] data = null)
{
// create the class by reflection
}
}
(要通过反射创建一个 class 检查这个:Activator.CreateInstance)
然后更改方法的签名StartWorking
以便将类型注入到方法中:
public void StartWorking(Type workYearType, DateTime joinedCompany)
和行:
year = new WorkYear(joinedCompany.Year, this, periods);
将是:
year = MyFactory.Create(classType, new object[]{ joinedCompany.Year, this, periods});
我目前正在学习 DI 和 IoC 原理。所以,我偶然发现了这样一种情况,我有一个方法具有无法通过构造函数注入的依赖项。另外,我不能将它作为方法参数传递,因为该实例的创建是有条件的+它只能用它自己的参数实例化。这是我的 Employee 和 WorkYear classes 的超级简化版本:
public abstract class Employee
{
private List<WorkYear> _workYears;
// other private fields....
protected Employee(IDependency1 dep, etc...)
{
WorkYears = new List<WorkYear>();
// other components initialization....
}
public IEnumerable<WorkYear> WorkYears
{
get => _workYears.AsReadOnly();
private set => _workYears = value.ToList();
}
public void StartWorking(DateTime joinedCompany)
{
List<PayPeriod> periods = // Calculating periods...
WorkYear year = WorkYears.FirstOrDefault(y => y.CurrentYear == joinedCompany.Year);
if (year == null)
{
// Here is the problem:
year = new WorkYear(joinedCompany.Year, this, periods);
AddYear(year);
}
else
{
// Logic when year not null
}
year.RecalculateAllTime();
}
public void AddYear(WorkYear workYear) => _workYears.Add(workYear);
// More code...
}
public class WorkYear
{
public WorkYear(int currentYear, Employee employee, List<PayPeriod> periods)
{
Employee = employee;
EmployeeId = employee.Id;
CurrentYear = currentYear;
PayPeriods = periods ?? new List<PayPeriod>();
foreach (PayPeriod period in PayPeriods)
{
period.WorkYear = this;
period.WorkYearId = Id;
}
}
// More code...
}
如您所见,如果 Employee 还没有,我只需要一个新的 WorkYear 实例。我发现了一个建议使用简单工厂 class 来解决类似问题的线程。这样的解决方案可以工作,但是我如何处理没有这些参数就无法实例化 WorkYear 的参数?
很高兴看到有关如何解决此问题的示例。
我不完全确定你想做什么,或者为什么“新的工作年需要抽象”。你会看到我们真的不能走得太远。但是让我们来看看:
我们可以定义一个通用接口:
public interface IFactory<T, TData>
{
T Create(TData data);
}
然后描述我们的数据并实现:
public class WorkYearData
{
WorkYearData(int currentYear, Employee employee, List<PayPeriod> periods)
{
CurrentYear = currentYear;
Employee = employee;
Periods = periods;
}
public int CurrentYear { get; }
public Employee Employee { get; }
public List<PayPeriod> Periods { get; }
}
public class WorkYearFactory : IFactory<WorkYear, WorkYearData>
{
public WorkYear Create(WorkYearData data)
=> new WorkYear(data.CurrentYear, data.Employee, data.Periods);
}
最后更新员工
//Then
year = new WorkYear(joinedCompany.Year, this, periods);
//Now
year = _factory.Create(new WorkYearData(joinedCompany.Year, this, periods));
如您所见,我们实际上并没有抽象太多,只是添加了间接。
这是使用界面隐藏真实对象的替代方法。
创建一个界面来代表您的工作年限:
public interface IWorkYear
{
int CurrentYear { get; }
void RecalculateAllTime();
}
让你的 WorkYear
class 实施它:
public class WorkYear : IWorkYear
{
//Implementation code here
}
创建一个接口来表示提供者:
public interface IWorkYearProvider
{
IWorkYear CreateNewWorkYear(int year, Employee employee, List<PayPeriod> periods);
}
让你的 Employee
class 使用 IWorkYear
接口而不是实际对象:
public abstract class Employee
{
private List<IWorkYear> _workYears;
//Notice the dependency injection here
public Employee(IWorkYearProvider workYearProvider)
{
WorkYearProvider = workYearProvider;
}
//Reference to the dependency
public IWorkYearProvider WorkYearProvider { get; private set; }
public IEnumerable<IWorkYear> WorkYears { get => _workYears.AsReadOnly(); private set => _workYears = value.ToList(); }
public object Id { get; internal set; }
public void StartWorking(DateTime joinedCompany)
{
List<PayPeriod> periods = null; // Calculating periods...
IWorkYear year = WorkYears.FirstOrDefault(y => y.CurrentYear == joinedCompany.Year);
if (year == null)
{
// Use the dependency to create the object
year = WorkYearProvider.CreateNewWorkYear(joinedCompany.Year, this, periods);
_workYears.Add(year);
}
else
{
// Logic when year not null
}
year.RecalculateAllTime();
}
// More code...
}
我创建的几个测试 classes 来说明:
public class SampleEmployee : Employee
{
public SampleEmployee(IWorkYearProvider provider) : base(provider) { }
}
public class SampleWorkYearProvider : IWorkYearProvider
{
public IWorkYear CreateNewWorkYear(int year, Employee employee, List<PayPeriod> periods)
{
IWorkYear workYear = null;
workYear = new WorkYear(year, employee, periods);
return workYear;
}
}
示例用法:
//Create your provider
IWorkYearProvider provider = new SampleWorkYearProvider();
//Inject it as a dependency
var employee = new SampleEmployee(provider);
//Call the method
employee.StartWorking(DateTime.Today);
创建现在被抽象出来了。
我更愿意建议您重新考虑您的设计。抽象出 StartWorking
.
首先,使用 WorkYear
的实例注入您的代码不会使您的代码独立于 WorkYear
,因为 Employee
仍然知道这个 class(如list List<WorkYear> WorkYears
例如),因此,如果你需要 DI,你应该与代表此 class 的接口进行对话,例如 IWorkYear
(这里的列表将改为 List<IWorkYear> WorkYears
)。
现在注入和实例化:
创建工厂 class 如下:
public static class MyFactory
{
public static object Create(Type classType, object[] data = null)
{
// create the class by reflection
}
}
(要通过反射创建一个 class 检查这个:Activator.CreateInstance)
然后更改方法的签名StartWorking
以便将类型注入到方法中:
public void StartWorking(Type workYearType, DateTime joinedCompany)
和行:
year = new WorkYear(joinedCompany.Year, this, periods);
将是:
year = MyFactory.Create(classType, new object[]{ joinedCompany.Year, this, periods});