Best approach for the Design Pattern for multiple schedulers using interface and abstract class I have a requirement to fetch data for different types of Users (Customers, Publishers etc) for three different timelines (retro, current, and past), all have different URLs. There is a scheduler which will run at the different interval (for example 2 minute for retro data, 5 minutes for current data and 10 minutes for Future Dated Data). I have attached an image for better understanding.

 interface IDataPublisher { 
   void GetUrl(); 
   void Publish(); 
 }

  interface ITimeLineGrabber { 
     TimeSpan GetTimeSpanForNextRun(); 
  }

 abstract class BasePublisher : IDataPublisher, ITimeLineGrabber, BackgroundService 
 {
    private readonly ILogger&lt;BasePublisher&gt; _logger;
    public BasePublisher (ILogger&lt;BasePublisher&gt; logger)
    {
        _logger = logger;
    }

    public abstract string GetUserType();
    public abstract string GetDataType();
    public abstract TimeSpan GetTimeSpanForNextRun();
    public abstract string GetUrl();

    public override async Task StartAsync(CancellationToken cancellationToken)
    {
      await base.StartAsync(cancellationToken);
    }

    public override async Task StopAsync(CancellationToken cancellationToken)
    {
      await base.StopAsync(cancellationToken);
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
      var userType = GetUserType();
      var dataType = GetDataType();
      TimeSpan timeSpan = GetTimeSpanForNextRun();
     
      while (!stoppingToken.IsCancellationRequested)
      {
            try
            {
                // Do Something with userType and dataType 
                await Task.Delay(timeSpan , stoppingToken);
            }
            catch (Exception ex)
            {
                _logger.LogError($&quot;Error - {ex.Message}&quot;, DateTimeOffset.Now);
            }
        }
   }

  public sealed class CustomerRetroPublisher: BasePublisher 
  {
    public override string GetUserType()
    {
       return &quot;Customer&quot;;
    }

    public override string GetDataType()
    {
      return &quot;Retro&quot;;
    }

    public override Timespan GetTimeSpanForNextRun()
    {
      return Timespan.FromMinutes(2); // read from config file..
    }

    public override string GetUrl()
    {
       return &quot;CustomerRetroUrl&quot;;
    }
  }

but this will end up with 12 different classes and loads of code will be duplicated/ over complicated. I have a feeling there must be a simple approach to all this. Just wondering what could be the best approach for my requirement.
It seems not a good idea to hard code the different constants. I would inject them through the constructor (I am using properties instead of Get-methods):
public sealed class Publisher : interfaces
{
    public void Publisher (string userType, string dataType, Timespan interval, string url)
    {
        UserType = userType;
        DataType = dataType;
        Interval = interval;
        Url = url;
    }

    public string UserType { get; }
    public override string DataType { get; }
    public override Timespan Interval { get; }
    public override string Url { get; }
}

Then create a factory
public static class PublisherFactory
{
    public static Publisher CreateCustomerRetroPublisher()
    {
        return new Publisher(&quot;Customer&quot;, &quot;Retro&quot;, Timespan.FromMinutes(2), &quot;CustRetroUrl&quot;);
    }

    ...
}

If you need different behaviors for different publishers, you can inject them this way as well. For instance, inject services for timeline-specific behavior (through a ITimelineService) and user type-specific behavior (through a IUserService). Like this you can combine the different behavior types in different ways. 3 timeline services + 4 user services = 7 services is better than 12 different classes.
The constructor would then have additional parameters
public void Publisher (string userType, string dataType, Timespan interval, string url,
    ITimelineService timeLineService, IUserService userService)

The services can be assigned to private fields, as they do not need to be visible publicly.
See also: Composition over inheritance (Wikipedia)