Refactoring di un metodo 1500 LOC che costruisce solo l'interfaccia grafica [chiusa]

8

Attualmente mi sto graffiando la testa su come refactoring un metodo che fondamentalmente costruisce solo l'interfaccia utente.

Il metodo è lungo più di 1500 righe di codice (LOC) e il conteggio. È cresciuto, non c'era un piano su come affrontarlo. potresti trovare questo familiare.

In ogni caso, è fondamentalmente solo un metodo di grandi dimensioni che va un po 'in questo modo:

    .
    .
    .

    # null checks
    null_checks_bx = Box(True)

    null_checks_ck = CheckBox()
    null_checks_ck.set_text('Null checks throwing exceptions of type:')

    if 'doNullChecks' in options:
        null_checks_ck.set_active(options['doNullChecks'])
    else:
        null_checks_ck.set_active(True)

    # dict to sorted list: extract values from dict by list comprehension
    exceptions = sorted([exception.get_full_name() for exception in JavaTypes.exception_types])

    null_checks_se = Selector()
    null_checks_se.add_items(exceptions)
    null_checks_se.set_enabled(null_checks_ck.get_active())

    if 'nullChecksExceptionFullClassName' in options:
        null_checks_se.set_value(options['nullChecksExceptionFullClassName'])
    else:
        # default to first entry
        #(defaulting to doEquals = True and doHashCode = True by using hardcoded Commons Lang implies availability of Commons Lang NullArgumentException)
        null_checks_se.set_value(JavaTypes.exception_types[0].get_full_name())

    # callback
    Callbacks.sync_model_active_status(null_checks_ck, null_checks_se)

    null_checks_ck.add_clicked_callback(lambda: Callbacks.sync_model_active_status(null_checks_ck, null_checks_se))

    null_checks_bx.add(Label(indent), False, True)  # dummy indent label
    null_checks_bx.add(null_checks_ck, False, True)
    null_checks_bx.add(null_checks_se, False, True)

    # relationship entity class constructor calls
    relationship_constructor_calls_bx = Box(True)
    #relationship_constructor_calls_bx.set_spacing(5)
    #relationship_constructor_calls_bx.set_padding(3)

    relationship_constructor_calls_ck = CheckBox()
    relationship_constructor_calls_ck.set_text('Instantiate relationship entities (if all necessary columns present)')

    if 'doRelationshipConCalls' in options:
        relationship_constructor_calls_ck.set_active(options['doRelationshipConCalls'])
    else:
        relationship_constructor_calls_ck.set_active(False)

    relationship_constructor_calls_bx.add(Label(indent), False, True)  # dummy indent label
    relationship_constructor_calls_bx.add(relationship_constructor_calls_ck, False, True)

    # relationship not-null checks: this is an option independent of the null checks!
    relationship_not_null_checks = Box(True)
    #relationship_not_null_checks.set_spacing(5)
    #relationship_not_null_checks.set_padding(3)

    relationship_not_null_checks_ck = CheckBox()
    relationship_not_null_checks_ck.set_text('Not-null checks before relationship instantiation')

    relationship_not_null_checks_ck.set_enabled(relationship_constructor_calls_ck.get_active() and not null_checks_ck.get_active())

    if 'doRelationshipNotNullChecks' in options:
        relationship_not_null_checks_ck.set_active(options['doRelationshipNotNullChecks'])
    else:
        relationship_not_null_checks_ck.set_active(True)

    # callback
    Callbacks.sync_convenience_constructor_options(null_checks_ck, relationship_constructor_calls_ck, relationship_not_null_checks_ck)

    null_checks_ck.add_clicked_callback(lambda: Callbacks.sync_convenience_constructor_options(null_checks_ck, relationship_constructor_calls_ck, relationship_not_null_checks_ck))
    relationship_constructor_calls_ck.add_clicked_callback(lambda: Callbacks.sync_convenience_constructor_options(null_checks_ck, relationship_constructor_calls_ck, relationship_not_null_checks_ck))

    relationship_not_null_checks.add(Label(indent), False, True)  # dummy indent label
    relationship_not_null_checks.add(Label(indent), False, True)  # dummy indent label
    relationship_not_null_checks.add(relationship_not_null_checks_ck, False, True)

    # build final box
    checks_bx = Box(False)
    checks_bx.set_spacing(5)
    #checks_bx.set_padding(3)
    checks_bx.set_homogeneous(True)
    checks_bx.add(omit_auto_increment_columns_bx, False, True)
    checks_bx.add(omit_auto_timestamp_columns_bx, False, True)
    checks_bx.add(null_checks_bx, False, True)
    checks_bx.add(relationship_constructor_calls_bx, False, True)
    checks_bx.add(relationship_not_null_checks, False, True)

    # callback
    Callbacks.sync_model_active_status(convenience_constructors_ck, checks_bx)

    convenience_constructors_ck.add_clicked_callback(lambda: Callbacks.sync_model_active_status(convenience_constructors_ck, checks_bx))

    # need wrapped box for Panel class
    constructors_bx = Box(False)
    constructors_bx.set_spacing(5)
    constructors_bx.set_padding(3)
    #constructors_bx.set_homogeneous(True)
    constructors_bx.add(convenience_constructors_bx, False, True)
    constructors_bx.add(checks_bx, False, True)

    constructors_pn = Panel(TitledGroupPanel)
    constructors_pn.set_title('Constructors')
    constructors_pn.add(constructors_bx)

    # getters and setters
    is_conversion_bx = Box(True)
    #is_conversion_bx.set_spacing(3)
    #is_conversion_bx.set_padding(3)

    is_conversion_ck = CheckBox()
    is_conversion_ck.set_text('Convert respective column names starting with "is_"')
    is_conversion_ck.set_tooltip("Column is_incognito => getter isIncognito() (not getIsIncognito()) and setter setIncognito()")

    if 'doIsConversion' in options:
        is_conversion_ck.set_active(options['doIsConversion'])
    else:
        is_conversion_ck.set_active(True)

    is_conversion_bx.add(is_conversion_ck, False, True)

    # generate code for relationship getters and setters to synchronize with column fields
    sync_relationship_pk_fields_bx = Box(True)
    #sync_relationship_pk_fields_bx.set_spacing(3)
    #sync_relationship_pk_fields_bx.set_padding(3)

    sync_relationship_pk_fields = CheckBox()

    .
    .
    .

Ottieni il punto.

Quello che non vedo è quale potrebbe essere il criterio per dividere questo metodo in modo che il codice diventi più gestibile, più comprensibile ecc. pp.

Quali sono le migliori pratiche qui?

Potrei creare alcuni metodi che creerebbero tipi ricorrenti di pannelli e widget, ma questo diventerebbe uno sforzo piuttosto noioso per quello che non riesco a vedere il vero guadagno.

Ancora una volta, quali sono alcuni approcci pratici e utili a metodi ampi e goffi che costruiscono solo l'interfaccia grafica?

Grazie

PS: oh e BTW l'interfaccia utente è una singola finestra di dialogo in un'applicazione standard con finestre (senza web) e questa finestra di dialogo ha 6 schede che contengono principalmente caselle di controllo, pulsanti di opzione e caselle di testo. È un plug-in di generazione del codice che l'utente può scegliere da molte opzioni. Potrei pubblicare un'immagine non appena avrò riavviato il plug-in ...

    
posta Kawu 24.05.2014 - 14:09
fonte

1 risposta

27

Il tuo codice sembra avere una funzione che puoi sfruttare a tuo vantaggio qui: tutti i commenti raggruppati. Puoi utilizzare questi commenti come base per suddividere il metodo in blocchi da estrapolando metodi più piccoli . Estragga ciascun blocco nel proprio metodo, quindi chiama tale metodo dalla posizione originale del codice.

Quando inizi a estrarre questi pezzi, troverai naturalmente la duplicazione o troverai naturalmente forti relazioni tra alcuni dei metodi. Quando trovi la duplicazione, la estrai nei metodi di supporto per eliminare il tuo codice. Trovando i confini naturali delle relazioni, puoi suddividerli in classi separate di funzionalità più strettamente correlate.

Di seguito, ad esempio, ho iniziato a tirare fuori la logica per costruire la porzione di assegni nulli. L'ho estratto come metodo, scomposto alcuni pezzi che avevano bisogno di un nome migliore e de-namespace tutte le variabili all'interno del metodo. C'è molto di più che può essere fatto, ma è lasciato come esercizio per il lettore.

def add_indent_label(self, box):
    box.add(Label(indent), False, True)

def get_null_checks(self, options):
    box = Box(True)

    checkbox = CheckBox()
    checkbox.set_text('Null checks throwing exceptions of type:')

    if 'doNullChecks' in options:
        checkbox.set_active(options['doNullChecks'])
    else:
        checkbox.set_active(True)

    exceptions = self.get_exceptions()

    selector = Selector()
    selector.add_items(exceptions)
    selector.set_enabled(checkbox.get_active())

    if 'nullChecksExceptionFullClassName' in options:
        selector.set_value(options['nullChecksExceptionFullClassName'])
    else:
        selector.set_value(self.get_first_exception())

    # callback
    Callbacks.sync_model_active_status(checkbox, selector)
    checkbox.add_clicked_callback(lambda: Callbacks.sync_model_active_status(checkbox, selector))

    self.add_indent_label(box);
    box.add(checkbox, False, True)
    box.add(selector, False, True)

    return box

def get_exceptions(self):
    return sorted([exception.get_full_name() for exception in JavaTypes.exception_types])

# This could probably have a better name
def get_first_exception(self):
    #(defaulting to doEquals = True and doHashCode = True by using hardcoded Commons Lang implies availability of Commons Lang NullArgumentException)
    return JavaTypes.exception_types[0].get_full_name()

E nel tuo codice originale, diventa:

null_checks_bx = self.get_null_checks(options)
    
risposta data 24.05.2014 - 17:28
fonte

Leggi altre domande sui tag