Polimorfismo / Problemi di progettazione dell'ereditarietà

0

Ho un FtpServerDataSeriesProvider che viene utilizzato per fornire serie di dati da file server Ftp:

public interface class FtpServerDataSeriesProvider
{
    IEnumerable<DataSeries> GetDataSeries();
}

Ha diverse implementazioni concrete specifiche per la società proprietaria del server Ftp, come:

public class Company1FtpServerDataSeriesProvider : FtpServerDataSeriesProvider
{
    public IEnumerable<DataSeries> GetDataSeries()
    {
        // code to extract data series from ftp server files
    }
}

public class Company2FtpServerDataSeriesProvider : FtpServerDataSeriesProvider
{
    public IEnumerable<DataSeries> GetDataSeries()
    {
        // code to extract data series from ftp server files    
    }
}

Il processo di estrazione della serie di dati è gestito da un FileDataMiner che viene utilizzato per estrarre i dati dal contenuto di tipi di file specifici:

public interface IFileDataSeriesMiner
{
    IEnumerable<DataSeries> GetDataSeries(byte [] fileContent);
} 

Il parametro del metodo GetDataSeries() è un array di byte non elaborati che presenta il contenuto del file, all'interno di questo metodo sarebbe codificato / trasformato in MemoryStream a seconda del tipo di file targer.

// To get data from CSV-based file
public abstract CsvFileDataSeriesMiner : IFileDataMiner
{
    public abstract IEnumerable<DataSeries> GetDataSeries(byte [] fileContent);
    // here might be some protected csv-related helper methods
}

// To get data from Xls-based file
public abstract XlsFileDataSeriesMiner : IFileDataMiner
{
    public abstract IEnumerable<DataSeries> GetDataSeries(byte [] fileContent);
    // here might be some protected xls-related helper methods
}

Poi ci sono anche alcune classi concrete che rappresentano diversi formati di dati di file di input (ciascuna azienda memorizza i dati in un formato diverso):

public class Company1CsvFileDataSeriesMiner : CsvFileDataSeriesMiner
{
    public IEnumerable<DataSeries> GetDataSeries(byte [] fileContent)
    {
        // do actual stuff
    }       
}

public class Company2CsvFileDataSeriesMiner : CsvFileDataSeriesMiner
{
    public IEnumerable<DataSeries> GetDataSeries(byte [] fileContent)
    {
        // do actual stuff
    }       
}

public class Company1XlsFileDataSeriesMiner : XlsFileDataSeriesMiner
{
    public IEnumerable<DataSeries> GetDataSeries(byte [] fileContent)
    {
        // do actual stuff
    }       
}

public class Company2XlsFileDataSeriesMiner : XlsFileDataSeriesMiner
{
    public IEnumerable<DataSeries> GetDataSeries(byte [] fileContent)
    {
        // do actual stuff
    }       
}

Ora, per poter FtpServerDataSeriesProvider in grado di estrarre dati da specifici contenuti in formato file, ho bisogno di passare / iniettare IFileDataSeriesMiner in qualche modo al FtpServerDataSeriesProvider costruttori di calcestruzzo.

Ma, potrebbe essere che il FTPServer ci sono Csv e XLS (e altri) file presenti, quindi ho bisogno di essere in grado di selezionare la corretta IFileDataSeriesMiner dinamicamente all'interno% co_de codice% a seconda del formato del file attualmente in fase di elaborazione.

Quindi, le mie domande sono:

  1. Devo iniettare una fabbrica di FtpServerDataSeriesProvider in IFileDataSeriesMiner / Company1FtpServerDataSeriesProvider costruttori e quindi decidere quale oggetto minatore ho bisogno basandoci ad es. estensione del file?

Il factory del minatore per Company1 sarebbe simile a questo:

    public interface IFileDataSeriesMinerFactory
    {
        IFileDataSeriesMiner CreateMiner(string fileName); 
    }

    public class Company1FileDataSeriesMinerFactory : IFileDataSeriesMinerFactory
    {
        // basing on file extension suitable Miner object is created
        public static IFileDataSeriesMiner CreateMiner(string fileName); 
    }

    public class Company1FtpServerDataSeriesProvider : FtpServerDataSeriesProvider
    {
        private IFileDataSeriesMinerFactory minerFactory;

        public Company1FtpServerDataSeriesProvider(IFileDataSeriesMinerFactory minerFactory)
        {
            this.minerFactory = minerFactory;
        }

        // code ommited
    }
  1. Quando ci sono diversi file che devono essere elaborati all'interno del server, dovrei chiamare la fabbrica del minatore per ogni file che necessita di elaborazione, quindi per ogni file verrà creato un nuovo oggetto Miner - ok?

Codice semplificato per Company1FtpServerDataSeriesProvider:

    public class Company1FtpServerDataSeriesProvider : FtpServerDataSeriesProvider
    {
        private IFileDataSeriesMinerFactory minerFactory;

        public Company1FtpServerDataSeriesProvider(IFileDataSeriesMinerFactory minerFactory)
        {
            this.minerFactory = minerFactory;
        }

        public IEnumerable<DataSeries> GetDataSeries()
        {
            var ftpServerFilesToRead = ftpClient.GetFiles();

            foreach (string filepath in ftpServerFilesToRead)
            {
                byte[] fileBytes = ftpRequest.DownloadData(filepath); // Read file bytes
                var miner = minerFactory.CreateMiner(filename); // Create miner for current file to extract data series

                foreach (var dataSeries in miner.GetDataSeries(fileBytes))
                {
                    yield return dataSeries;
                }   
            }   
        }
    }
    
posta pitersmx 22.05.2018 - 14:35
fonte

1 risposta

3

Ecco come risolvere questo problema di strategia in lingue che includono il supporto per funzioni di prima classe, incluso C #. Eliminerà molta della cerimonia.

// This is just a stub class to demonstrate the use of a first-class function 
// to implement your Strategy Pattern.
public class UniversalDataMiner : IFileDataMiner
{
    Func<IFileDataSeriesMinerFactory, IEnumerable<DataSeries>> _getDataSeries;

    public UniversalDataMiner(Func<IFileDataSeriesMinerFactory, IEnumerable<DataSeries>> getDataSeries)
    {
        _getDataSeries = getDataSeries;
    }

    #region IFileDataMiner implementation
    IEnumerable<DataSeries> GetDataSeries()
    {
        return _getDataSeries();
    }
    #endregion
}

// This is the function that you will pass into the class above to implement
// the GetDataSeries functionality using FTP.
public IEnumerable<DataSeries> GetDataSeriesUsingFtp(IFileDataSeriesMinerFactory minerFactory)
{
    var ftpServerFilesToRead = ftpClient.GetFiles();

    foreach (string filepath in ftpServerFilesToRead)
    {
        byte[] fileBytes = ftpRequest.DownloadData(filepath); // Read file bytes
        var miner = minerFactory.CreateMiner(filename); // Create miner for current file to extract data series

        foreach (var dataSeries in miner.GetDataSeries(fileBytes))
        {
            yield return dataSeries;
        }   
    }   
}

// And this is how you set it up and use it.
IFileDataMiner miner = new UniversalDataMiner(GetDataSeriesUsingFtp);
IEnumerable<DataSeries> result = miner.GetDataSeries(myMinerFactory);
    
risposta data 23.05.2018 - 18:37
fonte

Leggi altre domande sui tag