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.
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:
La clase
MatchService
utiliza las clasesMatchPlayersRepository
,MatchFieldsInfo
yMatchScoreboardProvider
internamente para proporcionar la funcionalidad requerida por la interfazIMatchService
.El método
GetInstance
crea una instancia singleton deMatchService
, pasando elmatchId
a su constructor para inicializar el ID de coincidencia actual. La propiedadCurrentMatchId
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 deIMatchService
donde la necesites y llamar a sus métodos sin preocuparte por elmatchId
. Aquí tienes un ejemplo:La clase
MatchController
recibe una instancia deIMatchService
en su constructor, que es inyectada por el framework de DI. Luego, el controlador puede llamar a los métodos deIMatchService
sin pasar explícitamente elmatchId
.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 interfazIMatchService
sin afectar al resto del código.