Uso della riflessione e creazione di una nuova classe

3

Io e un amico abbiamo una discussione su quale sia la tecnica migliore da utilizzare nel seguente scenario:

Un'applicazione che dobbiamo testare ha un numero di componenti (chiamiamoli Eggs); ogni componente è un sacchetto di sottocomponenti predefiniti. Questi componenti sono l'input per la nostra applicazione, non è possibile modificare / annotare questi componenti all'origine.

Ora abbiamo un set di validatori per ogni sottocomponente. Non è programmaticamente possibile associare un uovo ai suoi validatori, quindi deve essere fatto manualmente per ogni nuovo uovo (ci sono un numero finito di uova). Una volta eseguita la mappatura, possiamo eseguire ogni validatore per ogni uovo per vedere se l'uovo è buono o marcio.

Ora, il mio modo di farlo è di avere un file di configurazione che consiste nella mappatura tra ogni Egg e i suoi validatori. Ad esempio:

{
  Egg1: [validator1, validator2]
  Egg2: [validator5, validator7, validator2]
}

Ora possiamo leggere questo file e usare le riflessioni per selezionare il validatore appropriato (dall'elenco di tutte le classi che implementano IValidator) per ogni Egg.

Il mio amico è contrario all'uso dei riflessi e preferisce aggiungere una nuova classe C # per ogni uovo ogni volta che viene aggiunto un nuovo tipo di uovo. La classe rappresenta l'uovo e gestisce i validatori appropriati per quell'uovo.

Qual è il modo migliore? Preferisco il metodo dei riflessi perché ogni volta che si presenta un nuovo tipo di Egg tutto ciò che dobbiamo fare è modificare la configurazione.

Per chiarire, un uovo è una selezione dall'elenco predefinito di sottocomponenti. Supponiamo che non ci saranno più sotto-componenti, e quindi non c'è bisogno di nuovi validatori. Non possiamo enumerare tutte le uova possibili perché ce ne sono troppe.

    
posta Likhit 09.12.2014 - 19:53
fonte

2 risposte

7

Which way is better?

In base a ciò che hai descritto, aggiungere una nuova classe è molto meglio.

why?

Perché la modifica di una configurazione è altrettanto difficile / dolorosa del codice di modifica, eccetto che è sempre più incline agli errori e richiede più lavoro per l'installazione - e nessuno entra nella tua azienda sa che diavolo è questa configurazione , o in che formato è, o dove trovarlo.

Ora, se si potesse usare la riflessione per ispezionare le uova e vedere quali componenti secondari hanno (e non si hanno requisiti di prestazione significativi), sembra una soluzione ancora migliore ...

    
risposta data 09.12.2014 - 20:21
fonte
2

Penso che le altre risposte siano un po 'troppo semplicistiche.

Il mio approccio sarebbe probabile per metterlo in configurazione nel codice. Cioè mantieni la stessa struttura dati che stavi pensando per il tuo file di configurazione, ma non metterlo in un file di configurazione esterno. Invece, costruiscilo nel codice.

In un mondo python, farei qualcosa di simile (non conosco c # abbastanza bene da sapere come appare lì.):

VALIDATORS_BY_TYPE = {
   "Egg1": [Validator1(), Validator2(), Validator3()],
   "Egg2": [Validator2(), Validator4(), Validator5()]
}

def validate_egg(egg):
    for validator in VALIDATORS_BY_TYPE[egg.type]:
        validator.validate(egg)

Che aspetto hanno le alternative? Supponiamo che tu abbia scritto classi per ogni tipo di uovo. Avresti qualcosa di simile:

class Egg(object):
   def __init__(self, egg):
       self.egg = egg

class Egg1(Egg):
   def validate(self):
       validator1.validate(self.egg)
       validator2.validate(self.egg)
       validator2.validate(self.egg)

class Egg2(Egg):
   def validate(self):
       validator2.validate(self.egg)
       validator4.validate(self.egg)
       validator5.validate(self.egg)

def MakeEgg(raw_egg):
    if raw_egg.type == 'Egg1':
         return Egg1(raw_egg)
    elif raw_egg.type == 'Egg2':
         return Egg2(raw_egg)
    else:
          raise "Unknown egg type!"

def validate_egg(raw_egg):
    MakeEgg(raw_egg).validate()

È molto più codice. Sarà anche molto più difficile da cambiare. Supponiamo che decidiamo che ci piacerebbe davvero accumulare gli errori per ogni tipo di errore. Se il primo validatore fallisce con il codice sopra, faremmo solo la cauzione. Ma invece, supponiamo di voler aggiungere tutti gli errori in una lista e metterli insieme. Con il metodo di configurazione, devi solo modificare validate_egg . Se hai scritto classi, dovrai riscrivere tutte le sottoclassi Egg per aggiungere la logica dell'accumulazione.

Che ne dici di utilizzare un file di configurazione esterno? Un file di configurazione esterno aggiunge complessità. Qui, posso costruire direttamente la mia struttura dati, completa di riferimenti agli oggetti validator. Se volessi implementare un file di configurazione dovrei fare qualcosa del tipo:

validator_config = json.load(config_files.get_path("validator_config_file.json"))
VALIDATORS_BY_TYPE = {egg_type: [getattr(validators, name) for name in names] 
                      for egg_type, names in validator_config} 

E questo si guasterà e diventerà più complesso se inizierai a dover aggiungere parametri ai validatori o ad altri metadati.

D'altra parte, i file di configurazione hanno il vantaggio di essere più facili da manipolare a livello di programmazione. Supponiamo di voler dare una descrizione agli EggTypes, dovremmo riscrivere il nostro blocco di configurazione per essere qualcosa del tipo:

VALIDATORS_BY_TYPE = {
   "Egg1": EggType("Scrambled", [Validator1(), Validator2(), Validator3()),
   "Egg2": EggType("Sunny-side up", [Validator2(), Validator4(), Validator5()])
}

Questo sarà un fastidioso riarrangiamento che dovrai fare a mano. D'altra parte, scrivendo uno script usa e getta che analizza un file di configurazione di JSON, lo manipola nel nuovo formato e l'output di una sostituzione è semplice.

Tuttavia, è necessario fare attenzione nell'adottare un approccio basato sulla configurazione. Spesso è difficile trovare una configurazione che gestisca tutti i casi. Quando non puoi gestire un caso particolare attraverso la tua configurazione, la tendenza è quella di aggiungere degli hack pazzeschi su tutto il tuo codice per gestirlo.

    
risposta data 09.12.2014 - 22:31
fonte

Leggi altre domande sui tag