Paradigmas de Programação: Estrutural e Orientado a Objetos
Paradigmas de Programação: Estrutural e Orientado a Objetos
Paradigmas de Programação
Paradigma declarativo: é feita a descrição do problema. Cada sentença tem significado por si
mesma e adiciona algumas informações diretamente associadas ao resultado. Elas podem ser
apresentadas em qualquer ordem. O foco do paradigma declarativo é, o que o programa deve
realizar.
Paradigma Estrutural
implementado pelo comando goto, que permitia saltos transferir a execução para qualquer
linha específica dos programas. Fazendo uso deste recurso, era possível quebrar a linearidade
do código, ou seja, a execução das linhas de código na ordem que foram escritas, o que
tornava o software incrivelmente difícil de manter e compreender, porém criava recursos
importantes como executar novamente uma ação sem a necessidade de escrever de novo a
mesma linha ou pulando uma ação desnecessária, economizando linhas de código e tempo
de processamento. Este estilo de programação era conhecido como código espaguete, uma
vez que toda estrutura estava toda entrelaçada.
Em 1968 e 1987, Edsger W. Dijkstra e Niklaus Wirth respectivamente, publicaram artigos nos
quais defendiam a abolição do goto nas linguagens de programação de alto nível, alegando
que o comando induzia a vários erros de programação e promovia práticas ruins, além de
tornar extremamente difícil a leitura de programas.
O nome de programação estruturada é dado pela forma que os programas são escritos,
possuindo uma estrutura básica formada por três etapas, que são: sequência, decisão e
interação (Paradigma Estrutural). Em particular eu prefiro chamar esse tipo programação de
programação orientada ao procedimento, que aliás era a matéria na qual aprendi esse
paradigma, pelo simples fato de que realmente tudo é orientado ao procedimento a ser
executado para conseguir o resultado esperado.
Na sequência, temos a ordem em que as coisas devem acontecer, como por exemplo, antes de
atribuir um valor a uma variável é necessário que ela esteja declarada. Na decisão podemos usar
desvios condicionais como IF ou SWITCH para tomar decisões baseadas em valores de variáveis,
e na Interação há a possibilidade de o usuário interagir com o programa enviando informações
e tomando decisões.
Existem muitas linguagens de programação que seguem a forma estruturada. Dentre as mais
conhecidas estão as linguagens C e Pascal. A linguagem C foi criada por Ken Thompson na década
de 60, originalmente desenvolvida para a implementação do sistema Unix. O nome C foi dado
pelo fato de a linguagem ter sido desenvolvida com características semelhantes à outra
linguagem de nome B, uma simplificação da linguagem BCPL.
Vale aqui nos lembrar que as linguagens de programação para CLP (Controlador Lógico
Programável), são por definições da IEC 6113-3, todas linguagens estruturadas e procedimentais,
a última atualização da IEC 6113-3 datada de Fevereiro de 2013, adiciona algumas características
para a Orientação a Objetos, mas veja bem, apenas algumas características, porém não as torna
Orientadas a Objeto. Um dos motivos é a própria característica do CLP quando foi editada pela
primeira vez a norma (em 1993), de trabalhar através de ciclos por varredura (Scan), em um
único sentido (de cima para baixo), o que torna a linguagem sequencial ideal para essa aplicação
de hardware. Os hardwares de CLP atuais, apesar de manterem suas características de varredura,
em virtude do aumento de capacidade computacional, atualmente podem executar rotinas
através de interrupções, múltiplos threads, rotinas principais em paralelo (múltiplos runtimes
em diferentes tempo de ciclo e execução), sendo assim, possuindo capacidade de sobra para
orientação a objetos, dependendo apenas de atualização da normal e das características das
linguagens e editores pelos fabricantes.
Vale lembrar as 3 principais linguagens utilizadas em CLP como Texto Estruturado, Diagrama de
Blocos, Ladder e Gráfico Sequencial (não necessariamente nessa ordem).
O paradigma orientado a objetos tem suas raízes nas linguagens de programação Simula e
Smalltalk nas décadas de 1960 e 1970, respectivamente. Contudo, este paradigma só veio a
ser aceito realmente para uso comercial por volta dos anos 1990, quando surgiu a linguagem
de programação Java, que ficou popularizada pelo fato de se poder programar para todas as
plataformas da mesma maneira, o que até então não era possível.
Uma classe define as características dos objetos, denominadas atributos e suas ações,
denominadas métodos. A classe Aluno, por exemplo, possui atributos
como nome, endereco, idade, serie e turma. Os métodos nos dizem o que pode ser feito com o
objeto, e no caso do objeto aluno, podemos armazenar, alterar, excluir e buscar. A quantidade
de atributos e métodos varia em cada programa e devem ser criados de acordo com a
necessidade especificada durante a fase de abstração do problema. As classes também
descrevem relacionamentos entre objetos (da mesma classe, ou de classes distintas). Ou seja,
o software é representado por um conjunto de estruturas de dados que interagem entre si,
cada uma delas contendo uma coleção de dados específicos e um conjunto de rotinas
(métodos) para manipular esses dados (ações sobre dados).
Classe
Uma classe é uma estrutura que abstrai um conjunto de objetos do mesmo tipo . É nela que
são definidas as características (estrutura) dos objetos daquele tipo, ou seja, que dados
(atributos) são armazenados pelo objeto e que operações (métodos) podem ser efetuadas
com estes dados.
• Atributo
Os dados contidos em uma classe são conhecidos como atributos daquela classe e
representam características presentes nos objetos do tipo da classe. Cada atributo
deve ter um escopo de acessibilidade, um nome e ser de um tipo, que será ou um
tipo de dado nativo da linguagem (geralmente tipos: inteiro, real, caractere, string,
booleano), ou outra classe já existente na linguagem ou definida pelo programador.
Por exemplo, para a classe Pessoa, que representa uma abstração de uma pessoa,
seus atributos poderiam ser os seguintes: nome (cadeia), idade (inteiro), peso (real)
e rg (cadeia). Consequentemente cada objeto do tipo Pessoa teria estes atributos,
cada um deles com um valor específico.
Vale ressaltar que um atributo de uma classe também pode ser declarado como
sendo um objeto de outra classe. Por exemplo, na classe Pessoa, o atributo rg
poderia ser da classe Rg e esta, por sua vez, teria os atributos nome, nome do pai,
nome da mãe, data de nascimento, naturalidade, número do RG, etc.
• Método
Assim como no paradigma procedimental, além dos métodos poderem ter suas
próprias variáveis locais, estes também podem acessar as variáveis globais, que no
caso da orientação a objetos são os atributos da classe ou da sua instância em
questão. Estes também podem receber ou não parâmetros, e podem retornar um
valor a quem o chamou (similar a uma função) ou apenas executar alguma ação com
os atributos acessíveis a ele (similar a um procedimento).
• Construtores e Destrutores
Objeto
Encapsulamento
Dessa maneira, podemos pensar em uma classe como uma caixa preta, pois sabemos o que
ela faz, conhecemos e interagimos com sua interface externa, mas não nos preocupamos
como o processo é feito lá dentro.
Modificadores de Acesso
Nas linguagens de programação orientada a objetos (e.g., C++, Java, C#), em geral, os
níveis de encapsulamento são indicados através das seguintes palavras-chaves:
Essas palavras chaves são chamadas de “modificadores de acesso” e são utilizadas para
definir quem terá acesso à classe e a seus membros (atributos e métodos), sendo assim,
essenciais para o encapsulamento.
Geralmente atributos encapsulados são atributos que não podem ser alterados
livremente, por estarem intimamente relacionado a outros atributos e a integridade do
objeto, de tal forma que se forem alterados de forma indevida, podem tornar o estado
do objeto inconsistente.
Para lidar com atributos encapsulados com o uso dos modificadores protected ou private,
é preciso implementar métodos públicos que acessam e alteram esses atributos, fazendo
todo tratamento para que a integridade do objeto seja mantida.
Herança
Herança Simples
O conceito de herança é construído sobre a ideia de que algumas classes estão intimamente
ligadas a outras classes, e que por conta disso, compartilham características em comum (i.e.,
atributos, métodos, comportamentos, etc.).
Abaixo tem uma figura que torna simples o entendimento, a classe mãe Animal, contém o
nível mais genérico, as classes filhas ou derivadas Mamífero e Ave, possuem um novo grau
de especialização, porém não deixar ser uma classe animal, o mesmo acontecendo com os
níveis subsequentes.
O mecanismo de herança permite que uma classe seja definida a partir de uma outra classe
já existente, com isso a definição da classe derivada herda automaticamente atributos,
métodos e comportamentos da classe base. Por conta disso, herança é considerada uma
técnica poderosa, uma vez que encoraja a reusabilidade durante o desenvolvimento e provê
uma grande economia de tempo, sem perda de qualidade no desenvolvimento (pois você
não precisa reescrever, testar, depurar e manter o código herdado).
Herança múltipla
Algumas linguagens orientadas a objetos como C++, Scala e Python também oferecem o
mecanismo de herança múltipla, que permite que uma classe herde de mais de uma classe
ao mesmo tempo.
Contudo herança múltipla tem sido uma questão sensível por muitos anos, pois pode
aumentar a complexidade na programação e gerar ambiguidade em situações onde mais de
uma classe base possui atributos ou métodos como mesmo nome, embora hoje já haja
abordagens que resolvem essa ambigüidade (i.e., em C++ por exemplo, é usado
classePai::AtributoAmbiguo para explicitar de qual classe o atributo ambíguo será acessado).
Polimorfismo
A palavra polimorfismo vem do grego, πολύς (poli) que significa muitas e μορφή (morphos)
que significa formas, ou seja, muitas formas. Em programação, é dito que um objeto é
polimórfico quando o sistema de tipo da linguagem atribui mais de um tipo a este.
float), e em algumas linguagens ainda, age como concatenador de strings (string x string ->
string). Sendo assim, podemos dizer que operador de soma é polimórfico, pois possui mais
de um tipo, e assume um deles de acordo com o contexto. Outro exemplo, seria a função
length(), que retorna o número de elementos em um vetor de tipo T (T[] -> int), sendo T
qualquer tipo.
Existem quatro tipos de polimorfismo que uma linguagem pode ter (note que nem toda
linguagem orientada a objetos implementa todos os tipos de polimorfismo).
• Sobrecarga
Sobrecarga ocorre quando definimos numa mesma classe métodos com o mesmo nome,
mas com assinaturas diferentes, ou seja, que recebem e/ou retornam parâmetros de
tipos diferentes. Esse tipo de polimorfismo ocorre em tempo de compilação, pois
conseguimos saber qual definição do método será invocada para um dado grupo de
parâmetros antes mesmo de o código ser executado.
• Coerção
Em C# por exemplo, é possível converter implicitamente um tipo int para double, como
ilustrado abaixo:
int a = 2;
double b = a;
Esse tipo de polimorfismo nos permite, por exemplo, definir uma função de soma, para
o tipo double, e usá-la para somar inteiros, bem como somar variáveis dos dois tipos.
Paramétrico
Polimorfismo paramétrico permite que uma função ou um tipo de dado seja escrito de forma
genérica, de modo que ele possa lidar com valores uniformemente, sem dependendo do seu
tipo.
Inclusão
Objetos de um subtipo (classe derivada) podem ser manipulados como sendo objetos de
algum de seus supertipos (classe base).
Classe Abstrata
Classe abstrata é uma classe que não pode ser instanciada diretamente, mas que pode ser
herdada por outras classes. Nela, definimos atributos, métodos e métodos abstratos, que
são métodos com apenas a assinatura definida, sem implementação.
Classes abstratas também podem herdar de outras classes abstratas e uma classe abstrata
pode ser herdada por uma classe concreta (i.e., classes instanciáveis, como Pessoa,
Trabalhador, Estudante, Pilha<T>, etc.).
Um bom exemplo de classe abstrata é a classe Animal, uma vez que esta classe em si é uma
abstração de todas as espécies de animais, e consequentemente, não existe no mundo real
um animal que seja apenas do tipo Animal, e sim “animais concretos” que são de alguma raça
e espécie específica (e.g., pastor alemão, harpia, pônei, etc., note que esses animais também
pertencem a outras classes abstratas, sendo estas cachorro, ave e cavalo, respectivamente,
que também herdam de Animal), ou seja, que pertencem a alguma classe concreta derivada
de Animal.
Interface
Na interface definimos atributos e métodos sem implementar nada do seu código (apenas
assinaturas) e a classe que implementa a interface é obrigada a fornecer (implementar) os
códigos definidos na interface. Vale também evidenciar que uma classe pode implementar
mais de uma interface, sendo esta uma alternativa para compartilhamento de características
entre classes em linguagens que não suportam herança múltipla.
Por exemplo, podemos pensar no código de barras de um produto como uma interface, uma
vez que são estabelecidos padrões de código de barra, e é necessário que todo produto
implemente esses padrões para poder ser identificado.
Legibilidade
Uma vez que os paradigmas procedimental e orientado a objetos são imperativos, nestes o
programador precisa definir exatamente como quer que o programa seja computado,
explicitando, além da lógica do algoritmo, todo o fluxo de controle e manipulação dos dados.
Por sua vez, nas abordagens dos paradigmas funcional e lógico, que são declarativos, o
programador delega ao sistema todo o fluxo de controle da execução do programa,
permitindo o mesmo apenas declarar as estruturas e funções necessárias para a solução do
problema.
Baseado nesse fato podemos argumentar que a abordagem imperativa faz com que um
programa seja mais difícil de ler se comparado a declarativa, pois uma vez que é necessário
explicitar sobre qual fluxo de controle o programa deve ser computado, parte do código
passar a ser mais para a máquina do que para o ser humano, o que pode inclusive dificultar
a compreensão deste quanto a ideia do algoritmo em si, uma vez que para o ser humano
compreender o processo de resolução de um problema, nem sempre é preciso explicitar
todo o fluxo, bastando uma abstração adequada.
Vale ressaltar também que, como na programação declarativa os dados são imutáveis, o
programa é livre de efeitos colaterais e possui transparência referencial, uma vez que você
entende como algo funciona, você não precisa mais se preocupar se em algum momento
especifico pode acontecer um erro naquele código caso o programa seja executado de
alguma forma não planejada. Essa característica torna a leitura do código muito mais simples,
uma vez que o resultado de todas as partes do programa não compartilham estados em
comum e são independentes das demais partes (e de sua ordem de execução), permitindo o
programador se concentrar em entender apenas a parte desejada, abstraindo as demais
partes, sem receio de que o não conhecimento delas possa facilitar um erro.
Capacidade de Escrita
Cada paradigma foi feito para expressar de forma simples e natural um tipo de problema,
consequentemente um programa relativamente simples pode se tornar complexo num
paradigma inapropriado.
Confiabilidade
No paradigma orientado a objetos, ainda pode ser usado o conceito de encapsulamento para
tornar o programa mais modular, como uma caixa preta. Assim, é fácil desenvolver um
módulo, testá-lo, e uma vez testado, usá-lo em outras partes do projeto com total
confiabilidade, bem como em outros projetos. Dessa forma, é possível aumentar a
produtividade sem perder confiabilidade.
Já no paradigma funcional o problema citado não existe, uma vez que o programa não tem
estados, e todos os dados definidos são imutáveis. Propriedades como transparência
referencial e ausência de efeitos colaterais tornam a programação funcional muito mais
modular e fácil do seu código ser analisado e testado de forma isolada, sendo um enorme
trunfo quanto à confiabilidade do programa. O mesmo também vale para programas
desenvolvidos no paradigma lógico, que assim como o funcional, também tem transparência
referencial e ausência de efeitos colaterais.
Eficiência
Reusabilidade
Como vimos ao longo deste trabalho, nos paradigmas procedimental, funcional e orientado
a objetos podemos escrever funções e / ou subrotinas, e por meio dessas encapsular uma
determinada ação ou processo do sistema, bastando apenas chamá-la quando necessário; o
que nos evita ter de reescrever o código em todos os lugares onde aquela ação se faz
Manutenabilidade
Manutenção em programas imperativos tende a ser uma tarefa mais difícil já que esses
possuem estados mutáveis com dependências implícitas e, possivelmente, subrotinas pouco
expressivas. Uma forma de minimizar essa dificuldade é fazer uso do conceito de
encapsulamento, ocultando assim toda parte “sensível” do programa (e.g., controle interno
de um objeto, validações da regra de negócio, etc.), tornando assim o programa mais modular
e por tanto, mais fácil de testar e manter. No paradigma procedimental, que não possui
explicitamente um conceito de encapsulamento, podemos modularizar o código por
subrotinas e bibliotecas.
Bibliografia:
https://leandromoh.gitbooks.io/tcc-paradigmas-de-programacao
https://fit.faccat.br/~guto/artigos/Artigo_Paradigmas_de_Programacao.pdf
https://www.devmedia.com.br/paradigmas-de-programacao-estruturado-e-orientado-a-
objetos/27335
Programação de computadores em Java, Rui Rossi dos Santos, Nova Terrra, 2011
https://www.devmedia.com.br/programacao-orientada-a-objetos-versus-programacao-
estruturada/32813
https://www.differencebetween.com/difference-between-declarative-and-vs-imperative-
programming/
https://bar8.com.br/abap-oo-versus-procedural-50474ff371a5
http://www.dsc.ufcg.edu.br/~pet/jornal/junho2011/materias/recapitulando.html