Definir classes não é programar com orientação a objetos: Ciclo de vida

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:

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:

Quebra de Encapsulamento

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:

Aggregate Root

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

Object Life Cycle

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!

Concluindo?

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.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s