开闭原则 - 如何使用提供者的参数进行重构
Open-closed Principle - How to refactor with arguments to providers
我正在研究一些遗留的 dotnet 框架 4.7 代码,它似乎打破了开闭原则。报告 classes 做几乎相同的事情:从数据库中获取一些项目,运行一些转换并 returns 在 GetItems 方法中。我被要求添加另一个报告,但这意味着更改不断增长的 ReportManager class。这些报告需要不同的参数,这让我很难看清我该如何前进。
我怎样才能重构它以使其不破坏 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
界面的 public 属性,您只需将 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);
我正在研究一些遗留的 dotnet 框架 4.7 代码,它似乎打破了开闭原则。报告 classes 做几乎相同的事情:从数据库中获取一些项目,运行一些转换并 returns 在 GetItems 方法中。我被要求添加另一个报告,但这意味着更改不断增长的 ReportManager class。这些报告需要不同的参数,这让我很难看清我该如何前进。 我怎样才能重构它以使其不破坏 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
界面的 public 属性,您只需将 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);