CONCEITOS DE OOP (Programação Orientada a Objeto)

Dois conceitos importantes a dominar em Programação Orientada a Objeto são classe e objeto. A programação orientada a objetos (seu acrônimo em inglês é OOP) tornou-se popular no final dos anos 1990, quando a linguagem C++, de Bjarne Stroolstrup, passou a ser “a linguagem” de programação dos profissionais de computação. Outras linguagens orientadas a objetos como Delphi, Eiffel e Python foram usadas, mas C++ foi uma febre entre programadores. Até hoje ela é importante na programação do produto Windows.

Bertrand Meyer [Meyer, 1997] definiu o que é “qualidade de programa” ou seja, as características que fazem com que um programa seja bom. Dentre elas está alterabilidade (ou “Manutenibilidade”). É a capacidade de alterar facilmente um programa quando os requisitos são alterados, por exemplo. A OOP vem de encontro a esse item de qualidade.

Fatores Externos de Qualidade de Software (Bertrand Meyer)

Correção: característica do software que realiza as tarefas como foram definidas em sua especificação de requisitos.
Robustez: um software é robusto se realiza as suas tarefas de forma correta mesmo quando submetido a condições anormais.
Extensibilidade: característica de um software poder ser facilmente adaptado a inclusões e alterações de requisitos.
Reusabilidade: característica de um software que pode ser reutilizado ao todo ou em parte por outros softwares.
Compatibilidade: facilidade de se combinar o software com outros softwares. Essa característica é importante porque raramente um software é construído sem interação com outros softwares.
Eficiência: refere-se ao bom uso que o software faz dos recursos de hardware, tais como memória e processadores.
Portabilidade: é a facilidade de se utilizar o software em diferentes ambientes de hardware e software.
Verificabilidade: é a facilidade de se preparar rotinas para se verificar a conformidade do software com os seus requisitos.
Integridade: é uma característica relacionada à segurança de dados, programas e documentos. Integridade é a habilidade de proteger tais componentes contra acessos não autorizados.
Facilidade de uso: também denominada usabilidade, é a facilidade com que o software pode ser aprendido e utilizado, inclusive por pessoas com deficiência.

Fatores de Internos de Qualidade de Software

Modularidade: característica de um software que é constituído por unidades denominadas módulos.
Legibilidade : deve ser possível para um programador qualquer ler e entender o programa.
Manutenibilidade: facilidade de realizar manutenção em um software.

A forma com um software é construído permite atingir os fatores internos de qualidade. Os fatores internos de qualidade permitem atingir os fatores externos de qualidade. A Orientação por Objetos é um paradigma cujas características permitem a obtenção de software modular, legível e de fácil manutenção.”1

Costuma-se usar ferramentas nos próprios programas’, que permitem obter um feedback do usuário a cada momento (geralmente no fim de uma sessão). Assim ele ou ela podem informar automaticamente sobre seu grau de satisfação com o produto. Consulte a metodologia NPS (net promoter score2) e a ferramenta wootric3.

1– Veja [ http://portal6.pbh.gov.br/dom/Files/unidade.pdf ]

2https://pt.wikipedia.org/wiki/Net_Promoter_Score

3https://www.wootric.com/

A programação orientada a objetos (OOP) foi criada no início da década de 1970 no Xerox Corporation’s Palo Alto Research Center1. A OOP nasceu para atender aos critérios de qualidade de software, para torná-los possíveis dentro do desenvolvimento e de uma etapa até então desprezada: a manutenção . [MEYER, 1997], citando Lientz e Swanson, observa os custos de manutenção de software (portanto quando já está teóricamente pronto) por assunto:

1http://www.dba-oracle.com/t_object_database_model.htm

AssuntoCusto %
Modificação de requisitos de usuários41,8
Mudanças no formato dos dados17,6
Correções de emergência12,4
Correções de rotina9,0
Mudanças de hardware6,2
Documentação5,5
Melhoria de eficiência4,0
Outros3,4

Estimou-se que os custos do processo de manutenção, chegavam a 70% do custo do software. A OOP nasceu, portanto, para baratear a manutenção. Vamos tomar o exemplo da mudança no formato de dados. Antes da OOP, quando um conjunto de dados se alterava, era preciso modificar várias partes do sistema: A impressão de relatórios, a exibição dos dados na tela, a gravação em meio físico, a recuperação, a crítica. Com a OOP, os dados sabem como realizar essas operações. Idealmente, uma modificação nos dados leva à alteração automática das operações em todo o sistema.

Classes e objetos

Para começar, OOP trabalha com classes. Uma classe define a estrutura, os tipos de dados que a estrutura contém e os métodos e comportamentos usados para manipular os dados. Quando definimos uma variável que faz parte de uma classe, temos um “objeto”1. Muitas linguagens de programação lidam de forma natural com isso JavaScript, por exemplo, adapta o conceito de função para criar classes e seus objetos.

1 – [MEYER, 1997]é uma excelente referência sobre o assunto. Veja os itens 1.3 a 2.6 da obra.

Imagine que você precisa fazer um programa para cadastrar armas portáteis. Em nossa definição, arma portátil é toda aquela que pode ser carregada por uma pessoa apenas. Vamos melhorar a definição: arma portátil é toda aquela que pode ser carregada por uma pessoa apenas, sem chamar a atenção de outras pessoas. Assim, um revólver, uma pistola, uma faca, um machado curto são armas portáteis. Suponha uma hierarquia entre essas armas:

  1. Armas portáteis
    1. Armas de fogo
      1. pistola
      2. revolver
    2. Armas de eletrochoque
      1. taser
      2. stunt gun
    3. Armas brancas
      1. faca
      2. punhal
      3. machado

A lista não é extensa e nem pretende ser. É apenas um exemplo bem simples. Suponha então que eu queira definir uma classe de “armas portáteis”, de acordo com a lista acima. Se eu definir a classe “Arma” tenho de incluir uma informação sobre o que é essa arma: Arma de fogo, eletrochoque ou branca. Assim, parece razoável que eu tenha um descritor “Tipo”, dando essa informação.

Regra#1 das classes: Toda classe precisa de um construtor, um método para criar seus objetos na memória.

class Arma {
constructor(Tipo, SubTipo) {
this.Tipo = Tipo;
this.SubTipo = SubTipo;
}
}
Exemplo em JavaScript

O trecho acima quer dizer que, na hora de criar um objeto da classe Arma, tenho de informar o tipo e o subtipo. O operador “this” serve para definir uma variável interna à classe. Ele quer dizer que estou falando do Tipo que é usado internamente ao objeto, e não o que é passado por parâmetro. Quer dizer também que a variável Tipo informada no parêntesis não será usada lá dentro da classe. Será usada outra variável de mesmo nome mas que não altera a primeira. O mesmo podemos dizer em relação a subtipo. Confuso ? Redundante ? Vou esclarecer. O construtor é chamado de método e dá as ordens para a criação adequada do objeto na memória. O segredo é usar o comando “new”. Vamos ver no console JavaScxript:

Criando a classe Arma() em JavaScript.
fogo
console.log(p.SubTipo)
revolver
var a=p.Tipo
undefined
a
“fogo”
Saída em JavaScript.

Note que todas as variáveis (ou objetos) criados à semelhança daquela classe são iguais em estrutura e métodos. S eu mudar a definição da classe num programa, todos os objetos pertencentes a ela serão automaticamente alterados. É isso que Bertrand Meyer quer dizer em seu estudo. Assim, lá vem a regra#2:

Regra#2 das classes: Toda classe precisa ser modificável, sem alterar o resto do programa.

Essa regra é desafiadora. Imagine que você criou o seu cadastro de armas, com suas telas, com gravação de informação num banco de dados, com relatórios. Se o seu cliente muda de ideia sobre as informações que irão no cadastro, você precisará alterar o programa aquelas partes todas. Mas se você usar corretamente as características da OOP, só precisará alterar um trecho: a classe Armas. Isso porque a classe Armas tem seus métodos. Ela terá, por exemplo, um método para fazer relatório, um método para gravar dados no banco de dados (no caso de Java, por exemplo), um método para mostrar o cadastro numa tela de computador ou num celular etc. Assim, alterando a classe, não é preciso alterar mais nada. Já adianto que na teoria é fácil. Mas na prática é preciso muita disciplina.

Vamos supor que, no caso de armas de fogo, só possam existir pistolas e revólveres. E vamos supor também que pistolas só possam ser aquelas homologadas para uso das polícias com munição .40, enquanto revólveres sejam apenas de uso particular, com munição .38. Sabemos que essa não é a realidade, mas estamos fazendo uma simplificação. Nossa classe ficaria assim:

class Arma {
constructor(Tipo, SubTipo, Municao) {
this.Tipo = Tipo;
this.SubTipo = SubTipo;
this.Municao = function(SubTipo) {
var Municao = “”;
if (SubTipo == ”pistola”) { Municao = “.40” }
else if (SubTipo == ”revolver”) { Municao = “.38” }
else
{ Municao = “” }
return(Municao);
}; // function
}; // constructor
}; // class Arma
Alteração da classe em JavaScript.

Eu defini uma função que vai colaborar com a inicialização de todos os objetos daquela classe. Essa função é defMunicao() e cria dados automaticamente para qualquer objeto da classe.

Note que JavaScript não trabalha com classes do mesmo modo que C++, Smalltalk, ou Java. Ela usa uma adaptação através de funções que lhe dão o “estilo” de linguagem OOP.

Existe banco de dados orientado a objeto ?

Primeiro precisamos refinar a pergunta. Um banco de dados orientado a objeto OODBMS é aquele que não segue o modelo de linhas e colunas (ou t-uplas). ou seja, não segue o modelo relacional. Tal sistema torna bem fácil traduzir as especificações de modelos orientados a objetos (classes) para os métodos de armazenamento. Considerando que os modelos baseados em classe são criados numa ferramenta CASE (como o Sparx Systems / Enterprise Architect) e que não tenho visto modo de traduzir automaticamente isso para om OODBMS, a resposta à pergunta acima tem de ser: não. Apesar de tais bancos existirem em teste no âmbito das universidades, os bancos de dados mais usados são os relacionas Oracle, MySql, MariaDB e PostgreSQL Nenhum deles orientado a objeto. O ideal seria que o armazenamento e recuperação de dados do banco (ou persistência de dados, como dizem) fosse feito com as características da própria linguagem OOP, de modo transparente. A Oracle tem feito pesquisas nesse sentido com a linguagem Java e a tecnologia DAO (Data Access Object) desde 2001. Em 2016, a Oracle publicou artigo1 relacionado a OOP e a linguagem, PL/SQL.

Três dos fundamentos da tecnologia OO são classes, objetos e mensagens. Uma classe descreve um grupo de objetos que têm relacionamentos, comportamentos e também possuem propriedades semelhantes. Um objeto abrange dados e funções relacionados em um pacote completamente independente. Como os objetos são “moldados” a partir de uma definição de classe comum, cada instância de um objeto dentro de uma classe herda todos os mesmos comportamentos e definições de dados. O coração de um banco de dados orientado a objetos é a persistência de objetos, e é o processo de armazenamento e recuperação de objetos que compreende o coração do gerenciamento de dados do objeto.”2 Outro aspecto crucial é a compatibilidade de soluções OOP de persistência com aplicações antigas, baseadas puramente em DBMS. Uma promessa interessante que irá alterar o modo como os novos programas são feitos é o banco de dados não-relacional MongoDB. Em breve estará disponível mais uma obra desta série: “MONGODB DE ZERO A CEM POR CENTO”, falando desse notável produto.

1– Ver [Burleson, 2020]

2– Ibdem

Características das linguagens OOP

Segundo Meyer, uma linguagem orientada a classes (i.e. a objetos) precisa apresentar as seguintes características:

Noção de classe: A noção de classe deve ser o conceito central da linguagem. A noção de classe é introduzida em JavaScript na especificação ECMA Script 2015. Na verdade, números são classes; strings também. Tanto é verdade, que existem propriedades e métodos para números, por exemplo.
Afirmações: A linguagem deve tornar possível equipar uma classe com afirmações (pré-condições, pós-condições, e invariantes), baseadas em ferramentas para produzir documentação a partir dessas afirmações e, opcionalmente, monitorá-las em tempo de execução.
Módulos: As classes devem ser os únicos módulos disponíveis.
Classes como tipos: Todo tipo deve ser baseado em uma classe. Em JavaScript, os tipos de dados (inteiro, ponto flutuante, booleano, indefinido, null, string, array etc), apesar de também terem métodos de declaração que se assemelham aos das linguagens antigas, na verdade são definidos colo classes, havendo uma equivalência do método comum de declarar com o método de classe. No caso da linguagem, os tipos são classes pré-definidas. Mesmo os operadores aritméticos, são na verdade propriedades de classes, podendo ser representados também pelos símbolos mais tradicionais (*, /, +. – etc).
Computação baseada em características: A chamada de uma característica deve ser o mecanismo primária da computação.
Ocultamento da informação: Deve ser possível para o criador de uma classe especificar que uma característica está disponível para todos os clientes, para nenhum cliente ou para clientes específicos.
Tratamento de exceções: A linguagem precisa prover um mecanismo para recuperar-se de uma situação anormal não esperada. Parra atender a essa característica, o JavaScript usa as declarações throw, try, catch e finally. Iremos conhecê-las mais tarde.
Tipagem estática: Um sistema de tipos bem definido deve, impondo um número de tipos regras de declaração e compatibilidade, garantir a segurança do tipo em tempo de execução nos sistemas que aceita.
Genericidade: Deve ser possível escrever classes com parâmetros genéricos formais representando tipos arbitrários.
Herança simples: Deve ser possível definir uma classe como herdada de outra.
Herança múltipla: Deve ser possível para uma classe herdar quantas outras forem necessárias, com um mecanismo adequado para desambiguar conflitos de nomes.
Herança repetida: Regras precisas devem governar o destino dos recursos sob herança repetida, permitindo que os desenvolvedores escolham, separadamente para cada herança herdada repetidamente recurso, entre compartilhamento e replicação.
Genericidade restrita: O mecanismo de genericidade deve suportar a forma restrita.
Redefinição: Deve ser possível redefinir a especificação, assinatura e implementação de um recurso herdado.
Polimorfismo: Deve ser possível anexar entidades (nomes nos textos do software representando objetos de tempo de execução) para objetos de tempo de execução de vários tipos possíveis, sob o controle do sistema de tipos baseados em herança.
Dynamic binding: Chamar um recurso em uma entidade sempre deve acionar o recurso correspondente ao tipo do objeto de tempo de execução anexado, que não é necessariamente o mesmo em diferentes execuções da chamada.
Interrogação de tipo em tempo de execução: Deve ser possível determinar em tempo de execução se o tipo de um objeto está em conformidade com um tipo estaticamente determinado.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *