Skip to content

Instantly share code, notes, and snippets.

@mt89vein
Created November 25, 2020 08:41
Show Gist options
  • Save mt89vein/607a5c35eb33abb00bb2d51a18b28f28 to your computer and use it in GitHub Desktop.
Save mt89vein/607a5c35eb33abb00bb2d51a18b28f28 to your computer and use it in GitHub Desktop.
Default interface implementation
public interface IMessageDto {} // базовый интерфейс для всех DTO, здесь пустой, но может иметь уникальный ид, время создания и т.п
public interface IRabbitMessage {} // интерфейс для нетипизированных сообщений, здесь пустой, но по задумке владеет информацией о том откуда сообщение получить (роуты, обменники)
public interface IRabbitMessage<T> : IRabbitMessage // типизированный вариант IRabbitMessage
where T : IMessageDto
{
}
// одна из конкретных реализаций, для примера
public class MetricsRabbitMessage : IRabbitMessage<MetricsDto> { }
public class MetricsDto : IMessageDto {}
// данные роутов, для публикации сообщения в RabbitMQ
public class RouteInfo {}
// теперь о проблематике: цель - дать возможность клиентскому коду расширить логику кастомным роутингом в удобном виде
// для регистрации в DI будет использоваться именованные инстансы через либу https://github.com/mt89vein/NamedResolver
// поэтому момент разрешения какая реализация интерфейса будет браться я опускаю, важны сейчас лишь сигнатуры методов
// т.к. в самой библиотеке требуется Generic метод, а не Generic интерфейс, чтобы проще было с ним работать в других классах:
public interface IRouteProvider
{
RouteInfo GetFor<TRabbitMessage, TMessage>(TMessage message, TimeSpan? delay = null)
where TRabbitMessage : IRabbitMessage
where TMessage : class, IMessageDto;
}
// таким образом если на стороне клиента позволить расширяться, то будет как-то так:
public sealed class MetricsRouteProvider : IRouteProvider
{
public RouteInfo GetFor<TRabbitMessage, TMessage>(TMessage message, TimeSpan? delay = null)
where TRabbitMessage : IRabbitMessage where TMessage : class, IMessageDto
{
// заметьте, что тут тип может прийти любой и чтобы получить данные сообщения, нужно будет кастовать
var metricsDto = (MetricsDto)message;
return new RouteInfo();
}
}
// но можно пойти другим путём используя возможности C# 8.0:
public interface IRouteProvider<TRabbitMessage, in TMessage> : IRouteProvider
where TRabbitMessage : IRabbitMessage
where TMessage : class, IMessageDto
{
// этот метод будет реализован в кастомных классах
RouteInfo GetFor(TMessage message, TimeSpan? delay = null);
// а явная реализация не generic интерфейса IRouteProvider прямо здесь. Это позволит не реализовывать его в самих классах
RouteInfo IRouteProvider.GetFor<TRmqMessage1, TMessageDto>(TMessageDto message, TimeSpan? delay)
{
return GetFor((message as TMessage)!, delay);
}
}
// и тогда у нас будет чистенький красивый клиентский код для кастомных роутингов:
public sealed class MetricsRouteProvider : IRouteProvider<MetricsRabbitMessage, MetricsDto>
{
public RouteInfo GetFor(MetricsDto message, TimeSpan? delay = null)
{
// здесь переопределение для конкретного типа
return new RouteInfo();
}
}
// а реализация по-умолчанию внутри либы будет такой, тут собственно знать конкретный тип не нужно, достаточно такого
public sealed class DefaultRouteProvider : IRouteProvider
{
public RouteInfo GetFor<TRabbitMessage, TMessage>(TMessage message, TimeSpan? delay = null)
where TRabbitMessage : IRabbitMessage where TMessage : class, IMessageDto
{
// тут дефолтная для всех реализация
return new RouteInfo();
}
}
// в качестве альтернативы, можно через наследование от абстрактного класса для этих же целей:
public abstract class RouteProviderBase<TRabbitMqMessage, TMessageDto> : IRouteProvider
where TMessageDto : class, IMessageDto
where TRabbitMqMessage : IRabbitMessage
{
public RouteInfo GetFor<TRabbitMessage, TMessage>(TMessage message, TimeSpan? delay = null)
where TRabbitMessage : IRabbitMessage
where TMessage : class, IMessageDto
{
return GetFor(message as TMessageDto, delay);
}
// заставляем клиента реализовать этот метод
protected abstract RouteInfo GetFor(TMessageDto messageDto, TimeSpan? delay = null);
}
// теперь клиент наследуется, а не реализует интерфейс.
public sealed class MetricsRouteProvider : RouteProviderBase<MetricsRabbitMessage, MetricsDto>
{
protected override RouteInfo GetFor(MetricsDto message, TimeSpan? delay = null)
{
// здесь переопределение для конкретного типа
return new RouteInfo();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment