问题描述
我正在处理一些似乎违反开闭原则的旧版 dotnet 框架 4.7 代码。报表类做几乎相同的事情:从数据库中获取一些项目,运行一些转换并在 GetItems 方法中返回它们。我被要求添加另一个报告,但这意味着更改不断增长的 ReportManager 类。报告需要不同的参数,这让我很难看到我该如何前进。 我该如何重构它,使其不会破坏 SOLID 中的 O?
更新
在两个报告中都添加了对 GetItems() 的缺失调用,以及对 _repository 的调用。
public interface IReport
{
string GetItems();
}
public class StoreItemsReport : IReport
{
private readonly int[] _numbers;
private readonly IRepository _repository;
public StoreItemsReport(IRepository repo,int[] numbers)
{
this._repository = repo;
this._numbers = numbers;
}
public string GetItems()
{
return _repository.GetStoreItems(numbers);
}
}
public class OnlineItemsReport : IReport
{
private readonly string _account;
private readonly IRepository _repository;
public OnlineItemsReport(IRepository repo,string account)
{
this._repository = repo;
this._account = account;
}
public string GetItems()
{
return _repository.GetonlineItems(account);
}
}
public class ReportManager
{
private readonly IRepository repository;
public ReportManager(IRepository repository)
{
this.repository = repository;
}
public void HandlesupplierItems(int[] numbers)
{
var items = new StoreItemsReport(repository,numbers).GetItems();
Utils.SendData("storeitems-url",items);
Emailer.SendReport(items);
}
public void HandleStoreItems(string account)
{
var items = new OnlineItemsReport(repository,account).GetItems();
Utils.SendData("onlineitems-url",items);
Emailer.SendReport(items);
}
// and so on
}
// I would like to do something like this,but how do I add arguments to the providers
var provider = providerFactory.GetProvider("suppliers");
var items = provider.GetItems();
Utils.SendData("url",items);
Emailer.SendReport(items);
解决方法
您可以让您的 ReportManager
接受工厂方法:
public class ReportManager
{
private readonly IRepository repository;
public ReportManager(IRepository repository)
{
this.repository = repository;
}
public void HandleItems(Func<IRepository,IReport> factory,string url)
{
var items = factory(repository).GetItems();
Utils.SendData(url,items);
Emailer.SendReport(items);
}
}
可以这样使用:
var numbers = new[] { 1,2,3 };
reportManager.HandleItems(repo => new StoreItemsReport(repo,numbers),"storeitems-url");
,
如果您将 IRepository
设为 IReport
接口的公共属性,您只需为 IReport
提供一个新的 ReportManager
实例:
public interface IReport
{
string GetItems();
IRepository Repository {get;set;}
string InfoString {get;}
}
public class StoreItemsReport : IReport
{
private readonly int[] _numbers;
public IRepository Repository {get;set;}
public InfoString => "storeitems-url";
public StoreItemsReport(int[] numbers)
{
this._numbers = numbers;
}
public string GetItems()
{
return Repository.GetOnlineItems(account);
}
}
public class OnlineItemsReport : IReport
{
private readonly string _account;
public IRepository Repository {get;set;}
public InfoString => "onlineitems-url";
public OnlineItemsReport(string account)
{
this._account = account;
}
public string GetItems()
{
return Repository.GetStoreItems(numbers);
}
}
public class ReportManager
{
private readonly IRepository repository;
public ReportManager(IRepository repository)
{
this.repository = repository;
}
public void HandleItems(IReport report)
{
report.Repository = repository;
var items = report.GetItems();
Utils.SendData(report.InfoString,items);
Emailer.SendReport(items);
}
}
用法是
var report = new OnlineItemsReport("test");
reportManager.HandleItems(report);