es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Cómo evitar pasar parámetros de tiempo de ejecución por todas partes.

Tengo una aplicación de cliente que permite que el usuario participe en un partido. En casi todos los casos, tal vez excepto en las pruebas, el usuario participa en uno o ningún partido al mismo tiempo. Cada partido tiene un ID único, vamos a llamarlo MatchID. Tengo muchos servicios que proporcionan información sobre el partido, por ejemplo:

interface IMatchPlayersRepository
{
    string[] GetMatchPlayers(string matchId);
    string GetBestPlayer(string matchId);
}

interface IMatchFieldsInfo
{
    int GetFieldSize(string matchId);
}

interface IMatchScoreboardProvider
{
    Scoreboard GetScoreboard(string matchId, string someOtherParam);
}

// Y muchos otros...

Las implementaciones de estos se utilizan entre sí, por ejemplo:

public class MatchScoreboardProvider : IMatchScoreboardProvider
{
    public MatchScoreboardProvider(IMatchPlayersRepository playersRepository)
    { //...
    }

    public Scoreboard GetScoreboard(string matchId, string someOtherParam)
    {
        var bestPlayer = _playersRepository.GetBestPlayer(matchId);
        //...
    }
}

¿Cómo evito pasar matchId en todas partes? El uso de fábricas solo traslada el problema a los métodos de fábrica, por ejemplo:

public class MatchPlayersRepositoryFactory
{
    public IMatchPlayersRepository Get(string matchId) => new MatchPlayersRepository(matchId);
}

public class MatchScoreboardProvider : IMatchScoreboardProvider
{
    // Creado desde la fábrica
    public MatchScoreboardProvider(string matchId, MatchPlayersRepositoryFactory playersRepositoryFactory)
    { //...
    }

    public Scoreboard GetScoreboard(string someOtherParam)
    {
        var bestPlayer = _playersRepositoryFactory.Get(_matchId).GetBestPlayer();
        //...
    }
}

Idealmente, tendría otra “raíz de composición” de inyección de dependencias para cada partido, donde todos los objetos se crean a partir de este nuevo marco de inyección de dependencias, que les pasa matchId. Sin embargo, crear una nueva “raíz de composición” parece ser una idea realmente mala, según entiendo.

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Una forma de resolver este problema es utilizar la inyección de dependencias (DI) y patrones de diseño como el patrón de fábrica y el patrón singleton.

    Primero, crea un servicio de coincidencia que encapsule todas las funcionalidades relacionadas con las coincidencias y las exponga a través de una única interfaz. Este servicio puede tener una instancia singleton que lleve un seguimiento del ID de coincidencia actual para no tener que pasarlo por todas partes. Aquí tienes una implementación de ejemplo:

    public interface IMatchService
    {
        string CurrentMatchId { get; }
        string[] GetMatchPlayers();
        string GetBestPlayer();
        int GetFieldSize();
        Scoreboard GetScoreboard(string someOtherParam);
    }
    
    public class MatchService : IMatchService
    {
        private readonly IMatchPlayersRepository _playersRepository;
        private readonly IMatchFieldsInfo _fieldsInfo;
        private readonly IMatchScoreboardProvider _scoreboardProvider;
    
        private static MatchService _instance;
    
        public string CurrentMatchId { get; private set; }
    
        private MatchService(string matchId)
        {
            CurrentMatchId = matchId;
            _playersRepository = new MatchPlayersRepository(matchId);
            _fieldsInfo = new MatchFieldsInfo(matchId);
            _scoreboardProvider = new MatchScoreboardProvider(_playersRepository);
        }
    
        public static MatchService GetInstance(string matchId)
        {
            return _instance ??= new MatchService(matchId);
        }
    
        public string[] GetMatchPlayers()
        {
            return _playersRepository.GetMatchPlayers(CurrentMatchId);
        }
    
        public string GetBestPlayer()
        {
            return _playersRepository.GetBestPlayer(CurrentMatchId);
        }
    
        public int GetFieldSize()
        {
            return _fieldsInfo.GetFieldSize(CurrentMatchId);
        }
    
        public Scoreboard GetScoreboard(string someOtherParam)
        {
            return _scoreboardProvider.GetScoreboard(CurrentMatchId, someOtherParam);
        }
    }
    

    La clase MatchService utiliza las clases MatchPlayersRepository, MatchFieldsInfo y MatchScoreboardProvider internamente para proporcionar la funcionalidad requerida por la interfaz IMatchService.

    El método GetInstance crea una instancia singleton de MatchService, pasando el matchId a su constructor para inicializar el ID de coincidencia actual. La propiedad CurrentMatchId expone el ID de coincidencia actual a todos los métodos del servicio.

    Ahora, en lugar de pasar el matchId a cada método de cada servicio, simplemente puedes crear una instancia de IMatchService donde la necesites y llamar a sus métodos sin preocuparte por el matchId. Aquí tienes un ejemplo:

    public class MatchController
    {
        private readonly IMatchService _matchService;
    
        public MatchController(IMatchService matchService)
        {
            _matchService = matchService;
        }
    
        public ActionResult Index()
        {
            var players = _matchService.GetMatchPlayers();
            var bestPlayer = _matchService.GetBestPlayer();
            var fieldSize = _matchService.GetFieldSize();
            var scoreboard = _matchService.GetScoreboard("someOtherParam");
    
            // ...
        }
    }
    

    La clase MatchController recibe una instancia de IMatchService en su constructor, que es inyectada por el framework de DI. Luego, el controlador puede llamar a los métodos de IMatchService sin pasar explícitamente el matchId.

    Este enfoque simplifica el código y reduce la cantidad de duplicación necesaria al pasar el matchId por todas partes. También facilita el cambio de la implementación de la interfaz IMatchService sin afectar al resto del código.

Comments are closed.