简体   繁体   中英

c# polymorphism + generics design - advice needed

I have a hierarchy of TrendProviders which are used to provide a series of datapoints (trends) from different sources.

I have started with the generic interface:

public interface ITrendProvider<T> where T: TrendResult
{
    IEnumerable<T> GetTrends(); 
}

TrendResult is a result class with the obtained trend data and operation status code:

public class TrendResult
{
    public TrendResult()
    {
        Points = new Dictionary<DateTimeOffset, decimal>();
    }

    public TrendResultCode Code { get; set; }
    public string Name { get; set; }
    public string Identifier { get; set; }
    public Dictionary<DateTimeOffset, decimal> Points { get; set; }
}

TrendResultCode is as follows:

public enum TrendResultCode
{
    OK,
    DataParseError,
    NoData,
    UnknownError
}

There are also two types (so far) of more detailed interfaces:

  • IFileTrendProvider - used to obtain trends from any files.

  • IServerTrendProvider - used to obtain trends from remote servers, using FTP for example.

IFileTrendProvider goes like this:

public interface IFileTrendProvider : ITrendProvider<TrendResult>
{
    IFileReader FileReader {get; set}
    FileTrendDiscoveryPattern Pattern {get; set;}  
}

IServerTrendProvider goes like this:

public interface IServerTrendProvider : ITrendProvider<TrendResult>
{
    IClient Client { get; set; }
    Dictionary<string, string[]> RequestedServerTrendIDs { get; set; }
}

The concrete classes that implements the interfaces are as follows:

One of FileTrend type:

public class GenericFileTrendProvider : IFileTrendProvider<TrendResult>
{
    public GenericFileTrendProvider() { }

    public IFileReader FileReader {get; set}
    public FileTrendDiscoveryPattern Pattern {get; set;}  

    public IEnumerable<TrendResult> GetTrends()
    {
       // Some code to obtain trends from file using FileReader
    }
}

And one of the ServerTrend type:

public class ServerATrendProvider : IServerTrendProvider<TrendResult>
{
    public ServerATrendProvider () { }

    public IClient Client { get; set; }
    public Dictionary<string, string[]> RequestedServerTrendIDs { get; set;}

    public IEnumerable<TrendResult> GetTrends()
    {
       // Some code to obtain trends from remote server using Client
    }
}

Now, what I want to achieve and where I need your help and advice:

The IFileReader introduces its own error codes:

public enum FileReadResultCode
{
    OK,
    FileAlreadyOpen,
    FileNotFound,
    UnknownError
}

IClient also introduces its specific operation error codes:

public enum ClientCode
{
    OK,
    InvalidCredentials,
    ResourceNotFounds,
    UnknownError
}

As both: IFileReader and IClient also returns its own error codes, how could I intruduce them in the TrendResult object returned by GetTrends() method, so that in the end there will be also some information about the "internal" error, not only TrendResult specific error?

I would need some flexible solution - I was thinking about inheriting from TrendResult and create a FileTrendResult and ServerTrendResult for each of concrete providers, and also define a specific error enumerations for both so that they return its own result type, but can it be done in some other way?

Instead of having a pre-defined list of possible errors in the enumerations ( TrendResultCode , FileReadResultCode , ClientCode , ...) you could change the TrendResult class to store a list of exceptions and get rid of the enumerations:

public class TrendResult
{
    // ...    
    // public TrendResultCode Code { get; set; }
    public IEnumerable<Exception> Errors { get; set; }
    // ...
}

This way, you can store the complete information of the error including stack trace and other valuable information (or of several errors if there is more than one problem in a single TrendResult ). Even for unexpected errors, you are able to store the details instead of having an unknown error that is hard to track down.

If a TrendResult does not have any items in the enumeration, there are no problems with it. Otherwise, you can put the information to the log.

The implementation follows the Open/Closed principle , so you don't have to change the implementation that handles the TrendResults if there are new conditions that lead to an error.

For your business exceptions, eg parsing errors, you should create specific exception types so that you are able to analyze these errors later on based on their type.

Since IServerTrendProvider will be receiving TrendResults from other computers, you will not be able to use System.Exception in the general case.

In these cirumstances, people fall back to strings - which are always supported regardless of operating system.

public class TrendResult
{
   public string[] ErrorMessages;  // Error messages - could be the stack trace
   public int[]    ErrorCodes;     // Os-specific error numbers
}

Consider security when sending source-code fragments (eg names, stack traces) in error messages, because these could be of use to hackers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM