Há pouco mais de 2 anos, falei sobre esse tema na PHP Conference Brasil e acho válido revisitá-lo uma vez que muitos desenvolvedores sentem orgulho em dizer que programam orientado a objetos, mas ao fazer uma simples pergunta acabam não sabendo respondê-la:
O que é Orientação a Objetos ?
Alan Kay, criador da linguagem Smalltalk e um dos primeiros defensores da orientação a objetos, a explicou da seguinte forma:
"The big idea is "messaging" (...)
Pronto, fim do post. Obrigado por ler.
Messaging ?
De nada adianta falar sobre Test-First, Princípios de Orientação a Objetos, Mocking, AntiPatterns uma vez que não se conhece o núcleo da Orientação a Objetos.
O grande cerne da questão é justamente a troca de mensagem entre objetos através de seus métodos. É assim que Alan Kay defendeu e é assim que o Smalltalk funciona. Recentemente, alguns desenvolvedores, especialmente em Ruby, têm voltado a reforçar o propósito em OO.
A melhor forma de manter seu código desacoplado e "falante" é forçar os objetos a trocarem mensagens. Lembra do Tell Don't Ask? Pois é, aqui isso é parte chave para atingir-se tal objetivo.
Vou repetir:
- http://c2.com/cgi/wiki?TellDontAsk
- http://pragprog.com/articles/tell-dont-ask
- http://martinfowler.com/bliki/TellDontAsk.html
- Tem a versão do Giovanni Bassi, do .NET Architects também;
Pronto, acho que deu para entender o quão importante é manter no objeto a responsabilidade de manipular os próprios dados (armazenados em vossos atributos).
O Ciclo de Vida de um Objeto
Já parou para pensar em qual seria o ciclo de vida de um objeto?
Bom, inicialmente ele Nasce, depois Cresce, Continua disponível e finalmente Morre.
O nascimento de um objeto
O nascimento do objeto é através de seu construtor. Simples, porém muitas vezes ele acaba ignorado, ainda mais quando vicia-se em utilizar frameworks arquiteturais sem entender o motivos das coisas.
Correto:
Employee.new(name: "Durval", lastname: "da Silva", cpf: "123.123.123-X")
new Employee("Durval", "da Silva", "123.123.123-X")
Incorreto:
durval = Employee.new
durval.name = "durval"
durval.lastname = "da Silva"
durval.cpf = "123.123.123-X"
durval = new Employee();
durval.setName("durval");
durval.setLastname("da Silva");
durval.setCpf("123.123.123-X");
O motivo é simples: o construtor é o contrato que nos diz: "para você obter um novo objeto tipo Employee
, você precisa obrigatoriamente me informar: Nome, Sobrenome e CPF, pois caso contrário, você criará um Employee
inválido."
E é justamente o que o exemplo incorreto faz: cria um Employee
sem dado algum. Quem garante que eu irei chamar setName
, setLastname
e setCpf
depois? Eu posso esquecer. Pode nem ser eu o responsável por utilizá-lo depois. (APIs-like).
Lembre-se o construtor é a certidão de nascimento do objeto. Não tire dele esse direito. Brasil, país rico é país...
Cresce
Aqui temos outros métodos que mudarão o status de nosso objeto ao longo de sua vida. Nem sempre é com setter - outro erro comum daqueles que programam por coincidência. Veja exemplos:
Correto:
employee.exame_admissional = arquivo_pdf_do_exame # snake case r0cks
employee.setExameAdmissional(arquivoPdfDoExame)
employee.definir_credencial_de_rede(username: "new_username", password: "983hJfh78")
Incorreto:
objeto_credencial = Credencial.new(username: "new_username", password: "9789237498")
employee.credencial = objeto_credencial
objetoCredencial = new Credencial("new_username", "3798472398478234")
employee.setCredencial(objetoCredencial)
Neste caso, o incorreto viola o encapsulamento da Credencial, deixando a cargo do cliente conhecer como a Credencial é criada. Para entender melhor, desenho:
Cliente é toda classe que utiliza outra. Só para deixarmos as coisas claras por aqui.
O problema aí é que no caso o Controller precisa saber como construir Employee e Credencial, além de precisar conhecer como relacioná-las. Um exemplo mais OOP seria:
Agora, o Controller precisa apenas lidar com Employee
e este lidará com a Credencial
. Em pseudocódigo, ficaria:
# Ruby
class Employee
# construtor e outros métodos ocultados
def define_credencial(username: username, password: password)
@credencial = Credencial.new(username: username, password: password, employee: self)
end
end
# Java, et al.
class Employee {
private Credencial credencial;
public void defineCredencial(String username, String password) {
credencial = new Credencial(username, password, this);
}
}
Neste caso, Credencial
é um Value Object
pertencente ao Employee
. Esse tipo de relacionamento é parte do Domain-Driven Design e é chamado de Aggregate Root.
Aggregate root são úteis quando você tem um relacionamento aonde a Parte (Credencial) é totalmente dependente do Todo (Employee). Em outras palavras: a Parte só tem vida quando o Todo tem vida. Não é legal que uma Parte tenha vários Todo(s).
Continuando disponível
Em orientação a objetos, nossa linha de pensamento deveria ser um pouco mais ampla. Ou seja, devemos pensar que uma vez que o objeto é criado (instanciado) ele ficará disponível até que alguém o mate (remova da memória). Enquanto ninguém o remover da memória, ele continuará vivo.
É importante esquecer o meio/tipo de armazenamento aqui. Robert Martin (@unclebob) falou sobre isso na sua must watch palestra na Ruby Midwest 2011 - sério: assista até o fim. Você será outro depois disso ;)
Morre
O objeto é removido através do Garbage Collector, Removido com ORM, etc. Aqui não há novidade mesmo.
1 imagem, 1000 palavras
O meio de vida do objeto (aquele dentro do retângulo) é aonde a maior parte dos objetos estarão. Ao buscar um objeto com um Repository
por exemplo, devemos pensar que o objeto estava em memória, já construído e do jeito que o deixamos da última vez que trabalhamos com ele. Ou seja: o Repository
não dará new
novamente, pois o objeto já existe. Pelo que me lembro, o Doctrine 2 seguia isto: ao recuperar um objeto do Repository
o construtor não era chamado novamente - o que é excelente, pois o Ciclo de Vida do Objeto não é quebrado!
Concluíndo?
O tema é extenso assim como Test-First. Conhecer bem o que a Orientação a Objetos reserva e espera de você é pré-requisito para iniciar com Teste de Software em aplicação Orientada a Objeto. Na sequência, continuarei a falar sobre Object Oriented Design para permitir que se veja além ao testar software.
0sem comentários ainda