Esiste un modello di progettazione che posso utilizzare per semplificare molti metodi nidificati

1

Sto tentando di ridefinire qualche codice legacy che è un bel casino. Stiamo parlando di una classe con 7000 linee e una manciata di metodi che stanno violando il Principio di Responsabilità Unica, così tanto da far male. Sono riuscito a suddividerlo un po ', ma trovo che sto principalmente rimescolando il codice in classi separate - la complessità è ancora lì. I miei metodi stanno ottenendo enormi liste di parametri perché tutto è così intricato.

Il mio obiettivo è aggiungere test di unità, ma anche rendere il codice più gestibile come risultato.

Sto lavorando attraverso il codice dai "punti più bassi" (quelli che toccano un database) e cercando di ricalcolarli in classi che fanno cose specifiche. L'accesso al database è semplice e facile, posso inserirlo in una classe "DataAccess".

Tuttavia, ho problemi con alcuni altri metodi. Ci sono alcune aree che stanno facendo un'unica cosa generale, ad esempio: caricamento di alcuni dati. Il mio problema è che questi metodi si chiamano tutti in una lunga catena.

Ad esempio, ecco alcuni esempi di psudocode che dimostrano ciò che ho:

class SensorDataLoader
{
    //injected dependency for data access
    private readonly DataAccess dataAccess;

    // loads all sensors
    public void LoadAllSensorData(int dataSetId, LoadParametersObject loadParameters, DataStoreObject dataStore, Configuration configuration, Report report)
    {
        foreach (var sensorDataStore in dataStore.sensorsToLoad)
        {
            if (!sensorDataStore.HasDataToLoad) continue; // these parameters have lots of complexity. I've cut out a lot of stuff like this that access the configuration objects, or the data store objects.

            LoadSensorData(dataSetId, sensorDataStore, configuration.SensorConfiguration);
        }
    }

    // load a single sensor (i.e. all it's 'tracks')
    public void LoadSensorData(int dataSetId, SensorDataStoreObject sensorDataStore, SensorConfiguration sensorConfiguration, Report report)
    {
        foreach (var trackDataStore in sensorDataStore.tracks)
        {
            // here I've tried to create some dependencies that I can mock to test later methods
            var trackNumberAllocator = new TrackNumberAllocator(trackDataStore.trackId, sensorConfiguration);
            var postProcessor = new TrackPostProcessor(sensorConfiguration, trackDataStore.trackId, trackNumberAllocator);

            if (sensorConfiguration.someCondition)
            {
                LoadTrackData(dataSetId, trackDataStore, postProcessor, sensorConfiguration, report);
            }
            else
            {
                report.Report(...);
            }
        }
    }

    // loads a single 'track' in the sensor
    public void LoadTrackData(int dataSetId, TrackDataStoreObject trackDataStore, PostProcessor postProcessor, SensorConfiguration sensorConfiguration, Report report)
    {
        //get the data
        var data = this.dataAccess.GetData(dataSetId, sensorConfiguration);

        postProcessor.PostProcess(data);

        postProcessor.CalculateOffsets(data);

        report.Report(...);
    }
}

Ho tre metodi, quell'area tutto carica un sensore (se li divido in classi separate ho praticamente bisogno di avere una classe separata per ogni singolo metodo in questo codice!)

Voglio testare i metodi separatamente, così posso testare le funzionalità di ognuno, ma sono concatenati insieme.

Dopo aver lottato per districare questo codice, ho anche realizzato che la struttura di queste classi è anche realmente accoppiata con la strucutre degli oggetti di configurazione e degli oggetti 'data store'.

Tutti questi metodi prendono un oggetto molto complesso e lo mutano in qualche modo. È davvero complicato districarli a causa di ciò.

Ci sono schemi di progettazione che potrebbero aiutarmi con questo? Penso di essere un po 'troppo ingenuo nel solo tentativo di suddividerli in classi separate e qualcosa di più drastico è necessario per separare le preoccupazioni, ma sto cercando di trovare un modello di design che si adatti.

    
posta Joe 14.12.2017 - 12:32
fonte

2 risposte

4

Considerando solo il tuo esempio dato, penso che questo risultato sia abbastanza buono e non violi SRP, perché hai una classe SensorLoader con la responsabilità di raccogliere tutti i dati dai sensori.

Farei quanto segue, dato l'esempio di codice:

  • Rendi pubblico solo il metodo LoadAllSensorsData ;
  • Crea unit test trasmettendo stub (o mock) per gli oggetti DataStore e Configuration;
  • Facendo quanto sopra, testerai il metodo principale e, di conseguenza, testerai gli altri metodi privati che sono chiamati internamente;
  • Il tuo esempio di codice segue l'SRP e il DIP (dependency injection - il codice deve dipendere dalle astrazioni)

Riguardo ad altre parti del tuo codice legacy, se hai un codice complesso con un sacco di accoppiamento tra oggetti diversi, potresti (dopo averlo diviso in classi separate come hai fatto nell'esempio) usare modelli come Mediator se necessario, ma suppongo se hai rifatto il refactoring allo stesso modo del sensore, andrà tutto bene.

    
risposta data 14.12.2017 - 12:45
fonte
1

I metodi nidificati possono rendere difficile il refactoring. Un modo per aggirare questo è sostituire le chiamate di metodo con il contenuto del metodo. Quindi sei sicuro che nulla è stato infranto e spesso trovo più semplice una grande funzione da refactoring. Quindi puoi decidere come suddividere il codice in funzioni più piccole che sono più facili da testare.

    
risposta data 14.12.2017 - 19:24
fonte

Leggi altre domande sui tag