开闭原则 - 如何使用提供者的参数进行重构

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