Princípios de Design Patterns em C# - Abstract Factory
Olá a todos! Já passou um tempinho quando escrevi o meu último post sobre TDD e nesse artigo eu prometi dar continuidade sobre o tema em questão. E como alguns aqui já sabem no momento me encontro desempregada (sim... estou desempregada...Mas, caso saiba de alguma vaga como Desenvolvedora .NET Pleno, não hesitem de entrar em contato comigo!!! ;) Porém, eu resolvi escrever tópicos de temas um pouco mais avançados para fins de estudo pessoal. E senti uma necessidade especial em escrever uma série de artigos sobre Padrões de Projetos. (a linguagem que será abordada nessa série de artigos será o C#) Mas, eu prometo que, assim que terminar de escrever sobre a série de Design Patterns em C# darei a continuidade sobre a série sobre TDD. ;)
Antes de mais nada... vem a pergunta em questão.... O que é Design Patterns? Por que é importante? Como e quando utilizá-lo? Essa nova série de artigos trataremos os principais Padrões de Projetos GoF (Gang of Four), juntamente com as explicações teóricas de cada padrão e seus respectivos exemplos em código. Então... vamos nessa?!! :D
O que são Design Patterns??? :S
https://code4coders.files.wordpress.com/2016/04/design_1.jpg
Antes de mais nada temos que entender o que seria o Design Pattern (Padrões de Projeto).
Então vamos buscar o conceito descrito pelo próprio Wikipedia:
"Em engenharia de software, um padrão de projeto (do inglês design pattern) é uma solução geral reutilizável para um problema que ocorre com frequência dentro de um determinado contexto no projeto de software. Um padrão de projeto não é um projeto finalizado que pode ser diretamente transformado em código fonte ou de máquina, ele é uma descrição ou modelo (template) de como resolver um problema que pode ser usado em muitas situações diferentes. Padrões são melhores práticas formalizadas que o programador pode usar para resolver problemas comuns quando projetar uma aplicação ou sistema. Padrões de projeto orientados a objeto normalmente mostram relacionamentos e interações entre classes ou objetos, sem especificar as classes ou objetos da aplicação final que estão envolvidas. Padrões que implicam orientação a objetos ou estado mutável mais geral, não são tão aplicáveis em linguagens de programação funcional."
Bom.... como podem observar na descrição acima, resumindo o que seria o design pattern, seria uma descrição de um determinado template em como um determinado desenvolvedor poderá resolver um problema que pode ser usado em muitas situações diferentes. Notam que, ao desenvolver um determinado tipo de Padrão de Projeto, se faz necessário o conhecimento muito forte de Orientação a Objetos. Agora uma dica muito importante para aqueles que desejam acompanhar essa série de artigos sobre Design Patterns:
Peço que, para aqueles que desejam dar continuidade em seguir essa série de artigos, façam um estudo cabal sobre OOP, e estudar os seguintes tópicos:* Objeto, Classe, Abstração, Encapsulamento, Herança/Herança Múltipla, Associação, Polimorfismo e Interface *com o intuito para obter o melhor aproveitamento dessa série de artigos que escreverei aqui!!! ;) Se possível antes de acompanhar essa série peço que estudem antes OOP Estarei enviando aqui abaixo alguns sites para realizarem estudos sobre OOP:
- Apostila de POO (Curso - k19)
- Video Aula (Cleyton Ferrari) sobre POO (super recomendado!!!)
Então por favor.... se você ainda não tem o conceito de OOP..... Não perca tempo e Estude........
https://code4coders.files.wordpress.com/2016/04/design_2.jpg
Agora se você tem um conceito muito forte sobre OOP, pode dar continuidade a leitura!! :D Okay Glaucia!!! Já entendi o que é Design Pattern... Mas... uma pergunta surge em nossa cabeça...
Por que é importante usar Design Patterns?
Quando você desenvolve um determinado projeto e vê que há uma necessidade muito grande fazer reutilização de código, você por consequência verá que haverá uma necessidade muito grande em utilizar algum padrão de projeto. Até com a objetivo de deixar o código mais elegível e mais fácil de ser manipulado para outros desenvolvedores que irão dar manutenção no projeto no futuro. Outro fator muito importante ao fazer uso de Design Patterns é: manter e estabelecer um vocabulário comum de desenho, visando facilitar a comunicação, documentação e o aprendizado do sistemas de software.
Os modelos de Design Patterns ficou muito conhecido depois do lançamento do livro: Design Patterns: Elements of Reusable Object-Oriented Software, publicado pelo Erich Gamma, Richard Helm, Ralph, Johnson, John Vlissides. Esse livro é considerado a Bíblia sobre Design Patterns (o livro contém mais de 400 páginas explicando detalhadamente sobre o assunto) para quem deseja entender profundamente os 23 conceitos de GoF (comentarei a respeito sobre eles abaixo), convido-os a fazerem leitura desse livro!!!.
Existe uma certa.....
https://code4coders.files.wordpress.com/2016/04/design_3.jpg
Porém, quando você entende cada Pattern, sabe em que momento deve utilizar e qual usá-lo observa que é primordial desenvolver um determinado sistema seguindo algum Padrão de Projetos. Por consequência você começará a desenvolver....
https://code4coders.files.wordpress.com/2016/04/design_4.jpg
Sem contar que você se torna um desenvolvedor mais completo e.....também...
https://code4coders.files.wordpress.com/2016/04/design_5.jpg?w=680
GoF - o.O
GoF... uma sigla que eu tenho falado bastante aqui nesse poste e não tenho explicado o que é.... mas o que seria? Não. Não pensem que é um nome de uma boy band. :D
GoF é acrônimo de Gang of Four. Ou traduzindo para português: Gangue dos Quatro. Mas, porquê do nome????
Lembram do livro que citei acima? Design Patterns: Elements of Reusable Object-Oriented Software. Como foram 4 escritores e profissionais que escreveram o livro considerado a Bíblia sobre Design Patterns, foram eles que deram o nome de GoF, ou seja em homenagem a eles mesmos!! Interessante não? :D
https://code4coders.files.wordpress.com/2016/04/design_6.jpg
Bom... já explicado o porquê do nome GoF.... Agora vamos comentar sobre os 23 padrões de projetos, a qual é organizado em 3 grupos. São eles:
- Padrões de Criação
- Padrões Estruturais
- Padrões Comportamentais
Vamos explicar sobre todos eles aqui!!
- Padrões de Criação
Os Padrões de Criação são relacionados à criação de objetos. São eles:
- Abstract Factory
- Builder
- Factory Method
- Prototype
- Singleton
2. Padrões Estruturais:
Os Padrões Estruturais tratam justamente das associações entre classes e objetos. São eles:
- Adapter
- Bridge
- Composite
- Decorator
- Facade
- Flyweight
- Proxy
3) Padrões Comportamentais:
Os Padrões Comportamentais já tratam das interações e divisões de responsabilidades entre as classes ou objetos. São eles:
- Chain of Responsability
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor
Há mais Padrões de Projetos para serem falados aqui, como por exemplo: SOLID, MVC, MVVM entre outros... mas isso vou deixar que a série de artigos que escreverei aqui por si só vai explicando cada um deles. ok? Bom... passando a parte sobre o que é Design Pattern, a sua importância e quais a serem usados.. vamos hoje em especial falar sobre Abstract Factory! Mas.... por favor.. não surtem, okay??!! :D
https://code4coders.files.wordpress.com/2016/04/design_8.gif
Abstract Factory..... WHAT?!!!
Algo a ser explicado antes.... sempre quando eu for explicar um determinado Padrão de Projeto, antes explicarei a parte teórica do mesmo e logo após estarei escrevendo o código com o intuito de fixar a compreensão de cada Pattern. Estarei disponibilizando os códigos de exemplos através do meu repositório do GitHub, junto ao código do no repositório do GitHub estarei disponibilizando também a explicação de maneira sucinta no arquivo Readme.md, okay?
Bom...o que seria o Abstract Factory? A intenção do Design Pattern Abstract Factory é fornecer uma interface para criar famílias de objetos relacionados ou dependentes sem especificar suas classes concretas. Esse modelo/examplo abaixo é muito conhecido e descreve muito bem o fluxo do Abstract Factory: https://code4coders.files.wordpress.com/2016/04/abstract.gif
Vamos entender esse fluxograma!!!
Nesse padrão, a fábrica (conhecida como Factory) recebe solicitações por objetos concretos a partir de um cliente. Este padrão pode ser utilizado quando um sistema de software precisa ser independente de como classes concretas (produtos) são criadas, compostas ou representadas. No padrão, uma fábrica fica responsável por encapsular a criação de uma família de produtos. Nesse caso, um cliente precisa conhecer somente as interfaces desses produtos, não a sua implementação, aumentando o encapsulamento e abstração.
Mas, quais são os benefícios em fazer uso do Padrão Abstract Factory? Simples. Quando se desenvolve um aplicação deste padrão traz como benefício principal o isolamento de classes concretas. Se for necessário trocar uma família inteira de produtos, esse processo se torna menos impactante nas demais partes do sistema, pois as classes-produto ficam isoladas e não expõem sua implementação a classes clientes, diminuindo o acoplamento.
Os participantes desse padrão são:
- * Abstract Factory:* declara uma interface para operações que criam objetos-produtos abstratos.
- Concrete Factory: implementa as operações que criam objetos-produtos concretos.
- Abstract Product: declara uma interface para um tipo de objeto-produto.
- Concrete Product: define um objeto-produto a ser criado pela correspondente fábrica concreta. Implementa a interface de Abstract Product.
- Client: usa somente as interfaces declaradas pelas classes Abstract Factory e Abstract Product.
Dos 23 de GoF é um dos Padrões de Projetos mais usados durante o desenvolvimento de um projeto.
Depois de ler a parte teórica você talvez ou com certeza deve estar pensando agora....Man....
https://code4coders.files.wordpress.com/2016/04/design_9.jpg
Calma... para quem nunca ouviu falar sobre Padrões de Projeto de início é complicado mesmo. Mas, vamos ao código para que seja de fácil entendimento para todos aqui! ;)
Demo em código Ufa!!!! :D
Bom.... vamos fixar a parte teórica desenvolvendo uma aplicação sobre o Abstract Factory. Abram o Visual Studio (2012/2013/2015) de sua preferência e crie um novo projeto Console chamado: AbstractFactory. Vou pedir para que criem agora algumas classes.
# 1 Passo: criar a classe: DbConnection.cs
namespace AbstractFactory
{
/* Abstract Product */
public abstract class DbConnection
{
public abstract void Open();
}
}
#2 Passo: criar a classe: SqlConnection.cs. Essa classe irá herdar da classe DbConnection.cs
namespace AbstractFactory
{
public class SqlConnection : DbConnection
{
/* Concret Product */
public override void Open()
{
Console.WriteLine("Método Open de SQL Connection foi chamado...");
}
}
}
#3 Passo: criar a classe: DbCommand.cs
namespace AbstractFactory
{
public abstract class DbCommand
{
public abstract void Execute();
}
}
#4 Passo: criar a classe: SqlCommand.cs. Assim como o passo 2 ela irá herdar da classe DbCommand.cs
namespace AbstractFactory
{
public class SqlCommand : DbCommand
{
public override void Execute()
{
Console.WriteLine("Executando o comando do sql.....";);
}
}
}
Bom... agora com todas as classes criadas, vai até o Program.cs e escreva o seguinte código e executem:
namespace AbstractFactory
{
class Program
{
static void Main(string[] args)
{
var con = new SqlConnection();
con.Open();
var cmd = new SqlCommand();
cmd.Execute();
Console.ReadLine();
}
}
}
Bom... o código irá executar sem nenhum problema. Mas, há um problema aqui... notem no código acima o código está intrinsecamente acoplada. Ou seja, se num futuro próximo eu precisar desacoplar esse código e fazer uma abordagem diferente de alguma determinada base de dados diferente do que eu programei acima, vou ter que mudar todas as classes envolvidas e desenvolvidas, ocasionando um grande problema para o futuro desenvolvedor.
Mas, como podemos resolver isso de uma maneira elegante e que seja produtivo para todos?
Peço que crie uma nova classe chamada: **DbFactory.cs. **Essa classe será do tipo 'abstract' e nessa classe vamos criar métodos classes com produtos concretos mas com abstração. Como segue o código abaixo:
namespace DbFactory
{
public abstract class AbstractFactory
{
public abstract DbConnection CreateConnection();
public abstract DbCommand CreateCommand();
}
}
Bom... agora vamos criar a nossa Concret Factory (Fábrica Concreta). Crie uma nova classe chamada: **SqlFactory.cs. **E segue o código abaixo:
namespace AbstractFactory
{
public class SqlFactory : DbFactory
{
public override DbConnection createConnection()
{
/* A classe aqui ficou encapsulada */
return new SqlConnection();
}
public override DbCommand createCommand()
{
return new SqlCommand();
}
}
}
Notem no código acima que a classe SqlFactory ela está herdando de DbFactory. Ou seja existe aqui uma abstração do código. O que facilita, por exemplo, caso eu queira incluir uma outra conexão de dados além do SQL Server, poderei criar uma nova Factory e herdar da classe Abstrata. Vamos observar no Diagrama de Classes como ficou o nosso código para que possamos entender melhor.
https://code4coders.files.wordpress.com/2016/04/screen-shot-04-29-16-at-01-14-am.png
Notem que agora que a nossa classe Client se comunica com a Abstract Class (DbFactory.cs) com o inuito de poder receber outros produtos concretos que podem ser trocados.
Agora vamos retornar para o nosso Program.cs e realizar algumas mudanças significativas.
using System;
namespace AbstractFactory
{
class Program
{
/* Client */
static void Main(string[] args)
{
/* Aqui vamos estanciar a nossa Factory */
DbFactory db = new SqlFactory();
var con = db.CreateConnection();
con.Open();
var cmd = db.CreateCommand();
cmd.Execute();
Console.ReadLine();
}
}
}
Notem que todas as classes são dependentes da Factory (Fábrica). Isso nos permite realizar uma inclusão ou até mesmo trocar, sem afetar o programa principal, de uma nova conexão. Algo que faremos logo a seguir. Tanto que ao executar o programa em questão, ele apresentará a mesma saída, ou seja, continua realizando o polimorfismo do método implementado.
Agora vamos imaginar o seguinte cenário: nesse projeto preciso implementar uma outra conexão de dados diferente da já existe. (MongoDb). E agora?! O que fazer?! Calma... que temos uma solução para isso! Vejamos como... :)
Crie uma nova classe chamada: MongoConnection.cs
using System;
namespace AbstractFactory
{
public class MongoConnection : DbConnection
{
public override void Open()
{
Console.WriteLine("Método Open de Mongo Connection foi chamado...");
}
}
}
Notem que ao implementar o MongoConnection.cs devemos herdar a classe **DbConnection.cs. **E observem que há uma lógica e uma elegibilidade no código. Deixando mais entendível e muito melhor para realizar a manutenção.
Agora crie uma outra classe chamada: MongoCommand.cs e faça o mesmo que o código acima.
[sourcecode language="csharp"]
using System;
namespace AbstractFactory
{
public class MongoCommand : DbCommand
{
public override void Execute()
{
Console.WriteLine("Executando o comando do sql do Mongo.....");
}
}
}
[/sourcecode]
Vamos criar a Factory dessa família. Crie uma nova classe chamada: MongoFactory.cs, conforme o código abaixo:
namespace AbstractFactory
{
public class MongoFactory : DbFactory
{
public override DbConnection CreateConnection()
{
return new SqlConnection();
}
public override DbCommand CreateCommand()
{
return new SqlCommand();
}
}
}
Agora vamos ver toda mudança no Program.cs e ver o resultado final de tudo isso! **:D **
using System;
namespace AbstractFactory
{
class Program
{
/* Client */
static void Main(string[] args)
{
/* Aqui vamos estanciar a nossa Factory */
//DbFactory db = new SqlFactory();
DbFactory db = new MongoFactory();
var con = db.CreateConnection();
con.Open();
var cmd = db.CreateCommand();
cmd.Execute();
Console.ReadLine();
}
}
}
Execute o código e vòilá! :D
Quando devemos usar o Abstract Factory?? :D
Devemos usar o Abstract Factory quando queremos criar um conjunto de objetos que estão relacionados ou que estão dependentes e necessitam ser usados de maneira conjunta. O sistema deve ser configurado para trabalhar com múltiplas famílias de produtos. A criação do objeto devem ser independentes do sistema. E uma última nota: as classes concretas devem estar desacopladas dos clientes.
Peço que além do artigo aqui escrito sobre Abstract Factory. Por se tratar de um tema bastante complexo, procurem fazer pesquisas mais profundas sobre o assunto e desenvolver outros projetos com ajuda de tutoriais de outros sites para poder deixar o assunto muito bem explicado na sua cabeça. Não se permite passar por um outro assunto, sem entender o atual. #ficaadica!!
Bom.... hoje é isso... acho que ficou até grande demais esse post! Estou enviando todo o código da demo através do meu repositório do GitHub (clique aqui). Como no momento estou desempregada e aproveitando o tempo para realizar estudos mais avançados sobre alguns temas, pode ser que eu consiga terminar essa série em 1 mês. Mas, caso eu venha a conseguir algum trabalho, farei o possível para poder entregar tudo em menos tempo! Até porque é um tema que me interessa e escrever posts me ajuda a disseminar o conhecimento daquilo que estou estudando não só para mim, mas para outras pessoas também! :D
O próximo artigo iremos abordar um outro Padrão de Projeto: **Builder. **Mas... por hoje....
https://code4coders.files.wordpress.com/2016/04/design_11.jpg
Bom... até a próxima pessoal!! :D