Em programação orientada a objetos, o Safe Navigation Operator pode ser útil para evitar checagens sequenciais de objeto nulo. Ele está presente em várias linguagens de programação e em Ruby ele foi incorporado como &.
. Vamos compreender melhor como ele funciona.
Considere, por exemplo, uma aplicação em que usuários (User
) possuem um perfil (Profile
) com alguns atributos (nome, sobrenome, cidade, etc). Quando tentamos obter o nome no perfil de um determinado usuário pode ser levantado um erro se user
ou profile
forem nulos:
class User
# código da classe
end
class Profile
# código da classe
end
user = User.new(profile: nil)
user.profile.name
# => NoMethodError (undefined method `name' for nil:NilClass)
Se quisermos fazer o mesmo de forma mais segura, evitando chamar métodos em objetos nulos, seria necessário executar uma série de checagens como:
user && user.profile && user.profile.name
# => nil
Outra forma de obter o mesmo resultado seria utilizando o método try
:
user.try(:profile).try(:name)
# => nil
É importante lembrar que o try
é um método do Active Suport e por isso só funciona no Rails ou se você adicionar a gem.
O Safe Navigation Operator retorna nil
se o primeiro argumento for nulo ou executa a operação do segundo argumento. Dessa forma ele ajuda a evitar as checagens sequenciais:
user&.profile&.name
# => nil
Apesar de tornar o código mais enxuto, existem algumas motivações para evitarmos seu uso. O motivo mais facilmente perceptível é que o &.
torna o código mais difícil de ser lido e compreendido. Sabemos que algum elemento na sequência pode ser nulo, mas não sabemos o motivo. Existem formas de evidenciar o que está acontecendo por trás do código implementado, como por exemplo definir métodos com nomes claros que validam a presença de objetos:
class User
# código da classe
def has_profile?
profile.present?
end
end
Vale também destacar que o &.
e try
nem sempre vão funcionar da mesma maneira. Por exemplo, o que acontece se o objeto não responder ao método chamado?
user = User.new(profile: Profile.new)
user.try(:profile).try(:passport_number)
# => nil
user&.profile&.passport_number
# => NoMethodError (undefined method `passport_number' for #<Profile:0x000055d67aa86780>)
O try
não verifica se o objeto responde ao método, retornando apenas nil
. O &.
levanta o NoMethodError
uma vez que passport_number
não existe para profile
neste exemplo.
O Safe Navigation Operator pode ajudar tornando o código mais enxuto, mas devemos atentar para as diferentes situações em que é aplicado para evitarmos problemas. Ele pode ser especialmente útil para prototipar algo de forma rápida ou para realizar testes dentro do IRB. No entanto, se houver tempo hábil, é possível utilizar alternativas como implementar o Null Object Pattern, por exemplo.