Разрешение интерфейсов во время выполнения (непосредственно перед использованием)

#c# #oop #design-patterns

#c# #ооп #шаблоны проектирования

Вопрос:

Допустим, у меня есть следующий интерфейс

 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 . Я должен иметь возможность издеваться над ILeague в UT, и в будущем может появиться еще много реализаций ILeague, поэтому я хочу избежать модификации и повторного тестирования компонентов, которые их используют

Комментарии:

1. Да, трудно понять, почему вы разработали это именно так, какие проблемы вы разделяли и чего вы пытались достичь. Вероятно, вы хотели бы, чтобы ваш LeagueDataProvider был универсальным классом. хотя это трудно сказать

2. Я должен быть в состоянии издеваться над ILeague в моем UT. Это основная причина, по которой я рефакторингую этот код. И другая причина в том, что в будущем может появиться еще много реализаций ILeague, поэтому я хочу избежать модификации классов, которые их используют в будущем

3. Я бы не стал создавать LeagueDataProvider an ILeague , потому что это не так. Это более или менее своего рода фабрика. Вы хотите, чтобы он содержал или создавал ILeague , а не реализовывал один. Google «пример шаблона фабрики классов c #»

4. @Andy Это была моя первоначальная мысль, сделать LeagueDataProvider в качестве фабрики, которая возвращает ILeague, которую я могу использовать. Но чтобы написать UT, я должен заставить свою фабрику реализовать интерфейс и издеваться над ним в моем UT. Я не был так уверен, что его хорошая практика для фабрики должна реализовывать интерфейс

5. Вы могли бы заставить его реализовать интерфейс. Нравится public interface ILeagueFactory { ILeague CreateLeague(); }

Ответ №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();
  }
}
  

Чтобы получить фабрику rigth league, вы можете обернуть нужные вам фабрики в коллекцию :

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

Ваш потребитель не изменится, когда вы добавите новую лигу