Created
November 25, 2020 08:41
-
-
Save mt89vein/607a5c35eb33abb00bb2d51a18b28f28 to your computer and use it in GitHub Desktop.
Default interface implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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