Aplicando o Padrão Template Method Para Limpar o Login e o Cadastro - Android M-Commerce

Receba em primeira mão, e com prioridade, os conteúdos Android exclusivos do Blog. Você receberá um email de confirmação. Somente depois de confirma-lo é que poderei lhe enviar os conteúdos exclusivos.

Email inválido.
Blog /Android /Aplicando o Padrão Template Method Para Limpar o Login e o Cadastro - Android M-Commerce

Aplicando o Padrão Template Method Para Limpar o Login e o Cadastro - Android M-Commerce

Vinícius Thiengo
(603)
Go-ahead
"Não espere pela perfeição. A vida não é perfeita. Faça o melhor que puder. As pessoas reais constroem, em seguida, elas testam e, em seguida, elas constroem novamente. Então você acorda um dia e você tem algo insanamente grande."
Guy Kawasaki
Kotlin Android
Capa do livro Desenvolvedor Kotlin Android - Bibliotecas para o dia a dia
TítuloDesenvolvedor Kotlin Android - Bibliotecas para o dia a dia
CategoriasAndroid, Kotlin
AutorVinícius Thiengo
Edição
Capítulos19
Páginas1035
Acessar Livro
Treinamento Oficial
Android: Prototipagem Profissional de Aplicativos
CursoAndroid: Prototipagem Profissional de Aplicativos
CategoriaAndroid
InstrutorVinícius Thiengo
NívelTodos os níveis
Vídeo aulas186
PlataformaUdemy
Acessar Curso
Receitas Android
Capa do livro Receitas Para Desenvolvedores Android
TítuloReceitas Para Desenvolvedores Android
CategoriaDesenvolvimento Android
AutorVinícius Thiengo
Edição
Ano2017
Capítulos20
Páginas936
Acessar Livro
Código Limpo
Capa do livro Refatorando Para Programas Limpos
TítuloRefatorando Para Programas Limpos
CategoriaEngenharia de Software
AutorVinícius Thiengo
Edição
Ano2017
Capítulos46
Páginas599
Acessar Livro
Quer aprender a programar para Android? Acesse abaixo o curso gratuito no Blog.
Conteúdo Exclusivo
Receba em primeira mão, e com prioridade, os conteúdos Android exclusivos do Blog.
Email inválido

Tudo bem?

Neste artigo, décima aula do projeto Android mobile-commerce, vamos a mais uma refatoração, fruto da quantidade de códigos repetidos criados na última aula, aula de criação de tela de cadastro.

Neste conteúdo iniciaremos também o uso dos padrões de projeto, mais precisamente do padrão Template Method para diminuir os códigos duplicados entre as UIs de login e de cadastro:

Animação com o resultado da segunda refatoração

Antes de continuar com o estudo, não deixe de se inscrever 📩 na lista de emails do Blog para ter acesso exclusivo às novas aulas do projeto e a outros conteúdos sobre o desenvolvimento Android.

A seguir os tópicos abordados:

Sou iniciante no projeto Android mobile-commerce

Você chegou aqui agora? Digo, chegou ao projeto BlueShoes somente agora?

Se sim, então saiba que é preciso primeiro estudar as nove aulas anteriores para ficar realmente por dentro do projeto:

Por que uma nova refatoração será necessária?

Primeiro é importante saber que a refatoração é um processo que não cessa até o momento que o projeto é pivotado ou o cliente não mais quer permanecer melhorando o projeto.

Sobre o porquê de já estarmos em uma nova refatoração: isso ocorre, pois na aula nove do projeto, a aula que criamos a tela de cadastro, ficou claro que muitos códigos em SignUpActivity eram similares a códigos em LoginActivity.

Ficou evidente também que o simples encapsulamento dos códigos duplicados em uma superclasse não seria a melhor opção.

Sendo assim, aqui nesta aula, vamos à nossa caixa de ferramentas do desenvolvedor, aplicando um padrão de projeto para limpar a duplicação critica existente entre essas duas atividades.

Padrão de projeto Template Method

O padrão de projeto Template Method (ou método template, ou método gabarito) é um dos padrões mais simples de entender. A seguir a definição dele:

O Template Method defini o esqueleto (passos) do algoritmo em uma operação, permitindo que subclasses implementem alguns passos específicos do processamento.

O diagrama a seguir pode ajudar melhor com o entendimento:

Diagrama do padrão de projeto Template Method

Mas Thiengo, você não vai falar mais sobre o padrão? Está é a primeira vez que eu implemento algum!

Sim... na verdade eu já falei. No artigo Padrão de Projeto: Template Method (Método Template) aqui do Blog eu falo por completo sobre o padrão Template Method e sobre algumas de suas variações. Ao final deste conteúdo, não deixe de também estudar o artigo deste padrão.

Antes de partir para o próximo tópico, saiba que eu tenho um livro completo sobre refatoração de código, não deixe de conferi-lo: Refatorando Para Programas Limpos.

Refatoração Formar Template Method

Se você é um daqueles desenvolvedores que também aplicam padrões de projeto você deve saber que muitos dos padrões são aplicados quando o código já existe e precisa ser melhorado.

Para isso existem também os métodos de refatoração para padrão.

Para o padrão Template Method temos o método Formar Template Method que basicamente defini os seguintes passos para conseguirmos a aplicação deste padrão em projeto:

  1. Em uma hierarquia de classes identifique um método similar, aquele que tem passos e execuções similares nas subclasses da hierarquia. Logo depois extraia, no método similar em cada subclasse, ainda mais métodos. Métodos idênticos (de mesma assinatura e corpo nas subclasses) e métodos únicos (com assinatura e corpo diferentes nas subclasses). Para o passo de extração de métodos idênticos e únicos você pode utilizar a refatoração Compor Método. Para saber se o método deve ser idêntico ou único é importante que você conheça bem o domínio do problema e então decida se o método que está para ser único deve realmente ter uma implementação em todas as subclasses da hierarquia, pois em passos posteriores esse método terá uma assinatura abstrata na superclasse dessa hierarquia. Caso não faça sentido essa implementação em todas as subclasses, coloque o método como idêntico;
  2. Mova os métodos idênticos para a superclasse da hierarquia;
  3. Faça com que todos os métodos únicos das subclasses da hierarquia em refatoração tenham exatamente as mesmas assinaturas. Isso para que o método similar, presente nas subclasses, tenha sempre o mesmo corpo de código;
  4. Caso o método similar ainda não tenha a exata mesma assinatura nas subclasses da hierarquia, atualize as versões dele para que essa característica esteja presente;
  5. Transforme o método similar em método template, movendo ele para a superclasse e definindo os métodos únicos como métodos abstratos, deixando com as subclasses a responsabilidade de implementar os códigos desses métodos únicos. Remova as versões do método similar de cada subclasse.

Passos extensos, não? Pelo texto parece ser complicado, mas na verdade essa refatoração também é simples, como o padrão de projeto alvo dela.

Aqui utilizaremos uma adaptação deste método de refatoração.

Adaptação?

Sim. Nos próximos tópicos entenderemos melhor essa "adaptação".

Antes de prosseguir, saiba que aqui no Blog temos um artigo completo sobre o método de refatoração Formar Template Method, não deixe de também acessa-lo (depois de estudar o padrão Template Method).

Anhan, sei. Mas como você sabe que o padrão Template Method é a melhor escolha?

Excelente pergunta. Na verdade é preciso conhecer alguns padrões de projeto para poder utiliza-los.

O problema que estamos enfrentando: códigos similares, mas não 100% idênticos. Este problema se encaixa perfeitamente na descrição de "motivo de solução" do padrão de projeto Template Method. Lembrando que eu já conhecia este padrão.

Agora sobre o Template Method ser a melhor escolha: isso é difícil de dizer sendo que o projeto ainda não chegou ao final.

É sim possível que ao término do aplicativo seja identificado que o uso de algum outro padrão de projeto, no mesmo ponto do algoritmo, traga uma maior redução de códigos duplicados sem que a execução perca em eficiência.

Mas por hora o Template Method é a escolha mais acertada.

Refatorando

A partir daqui começaremos a segunda refatoração de melhoria de nosso projeto Android de mobile-commerce. Novamente ressaltando que os procedimentos de refatoração são procedimentos comuns na industria de desenvolvimento de software.

Na verdade eles são comuns quando ao menos já houve o primeiro release do projeto. Aqui estamos adiantando algumas refatorações devido a quantidade crítica de código duplicado.

Estratégia que será utilizada

Como informado anteriormente: utilizaremos na refatoração desta aula uma "adaptação" do método de refatoração Formar Template Method.

A adaptação consiste em primeiro identificarmos os métodos idênticos já existentes em nossas subclasses alvo da hierarquia em refatoração, SignUpActivity e LoginActivity. E então iniciar as modificações de acordo com os métodos idênticos encontrados.

Uma observação: primeiro vamos na verdade moldar a hierarquia em refatoração, pois uma superclasse ainda precisa ser criada.

Criando a nova superclasse da hierarquia

Um passo da refatoração para o padrão Template Method que não precisamos deixar para depois é o da criação da nova superclasse direta das atividades de login e de cadastro de usuário.

Isso, pois a FormActivity, que continuará sendo um superclasse da hierarquia da Login e SignUpActivity, não deve conter códigos específicos de apenas algumas de suas subclasses.

Lembrando que a atividade de recuperação de acesso não tem o problema de igualdade de códigos como acontece com as atividades de login e de cadastro, digo, igualdade de código em grande quantidade.

Sendo assim, no pacote /view, crie um nova atividade com o rótulo FormEmailAndPasswordActivity, com o código inicial a seguir:

abstract class FormEmailAndPasswordActivity :
FormActivity() {

override fun onCreate( savedInstanceState: Bundle? ) {
super.onCreate( savedInstanceState )
}
}

 

O rótulo desta atividade é extenso, mas se encaixa muito bem às subclasses que herdam dela. E sim, essa classe será abstrata, pois também vai conter seus próprios métodos abstratos, no caso os métodos únicos de encontraremos nas atividades de login e de cadastro.

Colocando a nova herança nas atividades alvo

Nossas duas atividades alvo de refatoração, LoginActivity e SignUpActivity, agora passarão a herdar diretamente de FormEmailAndPasswordActivity.

Primeiro a atualização na LoginActivity:

class LoginActivity :
FormEmailAndPasswordActivity(),
KeyboardUtils.OnSoftInputChangedListener {
...
}

 

Então a atualização na SignUpActivity:

class SignUpActivity :
FormEmailAndPasswordActivity(),
KeyboardUtils.OnSoftInputChangedListener {
...
}

 

Identificando e movendo os métodos idênticos de Login e de Cadastro

Estudando a LoginActivity e a SignUpActivity é possível notar que os métodos onDestroy()callPrivacyPolicyFragment() têm exatamente os mesmos códigos e assinaturas.

Estes são o que chamamos de métodos idênticos. Sendo assim podemos com segurança move-los para a FormEmailAndPasswordActivity:

abstract class FormEmailAndPasswordActivity :
FormActivity() {

...

/*
* Método idêntico.
* */
override fun onDestroy() {
KeyboardUtils.unregisterSoftInputChangedListener( this )
super.onDestroy()
}

/* Listener de clique */
/*
* Método idêntico.
* */
fun callPrivacyPolicyFragment( view: View ){
val intent = Intent(
this,
MainActivity::class.java
)

/*
* Para saber qual fragmento abrir quando a
* MainActivity voltar ao foreground.
* */
intent.putExtra(
MainActivity.FRAGMENT_ID,
R.id.item_privacy_policy
)

/*
* Removendo da pilha de atividades a primeira
* MainActivity aberta (e a LoginActivity), para
* deixar somente a nova MainActivity com uma nova
* configuração de fragmento aberto.
* */
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP

startActivity( intent )
}
}

 

Não esqueça de remover estes métodos das atividades de login e de cadastro de usuário.

Identificando os métodos template

Métodos template, ainda não isolados das subclasses alvo de refatoração, têm como principais características:

  • Código similar, existindo poucos pontos únicos;
  • Ordem idêntica de execução do algoritmo.

Tendo em mente somente este dois principais aspectos, temos que os métodos onSoftInputChanged()changePrivacyPolicyConstraints() se encaixam perfeitamente na configuração de métodos templates, ou seja, métodos que depois de refatorados serão também movidos para a melhor superclasse da hierarquia, aqui a FormEmailAndPasswordActivity.

Ok, Thiengo. Eu concordo que os métodos citados no parágrafo anterior são sim passíveis de se tornarem métodos template, mas e o onCreate()? Este método também lembra muito um método template ainda não isolado.

Excelente pergunta. Na verdade o método onCreate(), mesmo tendo alguns códigos que parecem comuns nas atividades de login e de cadastro, esses códigos de mesmos rótulos são distintos a ponto de não poderem ser compartilhados em uma superclasse, os objetos utilizados são de fontes (layouts) diferentes.

Obviamente que os códigos que nós pudermos isolar de onCreate() nós isolaremos, ainda neste artigo, mas o onCreate() em si não é possível de ser isolado aqui, nem para a FormEmailAndPasswordActivity e nem para a FormActivity.

Configurando o método template onSoftInputChanged()

Nosso primeiro passo para configurar o método onSoftInputChanged() como um método template é criar, a partir dele, métodos que terão as mesmas assinaturas em ambas as classes alvo, de login e de cadastro, porém com os códigos únicos dentro deles.

Esses métodos serão invocados no lugar em que seus algoritmos estavam. Estes são os métodos únicos, que terão assinaturas de um método abstrato em alguma das superclasses das classes alvo. Aqui será na FormEmailAndPasswordActivity.

Vamos recapitular o código de onSoftInputChanged() na LoginActivity:

...
override fun onSoftInputChanged( height: Int ) {
if( ScreenUtils.isPortrait() ){
changePrivacyPolicyConstraints(
KeyboardUtils.isSoftInputVisible( this )
)
}
}
...

 

Agora na SignUpActivity:

...
override fun onSoftInputChanged( height: Int ) {
changePrivacyPolicyConstraints(
KeyboardUtils.isSoftInputVisible( this )
)
}
...

 

O que muda é que na versão de onSoftInputChanged() na SignUpActivity não há bloco condicional. Sendo assim, primeiro em LoginActivity, vamos criar um novo método que terá a função de retornar um valor booleano para indicar se o algoritmo dentro de onSoftInputChanged() deve ou não ser executado.

Esse método deve ter um rótulo relevante para a LoginActivity e para a SignUpActivity. Aqui escolhi um rótulo verboso, mas que dispensa comentários extras em código: isAbleToCallChangePrivacyPolicyConstraints().

Em LoginActivity coloque os códigos em destaque, incluindo a atualização em onSoftInputChanged():

...
override fun onSoftInputChanged( height: Int ) {

if( isAbleToCallChangePrivacyPolicyConstraints() ){
changePrivacyPolicyConstraints(
KeyboardUtils.isSoftInputVisible( this )
)
}
}

fun isAbleToCallChangePrivacyPolicyConstraints()
= ScreenUtils.isPortrait()
...

 

Assim, em SignUpActivity, podemos prosseguir com a seguinte atualização para que os métodos onSoftInputChanged() das duas atividades sejam iguais:

...
override fun onSoftInputChanged( height: Int ) {

if( isAbleToCallChangePrivacyPolicyConstraints() ){
changePrivacyPolicyConstraints(
KeyboardUtils.isSoftInputVisible( this )
)
}
}

fun isAbleToCallChangePrivacyPolicyConstraints()
= true
...

 

Note que mesmo o condicional não sendo necessário na versão de onSoftInputChanged() em SignUpActivity, nós o colocamos para os códigos internos deste método ficar igual aos códigos da versão dele em LoginActivity.

Agora vamos transportar todos os códigos de onSoftInputChanged() para a FormEmailAndPasswordActivity, incluindo a implementação de KeyboardUtils.OnSoftInputChangedListener:

abstract class FormEmailAndPasswordActivity :
FormActivity(),
KeyboardUtils.OnSoftInputChangedListener {

override fun onCreate( savedInstanceState: Bundle? ) {
super.onCreate( savedInstanceState )

/*
* Com a API KeyboardUtils conseguimos de maneira
* simples obter o status atual do teclado virtual (aberto /
* fechado) e assim prosseguir com algoritmos de ajuste de
* layout.
* */
KeyboardUtils.registerSoftInputChangedListener( this, this )
}
...

/*
* Método template.
* */
override fun onSoftInputChanged( height: Int ) {

if( isAbleToCallChangePrivacyPolicyConstraints() ){
changePrivacyPolicyConstraints(
KeyboardUtils.isSoftInputVisible( this )
)
}
}

/*
* Método gancho.
* */
open fun isAbleToCallChangePrivacyPolicyConstraints()
= true
}

 

Note que até mesmo o KeyboardUtils.registerSoftInputChangedListener( this, this ) do onCreate() das duas subclasses alvo foi migrado para a FormEmailAndPasswordActivity.

Não tem problemas em fazer isso, alias até mesmo diminuímos a quantidade de código repetido sem estar de fato trabalhando diretamente com um método template, o onCreate().

Também incluímos o método isAbleToCallChangePrivacyPolicyConstraints(), com uma implementação padrão, na mais nova superclasse. Isso, pois podemos colocar esse método como sendo um método gancho, devido ao valor padrão, true, que ele retorna quando em uma das atividades alvo de refatoração.

Sendo assim, nosso primeiro método template está completo e fazendo uso de um método gancho ao invés de métodos únicos.

Para que seja visível a melhoria de código nas atividades de login e de cadastro ainda falta:

  • Na LoginActivity remova:
    • Da assinatura da classe a Interface KeyboardUtils.OnSoftInputChangedListener;
    • Do onCreate() a linha com KeyboardUtils.registerSoftInputChangedListener();
    • Do corpo da classe o método onSoftInputChanged().

Note que a versão de isAbleToCallChangePrivacyPolicyConstraints() em LoginActivity continua lá, porém com um override:

...
override fun isAbleToCallChangePrivacyPolicyConstraints()
= ScreenUtils.isPortrait()
...

Assim:

  • Na SignUpActivity remova:
    • Da assinatura da classe a Interface KeyboardUtils.OnSoftInputChangedListener;
    • Do onCreate() a linha com KeyboardUtils.registerSoftInputChangedListener();
    • Do corpo da classe o método onSoftInputChanged();
    • Ainda do corpo da classe o método isAbleToCallChangePrivacyPolicyConstraints().

Finalizado. Primeiro método template isolado com sucesso.

Configurando o método template changePrivacyPolicyConstraints()

O método changePrivacyPolicyConstraints(), diferente do método onSoftInputChanged(), é bem extenso. Sendo assim, aqui colocarei somente as partes dele que se difere na LoginActivity e na SignUpActivity.

Primeiro a versão do método na LoginActivity:

...
private fun changePrivacyPolicyConstraints(
isKeyBoardOpened: Boolean
){
...

if( isKeyBoardOpened ){
constraintSet.connect(
privacyId,
ConstraintLayout.LayoutParams.TOP,
tv_sign_up.id,
ConstraintLayout.LayoutParams.BOTTOM,
(12 * ScreenUtils.getScreenDensity()).toInt()
)
}
...
}
...

 

As partes que se diferem em relação a versão do método na atividade de cadastro de usuário estão em destaque.

Agora a versão da SignUpActivity:

...
private fun changePrivacyPolicyConstraints(
isKeyBoardOpened: Boolean
){
...

if( isKeyBoardOpened || ScreenUtils.isLandscape() ){
constraintSet.connect(
privacyId,
ConstraintLayout.LayoutParams.TOP,
bt_sign_up.id,
ConstraintLayout.LayoutParams.BOTTOM,
(12 * ScreenUtils.getScreenDensity()).toInt()
)
}
...
}
...

 

A estratégia aqui envolverá a criação de dois métodos únicos:

  • Um para retornar um valor booleano que será testado no condicional;
  • Outro que receberá ao menos o objeto constraintSet para então configura-lo de acordo com a atividade em uso.

Lembrando que os novos métodos devem ter rótulos úteis, além de serem idênticos, para ambas as subclasses.

Primeiro na LoginActivity. Crie os método isConstraintToSiblingView()setConstraintsRelativeToSiblingView() e atualize o método changePrivacyPolicyConstraints() como a seguir, em destaque:

...
private fun changePrivacyPolicyConstraints(
isKeyBoardOpened: Boolean
){
...

if( isConstraintToSiblingView( isKeyBoardOpened ) ){
setConstraintsRelativeToSiblingView( constraintSet, privacyId )
}
...
}

fun isConstraintToSiblingView( isKeyBoardOpened: Boolean )
= isKeyBoardOpened

fun setConstraintsRelativeToSiblingView(
constraintSet: ConstraintSet,
privacyId: Int ){

/*
* Se o teclado virtual estiver aberto, então
* mude a configuração da View alvo
* (tv_privacy_policy) para ficar vinculada a
* View acima dela (tv_sign_up).
* */
constraintSet.connect(
privacyId,
ConstraintLayout.LayoutParams.TOP,
tv_sign_up.id,
ConstraintLayout.LayoutParams.BOTTOM,
(12 * ScreenUtils.getScreenDensity()).toInt()
)
}
...

 

Agora para a SignUpActivity vamos fazer a mesma criação e atualização de métodos, obviamente que respeitando os códigos únicos para os métodos nessa atividade:

...
private fun changePrivacyPolicyConstraints(
isKeyBoardOpened: Boolean
){
...

if( isConstraintToSiblingView( isKeyBoardOpened ) ){
setConstraintsRelativeToSiblingView( constraintSet, privacyId )
}
...
}

fun isConstraintToSiblingView( isKeyBoardOpened: Boolean )
= isKeyBoardOpened || ScreenUtils.isLandscape()

fun setConstraintsRelativeToSiblingView(
constraintSet: ConstraintSet,
privacyId: Int ){

/*
* Se o teclado virtual estiver aberto ou a tela
* estiver em landscape, então mude a configuração
* da View alvo (tv_privacy_policy) para ficar
* vinculada a View acima dela (bt_sign_up).
* */
constraintSet.connect(
privacyId,
ConstraintLayout.LayoutParams.TOP,
bt_sign_up.id,
ConstraintLayout.LayoutParams.BOTTOM,
(12 * ScreenUtils.getScreenDensity()).toInt()
)
}
...

 

Com isso podemos mover o novo método template, changePrivacyPolicyConstraints(), para a FormEmailAndPasswordActivity e também as assinaturas dos dois métodos únicos, como a seguir:

abstract class FormEmailAndPasswordActivity :
... {
...

/*
* Método template.
* */
private fun changePrivacyPolicyConstraints(
isKeyBoardOpened: Boolean
){
val privacyId = tv_privacy_policy.id
val parent = tv_privacy_policy.parent as ConstraintLayout
val constraintSet = ConstraintSet()

/*
* Definindo a largura e a altura da View em
* mudança de constraints, caso contrário ela
* fica com largura e altura em 0dp.
* */
constraintSet.constrainWidth(
privacyId,
ConstraintLayout.LayoutParams.WRAP_CONTENT
)
constraintSet.constrainHeight(
privacyId,
ConstraintLayout.LayoutParams.WRAP_CONTENT
)

/*
* Centralizando a View horizontalmente no
* ConstraintLayout.
* */
constraintSet.centerHorizontally(
privacyId,
ConstraintLayout.LayoutParams.PARENT_ID
)

if( isConstraintToSiblingView( isKeyBoardOpened ) ){
setConstraintsRelativeToSiblingView( constraintSet, privacyId )
}
else{
/*
* Se o teclado virtual estiver fechado, então
* mude a configuração da View alvo
* (tv_privacy_policy) para ficar vinculada ao
* fundo do ConstraintLayout ancestral.
* */
constraintSet.connect(
privacyId,
ConstraintLayout.LayoutParams.BOTTOM,
ConstraintLayout.LayoutParams.PARENT_ID,
ConstraintLayout.LayoutParams.BOTTOM
)
}

constraintSet.applyTo( parent )
}

/*
* Método único.
* */
abstract fun isConstraintToSiblingView( isKeyBoardOpened: Boolean ) : Boolean

/*
* Método único.
* */
abstract fun setConstraintsRelativeToSiblingView(
constraintSet: ConstraintSet,
privacyId: Int ) : Unit
}

 

Na LoginActivity remova o método changePrivacyPolicyConstraints() e coloque nas assinaturas dos métodos únicos o override:

...
override fun isConstraintToSiblingView( isKeyBoardOpened: Boolean )
...

override fun setConstraintsRelativeToSiblingView(
constraintSet: ConstraintSet,
privacyId: Int ){
...
}
...

 

Para a SignUpActivity o mesmo procedimento, remova o método changePrivacyPolicyConstraints() e coloque o override nos métodos únicos:

...
override fun isConstraintToSiblingView( isKeyBoardOpened: Boolean )
...

override fun setConstraintsRelativeToSiblingView(
constraintSet: ConstraintSet,
privacyId: Int ){
...
}
...

 

Assim terminamos o isolamento do segundo método template, removendo ainda mais códigos duplicados.

A FormActivity deve conter mais códigos únicos

Ainda estudando as atividades de login e de cadastro de usuário é possível encontrar trechos de códigos duplicados que podem facilmente serem movidos para alguma superclasse.

Quando estudando também a atividade de recuperação de acesso, ForgotPasswordActivity, fica fácil de perceber que os códigos ainda duplicados nas atividades de login e de cadastro também estão duplicados na atividade de recuperação de senha, sendo assim a superclasse que receberá os códigos duplicados será a FormActivity.

O método template mainAction()

Na LoginActivity, note o método mainAction():

...
override fun mainAction( view: View? ){
blockFields( true )
isMainButtonSending( true )
showProxy( true )

backEndFakeDelay(
false,
getString(R.string.invalid_login)
)
}
...

 

Já lhe adianto que a única coisa que muda no algoritmo de mainAction() da LoginActivity em relação aos algoritmos de suas versões nas outras atividades com formulários é o segundo parâmetro do método backEndFakeDelay().

Isso tendo em mente que este método, backEndFakeDelay(), será removido até o final do projeto dando lugar a outro que conterá ao menos o envio de dados ao back-end Web, algo que ocorre fora da camada de UI.

Com isso, enxergando que mainAction() pode se tornar um método template em FormActivity, vamos isolar em um método, ainda na LoginActivity, a parte de mainAction() que deve ter código único:

...
fun mainAction( view: View? ){
blockFields( true )
isMainButtonSending( true )
showProxy( true )
backEndFakeDelay()
}

fun backEndFakeDelay(){
backEndFakeDelay(
false,
getString( R.string.invalid_login )
)
}
...

 

Ao invés de aplicar essa mesma atualização na SignUpActivity e na ForgotPasswordActivity, vamos logo a atualização na FormActivity:

abstract class FormActivity :
... {
...

/*
* Método template.
* Responsável por conter o algoritmo de envio / validação
* de dados. Algoritmo vinculado ao menos ao principal
* botão em tela.
* */
fun mainAction( view: View? = null ){
blockFields( true )
isMainButtonSending( true )
showProxy( true )
backEndFakeDelay()
}

/*
* Método único.
* */
abstract fun backEndFakeDelay() : Unit
...
}

 

Agora na LoginActivity remova o método mainAction() e coloque o override em backEndFakeDelay():

...
override fun backEndFakeDelay(){
...
}
...

 

Na SignUpActivity elimine o método mainAction() e coloque a seguinte versão de backEndFakeDelay():

...
override fun backEndFakeDelay(){
backEndFakeDelay(
false,
getString( R.string.invalid_sign_up_email )
)
}
...

 

Na ForgotPasswordActivity também remova o método mainAction() e coloque a seguinte versão de backEndFakeDelay():

...
override fun backEndFakeDelay(){
backEndFakeDelay(
false,
getString( R.string.invalid_login_email )
)
}
...

 

Assim finalizamos o isolamento de mais um método template.

View.inflate() repetido!

Apesar de não ser parte de um método template, o trecho de código View.inflate() nos métodos onCreate() das três atividades que contém formulários é muito parecido, mudando somente o segundo argumento de inflate().

Sendo assim, na FormActivity realize a seguinte atualização, em destaque:

...
override fun onCreate( ... ) {
...

/*
* Colocando a View de um arquivo XML como View filha
* do item indicado no terceiro argumento.
* */
View.inflate(
this,
getLayoutResourceID(),
fl_form
)
}

abstract fun getLayoutResourceID() : Int
...

 

Assim, primeiro na LoginActivity, remova o trecho de código View.inflate() de onCreate() e adicione ao corpo da atividade a implementação do método getLayoutResourceID() como a seguir:

...
override fun getLayoutResourceID()
= R.layout.content_login
...

 

Agora na SignUpActivity remova o código View.inflate() de onCreate() e coloque ao corpo da atividade a implementação do método getLayoutResourceID() como a seguir:

...
override fun getLayoutResourceID()
= R.layout.content_sign_up
...

 

Por fim, na ForgotPasswordActivity remova o código View.inflate() do onCreate() e acrescente ao corpo da atividade a implementação do método getLayoutResourceID() como a seguir:

...
override fun getLayoutResourceID()
= R.layout.content_forgot_password
...

 

Com isso, ainda mais código duplicado foi removido. Vamos aos testes conferir se tudo continua funcionando normalmente.

Testes e resultados

Abra o Android Studio, no menu de topo acesse "Build", então clique em "Rebuid project". Ao final do rebuild execute o aplicativo em seu aparelho ou emulador Android de testes.

Coloque em teste o usuário com o status "não conectado", objeto presente na MainActivity:

...
val user = User(
"Thiengo Vinícius",
R.drawable.user,
false /* Usuário não conectado. */
)
...

 

Acessando a área de login, temos:

Animação do resultado da refatoração na tela de login

Acessando a área de cadastro de novo usuário, temos:

Animação do resultado da refatoração na tela de cadastro de novo usuário

Acessando a área de recuperação de acesso, temos:

Animação do resultado da refatoração na tela de recuperação de senha

Com isso finalizamos a segunda refatoração do projeto Android BlueShoes.

Antes de prosseguir, não deixe de se inscrever na 📩 lista de emails do Blog para receber todas as aulas do projeto Android de mobile-commerce.

Se inscreva também no canal do Blog em: YouTube Thiengo.

Vídeos

A seguir os vídeos com o passo a passo da aplicação do padrão de projeto Template Method:

O projeto também pode ser acessado pelo GitHub dele em: https://github.com/viniciusthiengo/blueshoes-kotlin-android.

Conclusão

Utilizar rótulos de classes, métodos e propriedades com termos que não exijam comentários de explicação é sem sombra de dúvidas o primeiro passo para um código limpo.

Encapsular códigos repetidos também é um passo inteligente para melhorar todo um projeto de software.

No aplicativo Android BlueShoes, chegamos a um ponto que um simples movimento não mais ajudava, tivemos de ir a um passo ainda mais fundo no processo de refatoração de projeto de software: utilização de padrão de projeto.

Felizmente o padrão Template Method nos atendeu muito bem e removeu bastante dos códigos duplicados, deixando as principais atividades de formulários, criadas até aqui, ainda mais clean.

Caso você tenha dúvidas ou dicas para este projeto, não hesite em deixar logo abaixo nos comentários.

Curtiu o conteúdo? Não esqueça de compartilha-lo. E, por fim, não deixe de se inscrever na 📩 lista de emails, respondo às suas dúvidas também por lá.

Abraço.

Receba em primeira mão, e com prioridade, os conteúdos Android exclusivos do Blog.
Email inválido

Relacionado

Como Criar Protótipos AndroidComo Criar Protótipos AndroidAndroid
Como Reter Objetos Utilizando Android-State APIComo Reter Objetos Utilizando Android-State APIAndroid
True Time API Para Data e Horário NTP no AndroidTrue Time API Para Data e Horário NTP no AndroidAndroid
Início de Projeto e Menu Gaveta Customizado - Android M-CommerceInício de Projeto e Menu Gaveta Customizado - Android M-CommerceAndroid

Compartilhar

Comentários Facebook

Comentários Blog

Para código / script, coloque entre [code] e [/code] para receber marcação especifica.
Forneça seu nome válido.
Forneça seu email válido.
Forneça o comentário.
Enviando, aguarde...