Atualmente, estou coçando minha cabeça sobre como refatorar um método que basicamente apenas cria a interface do usuário.
O método tem mais de 1500 linhas de código (LOC) - e contando. Ele cresceu, não havia plano de como abordar isso. Você pode achar isso familiar.
Enfim, é basicamente apenas um método grande que é mais ou menos assim:
.
.
.
# 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()
.
.
.
Você entendeu.
O que eu não vejo é qual pode ser o critério para dividir esse método, para que o código se torne mais gerenciável, mais compreensível etc pp.
Quais são as melhores práticas aqui?
Eu poderia criar alguns métodos que criariam tipos recorrentes de painéis e widgets, mas isso se tornaria um esforço bastante tedioso para o que não vejo o ganho real.
Novamente, quais são algumas abordagens práticas e úteis para métodos grandes e desajeitados que apenas criam a interface gráfica do usuário?
obrigado
PS: ah, e BTW, a interface do usuário é um único Diálogo em um aplicativo padrão com janelas (sem web) e essa caixa de diálogo possui 6 guias que contêm principalmente caixas de seleção, botões de opção e caixas de texto. É um plug-in de geração de código do qual o usuário pode escolher muitas opções. Posso postar uma imagem assim que o plug-in for executado novamente ...
Respostas:
Seu código parece ter um recurso que você pode usar aqui: todos esses comentários de agrupamento. Você pode usar esses comentários como base para dividir o método em partes extraindo métodos menores . Você extrai cada pedaço em seu próprio método e chama esse método a partir do local original do código.
Ao começar a extrair essas peças, você encontrará naturalmente duplicação ou naturalmente encontrará fortes relações entre alguns dos métodos. À medida que você encontra duplicação, você extrai isso para métodos auxiliares para secar seu código. Ao encontrar limites naturais de relacionamento, você pode dividi-los em classes separadas de funcionalidade mais intimamente relacionada.
Abaixo, por exemplo, comecei a extrair a lógica para criar a parte de verificações nulas. Eu o extraí como um método, dividi algumas partes que precisavam de um nome melhor e removemos o nome de todas as variáveis no método. Muito mais pode ser feito, mas isso é deixado como um exercício para o leitor.
E no seu código original, ele se torna:
fonte