TREINAMENTO
ORIENTAÇÃO A
OBJETOS
SOFTMAP
Tecnologia e Inovação
Copyright © 2016 Softmap Treinamentos e Serviços EIRELI ME. Todos os direitos reservados. Nenhuma parte deste documento pode ser copiada, reproduzida, traduzida ou transmitida por qualquer meio eletrônico ou mecânico, na sua totalidade ou em parte, sem a prévia autorização
SUMÁRIO Introdução ... 3 Linguagens de Programação ... 3 Conceitos ... 4 Encapsulamento ... 6 Herança ... 7
Relacionamento entre objetos ... 8
UML (Unified Modeling Language) ... 9
Diagrama de Classes ... 10
Diagramas de Interação ... 11
Padrões de Projeto ... 13
Princípios SOLID ... 13
(S) Single Responsability Principle - Princípio da Responsabilidade Única ... 13
(O) Open / Closed Principle - Princípio Aberto / Fechado... 13
(L) Liskov Substituiton Principle - Princípio Substituição de Liskov ... 14
(I) Interface Segregation Principle ... 14
(D) Dependency Inversion Principle – Princípio da Inversão de dependência ... 14
Padrões de Projeto GOF ... 14
Padrões de Criação ... 15
Padrões Estruturias ... 18
Orientação a Objetos (OOP)
IntroduçãoA proposta deste material é apresentar Orientação a Objetos de uma forma prática, ainda que o assunto seja essencialmente de conteúdo conceitual. Para complementação dos conceitos serão usados exemplos em algumas linguagens de programação, sem aprofundamento na linguagem, para oferecer uma ideia básica de como os conceitos são expressos por essas linguagens.
Inclui-se ainda, uma pequena introdução a UML e padrões de projeto. UML é um padrão amplamente utilizado para representar, através de vários tipos de diagramas, modelos orientados a objetos, sem se prender aos detalhes de uma implementação de linguagem. Padrões de projeto oferecem a possibilidade de construir bons projetos e melhor aproveitar os recursos oferecidos pela Orientação a Objetos. Os padrões de projeto são um apanhado de soluções comuns a vários tipos de problemas. É resultado do aprendizado prático compartilhado para que novos projetos evitem repetir antigos erros.
Linguagens de Programação
Nem todas as linguagens são orientadas a objetos e nem todas as linguagens tem todos os conceitos de OO. E são poucas aquelas que podem ser consideradas puras, isto é, tudo é considerado um objeto.
Além do mais, ainda que um programador da atualidade use na maior parte do tempo uma linguagem orientada a objetos, dificilmente ele estará limitado a tais linguagens. SQL e JavaScript são apenas alguns exemplos de linguagens não OO. E mesmo nesses casos, são encontradas interações, como é o caso da persistência de objetos em banco de dados relacionais através do uso de ORM.
A Orientação a Objetos não é algo que surgiu recentemente. Ganhou força através da linguagem C++ e com Java se tornou realmente popular. A importância de OO é tão grande que muitas linguagens que originalmente não ofereciam suporte a objetos, passaram a adotar tais conceitos.
Muito antes das linguagens orientadas a objeto, a programação utilizava a linguagem de máquina (assembly). Apesar de eficiente e ainda usada em alguns casos, não é produtiva. Surgiram então linguagens que permitiram um melhor entendimento do que se pretendia com código escrito, mas a medida que aumentavam os programas e a complexidade deles, novos desafios surgiam.
A linguagem C é a linguagem que C++ usou para se basear. Assim como outras linguagens procedurais, ela buscou organizar o código nas chamadas funções e permitir a criação de estrutura de dados. As funções permitem a reutilização de um determinado código, sem duplicação desse código. E as estrutura de dados possibilita o agrupamento de dados relacionados, não se limitando aos tipos primitivos (numéricos e caracteres).
E novamente surgiram situações que esse modelo tinha dificuldades em resolver. Por exemplo, uma estrutura de dados tem todos seus dados acessíveis a qualquer parte de um programa, não havendo nada que impeça seu uso indevido ou uma alteração de um dado que deveria provocar a execução de outros códigos.
Foi em busca de melhorar esse vínculo de estrutura de dados e código e sua reutilização que começa a surgir a Orientação a Objetos, que traz o conceito de classes, uma estrutura de dados onde apenas um conjunto limitado de funções, os métodos, tem acesso a detalhes. Ainda que se diga que é possível realizar uma programação orientada a objetos em C, isso exige muita disciplina e é sujeito a erros, pois a linguagem não oferece o suporte natural. Mas o suporte a orientação a objetos em uma linguagem não é suficiente para se desenvolver corretamente um bom programa. É necessário conhecer bem os conceitos de OO e as boas práticas. Do contrário, facilmente um código pode se tornar um programa procedural usando uma linguagem OO ou certas práticas válidas em um programa procedural criar vícios que prejudiquem o projeto.
Conceitos
O mundo real é rodeado de objetos. Lidamos com cadeiras, computadores, comida, carros e um infinito conjunto de exemplos de objetos da vida real. Esses objetos possuem características e podem executar coisas ou reagir a certas condições. Inspirado nisso, a programação orientada a objetos tenta trazer algo natural ao ser humano ao contexto do desenvolvimento de sistemas.
Na orientação a objetos, a estrutura fundamental está nas classes, sendo que um objeto é criado a partir de uma classe. Uma classe é formada por atributos e métodos. Os atributos são dados e métodos o código que utiliza tais dados.
Podemos dizer que uma cadeira representa uma classe e cada cadeira fabricada é um objeto. Uma cadeira tem como características gerais uma quantidade de pernas, existência de encosto ou braços, se permite reclinar, pode ser movimentada. Essas características são os atributos da classe. Uma cadeira ajustável altera altura ou inclinação, dentro de um limite físico permitido. Esse ajuste é feito, em termos de orientação a objetos, em um método, que executa a ação e verifica se os valores de altura ou inclinação estão sendo respeitados. Outro exemplo da vida real menos concreto são as formas geométricas. Quadrados, triângulos, círculos, retângulos e polígonos são exemplos de formas geométricas, cada qual com características próprias, mas que compartilham entre si características comuns. Se fossemos desenvolver uma aplicação de desenho, essas formas ocuparão uma área da tela, poderão ter cores para pintura da borda e do interior, os atributos das classes. Tais objetos poderão ser redimensionados, movimentados, ações que serão expressas em métodos dessas classes.
As linguagens usam sintaxes diferentes para definir uma classe. Por exemplo, em C++ temos class Shape { private: int topLeft; int topRight; int bottomLeft; int bottomRight; public:
Shape(int topLeft, int topRight, int bottomLeft, int bottomRight); void setTopLeft(int topLeft);
int getTopLeft(void); ...
int getBottomRigth(); void draw(void); }
Shape::Shape(int topLeft, int topRight, int bottomLeft, int bottomRight) {
... }
A mesma classe em Java:
public class Shape { private int topLeft; private int topRight; private int bottomLeft; private int bottomRight;
public Shape(int topLeft, int topRight, int bottomLeft, int bottomRight) {
... }
public void setTopLeft(int topLeft) { this.topLeft = topLeft;
}
public int getTopLeft() { return topLeft;
} ...
public void setBottomLeft(int bottomLeft) { this. bottomLeft = bottomLeft;
}
public void draw() { }
}
A mesma classe em C#
class Shape {
private int _topLeft; private int _topRight; private int _bottomLeft; private int _bottomRight; public int topLeft {
get { return _topLeft; } set { _topLeft = value; } }
public Shape() { }
public void draw() { }
}
Ainda que existam diferenças de sintaxe, todas seguem os mesmos conceitos. A classe de nome Shape tem a definição dos atributos topLeft, topRight, bottomRight e bottomLeft e de um método chamado draw.
Em UML, a representação da classe é feita através de um diagrama de classes:
Aqui é feita apenas a representação de uma única classe. Mais adiante é falado um pouco mais sobre diagrama de classes.
Encapsulamento
A junção de atributos e métodos relacionados em uma classe é denominada de
encapsulamento. Para que ela cumpra seu papel, atributos e métodos precisam ser
conceitualmente relacionados. Nossa classe Shape tem atributos que representam a localização espacial da forma geométrica. Incluir um atributo que não tenha relação com conceitos geométricos é um erro de projeto e pode se tornar um problema. Por outro lado, somente os métodos dessa classe devem ter acesso e poder alterar os valores dos atributos. Por exemplo, um quadrado deve ter garantia de que as medidas dos quatro lados são iguais, e isso é responsabilidade dos métodos desta classe.
Essa característica de limitar o acesso dos atributos a apenas os métodos da classe é uma parte do encapsulamento conhecida como ocultação de dados ou visibilidade, onde ficam estabelecidos níveis de acesso aos atributos e até mesmo aos métodos. A visibilidade pode ser pública ou privada. No caso dos atributos, a prática é mantê-los com acesso privado aos métodos da classe. Já os métodos devem oferecer uma visibilidade pública para serem úteis a outras classes / objetos. O comportamento do objeto é então definido pelos seus métodos que são os únicos que manipulam os atributos.
A possibilidade de um objeto manter dados através de seus atributos confere a ele a característica de retenção de estado. Um estado é o conjunto de valores mantidos pelos atributos em um dado momento. Com a alteração de um valor de um atributo, o estado do objeto é alterado resultando em um evento. Um evento pode ser usado para notificar a outros objetos a ocorrência dessa mudança.
Shape
topLeft : int topRight : int bottomLeft : int bottomRight : int draw()Os atributos de uma classe podem também definir a identidade de um objeto, dando uma unicidade, isto é, nenhum outro objeto será igual. Os atributos podem ainda ter características que determinam se poderão ser alteradas. Um atributo é considerado
imutável se seu valor não é alterado após definido, o que normalmente ocorre na criação do
objeto. Um atributo mutável não tem restrições de alteração.
Herança
Seguindo com o exemplo da classe Shape, observa-se que ela define 2 pares de coordenadas ‘x / y’, mas insuficientes para tratar uma diversidade de formas como Quadrado, Retângulo, Círculo e Polígonos. No entanto são valores que servem a todas essas formas, pois determinam a área em que se pretende desenhar a forma específica. Esta classe apresenta uma característica chamada de abstração. Ela define características comuns de um conceito, mas insuficientes para atender a todos cenários. E o motivo de não termos em uma só classe a possibilidade de desenhar todas as formas é porque isso traria grande complexidade ao código e teríamos atributos que não são usados em todos os casos, como um atributo raio para um círculo.
Portanto a orientação a objetos traz o benefício de diminuição de complexidade e por consequência melhor manutenção.
Quando, em uma determinada classe, um método que é considerado abstrato, isto é, não possui uma implementação definida, esta classe não está completa e não pode gerar objetos a partir dela. Isso nos obriga criar uma nova classe que estende e implemente de forma mais adequada. Essa nova classe mantém as características da classe estendida através de um conceito conhecido como herança.
O diagrama de classes a seguir mostra como a herança entre classes é representado em UML.
É através da herança é que será possível desenhar formas como quadrado, círculo ou retângulo. É muitas vezes chamada como relação “é um”, pois um quadrado é uma forma geométrica.
Uma classe Square e Circle é considerada uma especialização da classe Shape e esta uma
generalização de Square e Circle. Aqui é preciso observar que mesmo que Square e Circle
Square e Circle. Isto é, um objeto de Square não será totalmente compatível com um objeto Circle, não havendo conversão de um para outro.
A classe Shape pode ser chamada de classe pai ou superclasse ou classe base. As classes herdadas são denominadas subclasses, classes derivadas ou classes filhas.
Todos os atributos definidos em Shape são herdados pelas classes Circle e Rectangle. Circle adiciona o atributo radius, específico ao contexto de desenho de círculos. Da mesma forma Rectangle possui um novo atributo chamado width, que determina a medida dos lados.
Observa-se no diagrama que tanto Circle quanto Rectangle tem o método draw. Isso ocorre porque estas classes terão uma implementação especializada da operação de desenhar a forma geométrica. Essa possibilidade de sobrescrever um método definido na classe pai é chamada de polimorfismo. O polimorfismo é o que permite que o método draw em Circle faça o desenho de um círculo e draw em Rectangle desenhe um quadrado, sem que Shape precise ser alterado.
Dependendo do número de classes que são estendidas por uma nova classe, a herança pode ser denominada simples ou múltipla. Uma herança pode ainda ser classificada em herança de implementação e de interface ou tipo. A herança de interface ocorre quando uma classe estende uma ou várias interfaces. Uma interface, diferente de uma classe, define apenas métodos, sem implementação nenhuma. Não podem ser criados objetos.
Nem todas as linguagens permitem a herança múltipla, como é o caso de Java. No entanto Java possui um tipo de herança múltipla de interfaces.
Relacionamento entre objetos
Um programa orientado a objetos dificilmente terá apenas objetos de uma única classe. Muito pelo contrário, várias classes diferentes são criadas para representar conceitos distintos. E os objetos dessas classes relacionam-se entre si, de forma a colaborarem entre si, cada um com seu comportamento e responsabilidade individual, mas complementares no conjunto.
São três os tipos de relacionamentos. O mais comum é denominado associação. Há ainda a agregação e a composição, que são relacionamentos onde há uma maior dependência entre os objetos.
O grau de dependência que um objeto tem em relação a outro objeto define o acoplamento entre estes objetos. Por princípio deve-se buscar manter um baixo acoplamento. É uma característica que determina o quanto um objeto ou classe consegue realizar suas operações sem depender de outros objetos. Outro conceito relacionado à dependência é a coesão. É resultado do encapsulamento e implica em operações conceitualmente relacionadas. Por princípio espera-se uma coesão forte, isto é, o objeto é especializado em um determinado conceito e não é preciso um segundo objeto auxiliar. Por outro lado, esse objeto limitará a operações associadas ao conceito. Por exemplo, para manter uma coesão alta em classes de desenho de forma geométrica, elas devem permitir o maior número de operações como rotação, deslocamento ou redimensionamento. Se forem incluídas operações envolvendo a persistência, como salvar em um arquivo no disco rígido, a coesão é prejudicada. O reaproveitamento dos objetos depende das características da coesão e do acoplamento. O relacionamento entre objetos tem ainda outras características. Quando objetos chamam métodos uns dos outros, diz-se que há uma comunicação por troca de mensagens. Quando dois objetos se reconhecem entre si, podendo os dois se comunicar, há uma associação
bidirecional. Quando o objeto que recebe a mensagem, isto é, cujo método é executado, não sabe quem envia a mensagem há uma associação unidirecional.
O número de objetos de uma classe que outro objeto pode interagir determina a cardinalidade ou multiplicidade de um relacionamento.
A agregação é um tipo de associação onde vários objetos de classes normalmente distintas são unidos em um objeto principal. De forma similar exista a composição, em que há um objeto que contém outros. A diferença está no tempo de vida dos objetos que formam uma composição ou agregação. Na agregação um objeto em particular pode continuar existindo mesmo depois que o objeto principal deixa de existir. Já na composição quando o objeto principal deixa de existir, todos os demais objetos também. Um casal pode ser considerado uma composição, pois deixa de existir se uma das partes morrer. Já uma família é um tipo de agregação. Se um dos indivíduos morre, a família não necessariamente deixa de existir.
UML (Unified Modeling Language)
Até pouco tempo antes da criação da UML, a literatura existente utilizava várias formas de representação de classes e objetos em diagramas. Dependendo do autor, havia uma forte influência dos diagramas ER (entidade-relacionamento), usados para modelagem dos relacionamentos entre tabelas em um banco de dados.
UML surgiu quando três das principais personalidades na área de orientação de objetos se uniram em busca de uma padronização na modelagem OO. Cada um deles havia desenvolvido uma metodologia para análise e projeto de sistemas orientados a objeto e havia muitas similaridades entre elas.
A grande vantagem do uso de UML é auxiliar na visualização e tomada das decisões de projeto, além de documentar o sistema. Seu uso não exige obriga adotar uma determinada linguagem e permite a liberdade no nível de detalhes expressos nos diagramas, que pode ser simples diagramas conceituais ou até mesmo diagramas que servem de base para geração de código.
Os diagramas se dividem em dois grandes grupos: estruturais; o Diagrama de classes; o Diagrama de objetos; o Diagrama de componentes; o Diagrama de instalação; o Diagrama de pacotes;
o Diagrama de estrutura compostas; o Diagrama de perfil.
comportamentais.
o Diagrama de caso de uso; o Diagrama de sequencia; o Diagrama de colaboração; o Diagrama de estados; o Diagrama de atividade; o Diagrama de tempo;
o Diagrama de interação.
Esse material introduz os diagramas de classes e diagramas de sequencia e de colaboração, mais comuns e muito usados na literatura.
O diagrama de classes integra os diagramas estruturais. Nele são representadas as classes, seus métodos e atributos, herança e associações.
Os diagramas de sequencia e de colaboração são diagramas comportamentais, isto é, permitem modelar o sistema em aspectos dinâmicos. Esses dois diagramas são intercambiáveis, e oferecem o mesmo tipo de informação, sendo complementares.
Diagrama de Classes
Em um diagrama de classes, usa-se retângulo para representar classes. Na sua forma mais simples ele contém apenas o seu nome, como no caso das classes Curso e Estudante. Para apresentar os atributos e métodos, divide-se o retângulo em até 3 partes. Os atributos são exibidos na segunda parte e métodos na terceira. A classe Pessoa mostra os atributos nome e nascimento. Na classe disciplina é possível ver um método.
A sintaxe usada para atributos é:
[ + | # | - ] nome [‘[’dimensão‘]’] [‘:’ tipo] [‘=’ valor_inicial]
Os caracteres +, # e - indicam a visibilidade do atributo e seu uso é opcional e o significado é, respectivamente, público, protegido e privado. Quando nada é indicado, o padrão é público.
Após o nome pode ser incluído, entre colchetes, a multiplicidade, isto é, o número de valores que o atributo pode conter (array / matriz).
Quando é indicado o tipo do atributo, ele é indicado logo após o nome seguido do símbolo ‘:’ (dois pontos). Um atributo pode ainda conter um valor inicial, indicado pelo símbolo ‘=’ (igual) seguido pelo valor.
A sintaxe para métodos é:
[ + | # | - ] nome ‘(’argumentos‘)’ [‘:’ tipo]
Assim como para atributos, pode ser indicada a visibilidade antes do nome. Após o nome, entre parênteses, são listados os argumentos, separados por vírgula. Quando o método fornece retorno de valor, ele pode ser indicado ao final pelo símbolo ‘:’ (dois pontos) seguido pelo tipo.
O relacionamento entre classes é feito por linhas, cujo significado depende do símbolo usado na extremidade. Associação Bidirecional Associação Unilateral Composição Agregação Herança
Nas extremidades das linhas de associação, pode-se indicar a multiplicidade ou cardinalidade. Por padrão, quando nada é incluído, assume-se que não há restrição no número de associações. No diagrama, o relacionamento entre as classes Professor e Disciplina faz uso da multiplicidade com o significado de que uma disciplina poderá ter apenas um professor, mas um professor pode dar aulas em mais que uma disciplina.
Diagramas de Interação
Diagramas de sequencia ou de colaboração são dois tipos de diagramas em que é possível expressar a troca de mensagens entre objetos em uma determinada sequencia, oferecendo uma visão dinâmica do sistema. Um diagrama de classes mostra os métodos de cada classe, mas não permite enxergar como cada objeto interage entre si e a ordem de execução dos métodos.
Apesar de esses dois diagramas terem esse objetivo comum, eles se diferenciam na forma do desenho. O diagrama de sequencia é mais voltado ao aspecto da troca de mensagens enquanto que o de colaboração permite visualizar o relacionamento entre os objetos.
O diagrama de sequencia distribui os objetos em colunas. A chamada dos métodos é representada através de uma flecha entre dois objetos, com o nome do método sobre ela. A ordem de execução dos métodos se dá pela ordem em que são apresentadas as chamadas no eixo vertical. Esse tipo de diagrama ocupa mais espaço, mas tem maior facilidade em se visualizar a ordem de execução.
O diagrama de colaboração se parece em parte com um diagrama de classes, pois as classes se distribuem de uma forma livre, não em colunas e as linhas de relacionamentos estão presentes. É sobre as essas linhas que são indicadas as chamadas aos métodos de cada classe. A ordem de execução desses métodos é feita usando uma numeração.
Padrões de Projeto
Conhecer os conceitos que envolvem o desenvolvimento orientado a objetos é apenas uma parte do que se precisa para construir bons aplicativos. À medida que se adquire prática e nos deparamos com problemas, descobre-se melhores formas para a definição de classes. Mas essa prática leva tempo e muitas vezes os problemas enfrentados são os mesmos que outros programadores em algum momento também tiveram. Para isso é importante conhecer alguns princípios e padrões de projeto que são a contribuição de tais programadores.
Princípios SOLID
(S) Single Responsability Principle - Princípio da Responsabilidade Única
Uma responsabilidade é definida como sendo as obrigações que uma classe tem em termos de comportamento. Uma responsabilidade pode ser algo que uma classe faz, como execução de cálculos, criação de novos objetos, se comunicar com outros objetos ou coordenar outros objetos. Uma responsabilidade pode ser também ter conhecimento dos dados encapsulados, de objetos relacionados ou de dados derivados.
Este princípio orienta que uma classe deve sempre buscar manter uma única responsabilidade alcançando uma coesão alta. A consequência prática é que classes tendem a sofrer poucas alterações.
Considerando o exemplo das classes de forma geométrica. Um aplicativo de diagramação o usuário terá uma tela em que são exibidas as formas em sua representação gráfica, onde elas poderão ser redimensionadas, movimentadas dentro do espaço e com certeza espera-se que possa ser salvo em um arquivo. Essas são as responsabilidades de nosso sistema. Se tivermos apenas uma única classe para atender a todas essas funcionalidades, qualquer alteração que ocorra afetará sempre a mesma classe, além de produzirmos um código complexo. O melhor é separar em três classes distintas, Rectangle representando um quadrado e realize cálculos, RectangleView para exibir em tela um quadrado a partir de Rectangle e por fim RectangeFile que é capaz de criar um arquivo com os dados de Rectangle e posteriormente recriá-lo a partir do arquivo. A partir dessa separação, se uma alteração envolvesse a forma de como o quadrado é desenhado, apenas RectangleView sofreria a alteração.
(O) Open / Closed Principle - Princípio Aberto / Fechado
A definição diz que uma classe precisa permitir estender, mas não deve permitir modificações. É uma diretriz para o uso de abstração e polimorfismo.
Permitir estender é oferecer a possibilidade de incluir novas funcionalidades. A extensão é obtida através do uso de herança, mais especificamente abstração. A classe precisa estar preparada de forma que uma extensão não obrigue a modificação de um código existente. A classe Shape apresentada anteriormente possui um método ‘draw’. Se precisarmos alterar esse método para suportar uma nova forma geométrica, então o princípio não está sendo atendido.
A consequência prática é que uma vez definida uma classe, ela não é afetada pelas classes que a estendem.
(L) Liskov Substituiton Principle - Princípio Substituição de Liskov
Esse princípio orienta o uso de herança entre classes. De acordo com esse princípio, toda classe derivada pode ser substituída pela classe pai. Em outras palavras, se um código reconhece a classe Shape, esse mesmo código poderá interagir com qualquer objeto herdando Shape, como Rectangle ou Circle, sem a necessidade de modificações. Outro efeito desse princípio é que a classe Shape não é modifica se uma nova classe herdar dela.
(I) Interface Segregation Principle
Este princípio trata dos casos em que não é possível manter alta coesão em uma classe. Nesses casos facilmente encontra-se um acoplamento alto entre esta classe e seus clientes. O objetivo deste princípio é quebrar a classe em interfaces para cada tipo de cliente, diminuindo o acoplamento.
Para isso utiliza-se a herança de múltiplas interfaces.
(D) Dependency Inversion Principle – Princípio da Inversão de dependência
Muitas vezes um objeto depende de outros objetos que fornecem serviços que auxiliam nas tarefas. Esse tipo de dependência pode causar um acoplamento alto, normalmente obrigando a esse objeto criar os objetos auxiliares.
De acordo com esse princípio, o objeto não deve conhecer a classe concreta dos objetos de serviço, apenas a interface de interesse. A criação desses objetos de serviço também não é de responsabilidade do objeto principal, que apenas recebe os objetos de serviço. Como resultado, reduz-se o acoplamento.
Esse princípio está amplamente presente nos principais frameworks do mercado, como por exemplo spring-framework para java.
Padrões de Projeto GOF
Existe na literatura técnica um grande conjunto de padrões de projeto documentados, muito deles específicos a determinados contextos, como integração de sistemas, desenvolvimento de sistemas corporativos, desenvolvimento WEB, etc. Porém até mesmo esses padrões se utilizam dos padrões GOF. Trata-se de uma coleção de 23 padrões documentados por Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides, conhecidos como a gangue dos quatro (GOF).
Esse material apenas irá relacionar de forma resumida os padrões, retirados do livro “Padrões de Projeto: Soluções Reutilizáveis de Software Orientado a Objetos”, visto que eles publicaram em detalhes esses padrões e muitos artigos estão disponíveis com exemplos práticos em várias linguagens de programação.
Os pradrões se dividem em 3 grupos, listados a seguir.
Padrões de Criação
Abstract Factory
Fornece uma interface para criação de famílias de objetos relacionados ou dependentes sem especificar suas classes concretas.
Builder
Separa a construção de um objeto complexo da sua representação, de modo que o mesmo processo de construção possa criar diferentes representações.
Factory Method
Define uma interface para criar um objeto, mas deixa as subclasses decidirem qual a classe a ser instanciada. O factory method permite a uma classe postergar / atrasar a instanciação às suas subclasses.
Prototype
Especifica os tipos de objetos a serem criados usando uma instância de protótipo e cria novos objetos copiando esse protótipo.
Singleton
Garante que uma classe tenha somente uma instância e fornecer um ponto global de acesso a ela.
Padrões Estruturias
Adapter
Converte a interface de uma classe em outra interface esperada pelos clientes. O adapter permite que certas classes trabalhem em conjunto, pois de outra forma seria impossível por causa de suas interfaces incompatíveis.
Bridge
Separa uma abstração de sua implementação, de modo que as duas possam variar independentemente.
Composite
Compõe objetos em estruturas de árvore para representar hierarquias do tipo partes-todo. O
composite permite que clientes tratem objetos individuais e composições de objetos de
forma uniforme.
Decorator
Atribui responsabilidades adicionais a um objeto dinamicamente. Os decorators fornecem uma alternativa flexível a subclasses para extensão da funcionalidade.
Façade
Fornece uma interface unificada para um conjunto de interfaces em um subsistema. O
Flyweight
Usa compartilhamento para suportar grandes quantidades de objetos, de granularidade fina, de maneira eficiente.
Proxy
Fornece um objeto representante (surrogate), ou marcado de outro objeto, para controlar o acesso ao mesmo.
Padrões comportamentais
Chain of responsability
Evita o acoplamento do remetente de uma solicitação ao seu destinatário, dando a mais de um objeto a oportunidade de tratar a solicitação. Encadeia os objetos receptores e passa a solicitação ao longo da cadeia até que um objeto a trate.
Command
Encapsula uma solicitação como um objeto, desta forma permitindo que você parametrize clientes com diferentes solicitações, enfileire ou registre (log) solicitações e suporte operações que podem ser desfeitas.
Interpreter
Dada uma linguagem, define uma representação para sua gramática, juntamente com um interpretador que usa a representação para interpretar sentenças nesta linguagem.
Iterator
Fornece uma maneira de acessar sequencialmente os elementos de um objeto agregado sem expor sua representação subjacente.
Mediator
Define um objeto que encapsula como um conjunto de objetos interage. O mediator promove o acoplamento fraco ao evitar que os objetos se refiram explicitamente uns aos outros, permitindo que você varie suas interações independentemente.
Memento
Sem violar a encapsulação, captura e externaliza um estado interno de um objeto, de modo que o mesmo possa posteriormente ser restaurado para este estado.
Observer
Definie uma dependência um-para-muitos entre objetos, de modo que, quando um objeto muda de estados, todos os seus dependentes são automaticamente notificados e atualizados.
State
Permite que um objeto altere seu comportamento quando seu estado interno muda. O objeto parecerá ter muda sua classe.
Strategy
Define uma família de algoritmos, encapsular cada um deles e fazê-los intercambiáveis. O
Template Method
Define o esqueleto de um algoritmo em uma operação, postergando a definição de alguns passos para subclasses. O template method permite que as subclasses redefinam certos passos de um algoritmos sem mudar sua estrutura.
Visitor
Representa uma operação a ser executada sobre os elementos da estrutura de um objeto. O
visitor permite que você defina uma nova operação sem mudar as classes dos elementos