Python API Design per il software di automazione del carico di lavoro

-2

Voglio esporre le API in python per il mio software di automazione del carico di lavoro. Ho una classe di lavoro come definito / descritto di seguito. L'istanza di questa classe di lavoro rappresenta una particolare esecuzione di lavoro. Il lavoro può avere più checkpoint e ogni checkpoint può avere più comandi.

Job
 - JobName
 - [JobCheckpoint]
 - StartTime
 - EndTime
 - Status
 - ...

JobCheckpoint
 - JobCheckpointName
 - [JobCommand]
 - StartTime
 - EndTime
 - Status
 - ...

JobCommand
 - JobCommandName
 - [Command]
 - StartTime
 - EndTime
 - Status 
 - ...

Voglio le API utente per soddisfare i seguenti casi d'uso:

  1. Capacità di interrogare i lavori in base a un criterio (ad esempio, restituire lavori eseguiti in una determinata durata, restituire lavori in stato non riuscito, restituire checkpoint / comandi di un particolare lavoro e molti altri ...).
  2. Possibilità di creare ed eseguire lavori usando l'API Python.
  3. Possibilità di prendere azioni di controllo come rieseguire già il lavoro, uccidere il lavoro in esecuzione ecc.

Sto pensando di fornire il seguente metodo nella mia API utente.

get_jobs(Filter) - This serves/solves use-case#1

Domanda 1:

Non sono sicuro di come sia possibile progettare la classe Filter migliore. L'obiettivo è fornire agli utenti API intuitive e potenti.

Domanda 2:

Non sono sicuro di come dovrei risolvere il caso d'uso n. 2 e n. 3 sopra. Ho un paio di soluzioni nella mia mente, ma non sono sicuro di quale sia il migliore.

Soluzione # 1. Per creare lavori, gli utenti possono creare direttamente un'istanza dell'oggetto della classe Lavoro. Fornirò i metodi nella classe del modello di lavoro per eseguire e rieseguire il lavoro. Gli utenti creeranno istanze di metodi di classe e chiamata di lavoro (esegui, riesegui, uccidi ecc.) Sull'oggetto di lavoro.

Soluzione # 2. Insieme all'API get_job, esponi API separate per le azioni di controllo come eseguire e rieseguire nell'interfaccia utente.

get_job(Filter) 
run_job(Job)
rerun_job(Job)
...

Che cosa succede se in futuro desidero supportare molte più azioni di controllo sull'oggetto Job (per e.x. hold, resume ecc.). Se vado con la soluzione n. 2, finirò per creare molti metodi nell'API utente. Se vado con la soluzione n. 1, non sono sicuro che la classe Job possa essere chiamata come classe modello.

Non sono sicuro quale approccio sopra menzionato sia migliore e perché? O c'è un altro modo in cui dovrei pensare.

    
posta Lokesh Agrawal 14.10.2018 - 09:25
fonte

1 risposta

1

In primo luogo, Python non supporta i tipi di ritorno statici o generici. Ciò è dovuto principalmente al fatto che non ne hai bisogno perché Python è digitato in modo dinamico. Non puoi fare List<Job> o qualcosa del genere. Il list di Python può contenere qualsiasi tipo, incluso un oggetto Job . Se vuoi che un particolare elenco contenga solo% oggetti di% co_de, allora fallo . Non esiste un meccanismo di forzatura, ma se si tratta di un elenco di Job s, non aggiungere un gruppo di Job s o qualsiasi altro tipo e andrà tutto bene. Questo disturba un sacco di puristi Java / C ++ (io ero uno di loro), ma in pratica funziona bene.

In secondo luogo, sembra che tu stia cadendo vittima di BDUF (Big Design Up Front). In generale, il design dell'API è sottile e richiede molte decisioni di progettazione. Ma chi dice che devi farli tutti prima di iniziare? Prova a scrivere alcuni test e moduli unitari in uno stile TDD e verifica se qualcosa non emerge. Questo offre due vantaggi:

1) Avrai effettivamente un'API che qualcosa invece di un'API su carta che nulla

2) Potresti scoprire che il design emergente funziona meglio per il tuo problema. Prova a utilizzare TDD. Esempio (utilizzando la tua API proposta):

def test_job_can_get_command_names():
    job_command = JobCommand('some_command')
    job_command_2 = JobCommand('some_other_command')
    job_checkpoint = JobCheckpoint(name='test', commands=[job_command, job_command_2])     
    job = Job(name='job1', job_checkpoint=job_checkpoint)
    cmd = job.job_checkpoint.job_commands[0].job_command_name
    cmd2 = job.job_checkpoint.job_commands[1].job_command_name
    assert cmd == 'some_command'
    assert cmd2 == 'some_other_command'

La mia parola era dolorosa e prolissa. davvero credi che qualcuno vorrebbe usarlo? Come dev di Python, posso dirti che non lo farei. Se è questo che il tutorial ha per la tua API, smetterei di leggerlo subito dopo. In generale, le API Python sono brevi e al punto . Raramente creano un sacco di oggetti o qualcosa del genere. Usare la stringa al posto di enumerazione e gli oggetti "non impilabili" sono comuni. Dovendo creare un int e JobCheckpoint oggetto per creare un JobCommand sono grandi no-no. Python! = Java. Se mai mi trovassi a scrivere un test come questo, immediatamente eliminarlo e riprovare.

Semplificalo all'avvio (dovrebbe sempre essere facile accedere ai comandi sottostanti del lavoro):

def test_job_can_get_commands():
    job = Job(name='job1', commands=['some_command', 'some_other_command'])
    cmd = job.commands[0]
    cmd2 = job.commands[1]
    # maybe overload __eq__ if you want an underlying JobCommand object, but it should be transparent to the user.
    assert cmd == 'some_command'
    assert cmd2 == 'some_other_command'

Dimentica tutte le pazze classi all'inizio . Forse hai bisogno di loro, e forse non lo fai. Anche se lo fai, dovrebbero essere trasparenti per l'utente. Scrivi alcuni test, assicurati che la tua API sia effettivamente utilizzabile . assicurerà che la tua API sia utilizzabile. Se trovi difficile scrivere piccoli test come questo, indovina, i tuoi utenti troveranno altrettanto difficile utilizzare la tua API.

Mantieni la pulizia e mantieni la semplicità. Costruisci mentre vai: non provare a progettarlo tutto in anticipo. Prova a scrivere alcuni semplici test di filtraggio che affrontano un caso d'uso semplice e vai da lì. Lo stesso per le altre classi. Un test alla volta, una classe alla volta, e alla fine emergerà un design e un'API.

Se non emuli come l'API che emerge (ora che puoi vedere i difetti API, perché hai con cui lavorare in primo luogo), la bellezza dell'utilizzo di una suite di test e di TDD è che puoi semplicemente batterlo in qualunque API tu desideri pur mantenendo la correttezza. Questo perché TDD ti mette in una posizione in cui puoi refactoring fino a ottenere l'usabilità e il potere di cui hai bisogno. Se fai un brutto factoring, avrai un test fallito e potrai facilmente tornare indietro. TDD aiuta a separare la correttezza e la progettazione in modo che tu, come sviluppatore, puoi affrontare ogni preoccupazione individualmente.

HTH.

    
risposta data 15.10.2018 - 16:29
fonte

Leggi altre domande sui tag