Clean code em Javascript - troque seu if gigante por métodos

Clean Code

Como você viu no título, hoje vou falar sobre uma forma interessante de deixar suas funções / métodos pequenos, usando-os no lugar de if’s gigantescos!

Quando você tem um código limpo, fica muito mais fácil entender como tudo acontece, facilitando a manutenção do código.

Para usar a técnica de Clean Code você precisa deixar de pensar da forma tradicional, e adotar um novo paradigma. Sair da casinha, pensar fora da caixa!

Guga Alves pensando fora da caixa

Imagine a seguinte situação: você tem um código que precisa executar uma ação baseada no retorno de uma requisição. Essa requisição retornará um valor qualquer, dentre vários conhecidos.

Vamos tomar como exemplo uma base de usuários, onde deve ser executada uma ação conforme o papel (role) desse usuário:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
;(function( UsersResource ) {
function App() {
var $public = {};

$public.init = function init() {
var users = UsersResource.get();

users.forEach(function( user ) {
if( 'superadmin' === user.role ) {
console.log( 'SUPERADMIN', user );
}
else if( 'admin' === user.role ) {
console.log( 'ADMIN', user );
}
else if( 'editor' === user.role ) {
console.log( 'EDITOR', user );
}
else if( 'author' === user.role ) {
console.log( 'AUTHOR', user );
}
else if( 'contributor' === user.role ) {
console.log( 'CONTRIBUTOR', user );
}
else if( 'subscriber' === user.role ) {
console.log( 'SUBSCRIBER', user );
}
});
};

return $public;
}

App().init();
})( window.UsersResource );

E o UsersResource, que consulta informações em um servidor (por exemplo), e retorna um objeto com várias informações sobre usuários cadastrados no seu sistema e seus respectivos papéis:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
;(function( win ) {
function UsersResource() {
var $public = {};

$public.get = function get() {
return [
{ name: 'John', role: 'superadmin' },
{ name: 'Mark', role: 'admin' },
{ name: 'Robert', role: 'editor' },
{ name: 'Ana', role: 'author' },
{ name: 'Paul', role: 'contributor' },
{ name: 'Mariah', role: 'subscriber' }
];
};

return $public;
}

window.UsersResource = UsersResource();
})( window );

E aí, teve um Déjà vu? Quantas vezes já escrevemos códigos como esse? Muito fácil de ler não é? #sqn.
Todo o nosso código está centralizado em um único método - $public.init(). Temos várias coisas acontecendo aqui, mas ainda está tudo muito acoplado.

Que tal se tentarmos com switch?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
;(function( UsersResource ) {
function App() {
var $public = {};

$public.init = function init() {
var users = UsersResource.get();

users.forEach(function( user ) {
switch( user.role ) {
case 'superadmin':
console.log( 'SUPERADMIN', user );
break;
case 'admin':
console.log( 'ADMIN', user );
break;
case 'editor':
console.log( 'EDITOR', user );
break;
case 'author':
console.log( 'AUTHOR', user );
break;
case 'contributor':
console.log( 'CONTRIBUTOR', user );
break;
case 'subscriber':
console.log( 'SUBSCRIBER', user );
}
});
};

return $public;
}

App().init();
})( window.UsersResource );

Melhorou um pouco, mas ainda está tudo muito acoplado. Imagine se você precisar adicionar mais roles para os usuários.. pense no tamanho da zona que não vai virar?

Como resolver então?

Funções / Métodos

O Javascript é uma linguagem funcional, então porque escrever código imperativo? Vamos nos aproveitar do que o JS tem de melhor: as funções!

A versão clean code do nosso código ficaria assim:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
;(function( UsersResource ) {
function App() {
var $private = {};
var $public = {};

$public.init = function init() {
$private.getUsers();
};

$private.getUsers = function getUsers() {
var users = UsersResource.get();
users.forEach(function( user ) {
var role = $private.makeFirstLetterCapitalized( user.role );
$private[ 'handle' + role ]( user );
});
};

$private.makeFirstLetterCapitalized = function makeFirstLetterCapitalized( word ) {
return word.charAt(0).toUpperCase() + word.slice(1);
};

$private.handleSuperadmin = function handleSuperadmin( user ) {
console.log( 'SUPERADMIN', user );
};

$private.handleAdmin = function handleAdmin( user ) {
console.log( 'ADMIN', user );
};

$private.handleEditor = function handleAdmin( user ) {
console.log( 'EDITOR', user );
};

$private.handleAuthor = function handleAdmin( user ) {
console.log( 'AUTHOR', user );
};

$private.handleContributor = function handleAdmin( user ) {
console.log( 'CONTRIBUTOR', user );
};

$private.handleSubscriber = function handleAdmin( user ) {
console.log( 'SUBSCRIBER', user );
};

return $public;
}

App().init();
})( window.UsersResource );

Aumentou um pouco a quantidade de linhas, mas é visível a separação de responsabilidades aqui. Cada método faz somente uma coisa. Primeiro, tiramos toda a lógica do método init(), e o deixamos somente para chamar outros métodos.

Criamos um método makeFirstLetterCapitalized(), para transformar a primeira letra do role em maiúscula, pois padronizamos assim nossos métodos que irão manipular as informações desses usuários.

Separamos os métodos de manipulação para cada role, seguindo o padrão nomeado de handle{Role}, onde {Role} é o papel do usuário, com a primeira letra maíuscula.

Fica claro que isso não é solução para todos os casos, e você não vai trocar todos os seus if's por métodos. Mas essa é uma solução diferente para um problema bastante comum, que você pode aplicar no seu dia-a-dia, deixando seu código muito mais legível :)

Essa foi uma dica simples, escrita em um código Javascript, mas que é possível aplicar também em praticamente qualquer outra linguagem.

Gostou do post? Tem algo a compartilhar sobre o assunto? Comente!

Sobre o #1postperday: https://blog.da2k.com.br/2014/12/31/um-post-por-dia/

Tem alguma sugestão para os próximos posts do #1postperday? Deixe ela aqui: https://github.com/fdaciuk/fdaciuk.github.io/issues/1