【问题标题】:Resolving interfaces at runtime (just before the usage)在运行时解析接口(就在使用之前)
【发布时间】:2020-08-19 04:26:14
【问题描述】:

假设我有以下界面

interface ILeague
{
    string ShowSquad();
}

以下实现它们的类

class EPL : ILeague
{
    public string ShowSquad()
    {
      return "EPL players collection";
    }
} 

class LaLiga: ILeague
{
  public string ShowSquad()
  {
     return "La-liga player Collection";
  }
}

我正在消费这个界面,如下图

public string ShowLeaguePlayers(ILeague leagueDataProvider)
{
   return leagueDataProvider.ShowSquad();
}

现在,根据我所在的联赛,我想显示不同的数据。有时是 EPL,有时是 LaLiag。这种切换可以发生在同一个执行周期中。 我尝试了以下方法

class LeagueDataProvider : ILeague
{
    private ILeague m_Provider;

    private string league;
    private void SetContext()
    {
        // Have some logic to figure out the league
        league = "EPL";

        if (league.Equals("EPL"))
        {
            m_Provider = new EPL();
        }
        else
        {
            m_Provider = new LaLiga();
        }
    }

    public string ShowSquad()
    {
        SetContext();
        return m_Provider.ShowSquad();
    }
}

我已将我的客户端代码修改为以下

void ShowData()
{
   ILeague Dataprovider = new LeagueDataProvider();
   Console.WriteLine(ShowLeaguePlayers(Dataprovider));             
}
 
// copied again for easy viewing
public string ShowLeaguePlayers(ILeague leagueDataProvider)
{
   return leagueDataProvider.ShowSquad();
}

这很好用,但每次我调用 ShowSquad 时,它都必须检查联赛并获取数据。有没有更好的方法来做到这一点?

我想在这里实现什么:

根据联赛,我想在调用 ShowSquad 时获得不同的数据。 我应该能够在 UT 中模拟 ILeague,并且将来可能会有更多 ILeague 的实现,所以我想避免修改和重新测试消耗它们的组件

【问题讨论】:

  • 是的,很难知道你为什么要这样设计它,你要分离什么问题,以及你想要实现什么。您可能希望您的LeagueDataProvider 作为一个通用类。虽然很难说
  • 我应该可以在我的 UT 中模拟 ILeague。这是我重构这段代码的主要原因。其他原因是,未来可能会有更多的 ILeague 实现,所以我想避免修改将来使用它们的类
  • 我不会把LeagueDataProvider 变成ILeague,因为它不是。它或多或少是一个工厂。你希望它包含或产生一个ILeague,而不是实现一个。谷歌“c#类工厂模式示例”
  • @Andy 这是我最初的想法,将 LeagueDataProvider 作为一个工厂,返回一个我可以使用的 ILeague。但是要编写 UT,我应该让我的工厂实现一个接口并在我的 UT 中模拟它。我不太确定工厂的良好做法是否应该实现接口
  • 你可以让它实现一个接口。喜欢public interface ILeagueFactory { ILeague CreateLeague(); }

标签: c# oop design-patterns


【解决方案1】:

也许可以尝试使用工厂模式: 接口:

interface ILeague
{
    string ShowSquad();
}

interface ILeagueFactory
{
  ILeague CreateLeague();
}

实现:

class EPL : ILeague
{
    public string ShowSquad()
    {
      return "EPL players collection";
    }
} 

class LaLiga: ILeague
{
  public string ShowSquad()
  {
    return "La-liga player Collection";
  }
}

class EPLFactory : ILeagueFactory
{
  public ILeague CreateLeague()
  {
    return new EPL();
  }
}

class LaLigaFactory : ILeagueFactory
{
  public ILeague CreateLeague()
  {
    return new LaLiga();
  }
}

要获取正确的联盟工厂,您可以将所需的工厂包装在一个集合中:

class LeagueFactoryCollection
{
  private IDictionary<string, ILeagueFactory> factories;

  public LeagueFactoryCollection()
  {
    factories = new Dictionary<string, ILeagueFactory>();
  }

  public void Add(string key, ILeagueFactory factory)
  {
    factories.Add(key, factory);
  }

  public ILeagueFactory Get(string key)
  {
    return factories[key];
  }
}

现在您应该可以更轻松地添加新的联赛类型了:

//define your needed factories
var leagueCollections = new LeagueFactoryCollection();
leagueCollections.Add("EPL", new EPLFactory());
leagueCollections.Add("LaLiga", new LaLigaFactory());

//consumer
leagueCollections.Get("EPL").CreateLeague().ShowSquad();

当您添加新联赛时,您的消费者不会改变

【讨论】:

    猜你喜欢
    • 2020-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-22
    • 2022-02-03
    • 1970-01-01
    • 2012-09-05
    • 1970-01-01
    相关资源
    最近更新 更多