Design Pattern - Factory Method
Tags: designpatterns, csharp
Published: 2024-04-26 14:32:33

Design Patterns, padrões de design ou até padrões de projeto, são um conjunto de soluções típicas para problemas comuns dentro do desenvolvimento de software. 

São algoritmos?! Não. Um algoritmo é uma descrição detalhada de passos para realizar uma determinada tarefa. Um design pattern é mais alto nível que isso, expõe comportamentos e orientações a nível organizacional do código.

É importante salientar que diferente de funções e bibliotecas que encontramos e rapidamente incorporamos ao nosso código, implementar um padrão é muito mais do que isso, sendo necessário uma análise minuciosa dos relacionamentos existentes e dos possíveis ganhos que cada padrão poderá nos fornecer. 

Há três grandes grupos dentro do estudo de padrões de projeto:

1. Padrões criacionais
2. Padrões estruturais
3. Padrões Comportamentais

Vamos focar no primeiro e esmiuçar alguns pontos.

## Padrões criacionais

A criação de objetos é intrínseco ao desenvolvimento de software, é normal que passemos diariamente por problemas que outros já passaram e assim podemos nos beneficiar do estudo de padrões, afim de não reinventarmos a roda sempre que nos depararmos com um novo problema. 

Poderemos então abrir nossa caixa de ferramentas e encontrar algo que nos ajude.

### Factory Method

> O **Factory Method** é um padrão criacional de projeto que fornece uma interface para criar objetos em uma superclasse, mas permite que as subclasses alterem o tipo de objetos que serão criados.

Talvez seja o padrão criacional mais simples de se entender, talvez você já até tenha usado esse padrão nos seus projetos e nem sabia que havia um nome para esta abordagem. 

A ideia aqui é criar uma *fábrica* de objetos, sempre que precisarmos de um objeto específico poderemos usar esta fábrica e obter um objeto válido sem maiores dificuldades. Mas por que? Como? Quando?

Vamos dar um passo atrás...

Vamos pensar em uma faculdade, no primeiro semestre há apenas o curso de Engenharia Civil.

```cs

public class CivilEngineeringStudent
{
	public string Name {get; set;}
	public string DateOfBirth {get; set;}
	public int Duration {get; set;}
	public List<string> Courses {get; set;}

	public (string name_, DateTime DateOfBirth_)
	{
		name = name_;
		DateOfBirth = DateOfBirth_;
		Duration = 5;
		Courses.Add("Calculus I");
		Courses.Add("Programming I");
		Courses.Add("algebra");
	}
}

```

Então quando queríamos criar seria só usar o `new` e pronto, teríamos um novo estudante. E estava tudo certo, até que em algum momento o CEO decidiu que agora teríamos também o curso de Biologia. E lá vamos nós construir um novo modelo de dados para vídeo 

```cs

public class BiologyStudent
{
	public string Name {get; set;}
	public string DateOfBirth {get; set;}
	public int Duration {get; set;}
	public List<string> Courses {get; set;}

	public (string name_, DateTime DateOfBirth_)
	{
		name = name_;
		DateOfBirth = DateOfBirth_;
		Duration = 4;
		Courses.Add("Marina Life I");
		Courses.Add("Algebra");
		Courses.Add("Calculus I");
	}
}

```

Mas pera aí, na hora de criar um novo estudante, vou precisar selecionar o tipo de post para então criar um objeto. Estou aumentando a complexidade... Há uma forma mais interessante de fazer isso, usando o **Factory Method**.

Vamos criar a interface *graduation*, e nossas classes *CivilEngineeringStudent* e *BiologyStudent*, irão herdar dela os seus dados.

```cs

public interface IGraduation 
{
	public string Name {get; set;}
	public string DateOfBirth {get; set;}
	public int Duration {get; set;}
	public List<string> Courses {get; set;}
	public void PrintStudent();
}

public class CivilEngineeringStudent : Graduation
{
	public (string name_, string DateOfBirth_)
	{
		name = name_;
		DateOfBirth = DateOfBirth_;
		Duration = 5;
		Courses.Add("Calculus I");
		Courses.Add("Programming I");
		Courses.Add("algebra");
	}

	public void PrintStudent()
	{
		 Console.WriteLine($"Student of Civil Engineering - {this.name}")
	}

}
public class BiologyStudent : Graduation
{
	public (string name_, string DateOfBirth_)
	{
		name = name_;
		DateOfBirth = DateOfBirth_;
		Duration = 4;
		Courses.Add("Marina Life I");
		Courses.Add("Algebra");
		Courses.Add("Calculus I");
	}

	public void PrintStudent()
	{
		 Console.WriteLine($"Student of Biology - {this.name}")
	}
}
```

Mas só isso ainda não resolve nosso problema original, continuaria sendo necessário fazer duas chamadas diferentes, selecionando manualmente qual objeto seria criado em qual momento.

Para isso criaremos também uma classe abstrata chamada de Creator, que irá criar os objetos de acordo a nossa necessidade.

```cs
public abstract Creator 
{
	public abstract IGraduation FactoryMethod();
}

public class ConcreteCreator: Creator
{
	public override IConteudo GetGraduation(string graduation, string name, string DateOfBirth)  
	{
		switch(graduation)
		{
			case "Biology"
				return new BiologyStudent(name, DateOfBirth);
			break;
			case "Civil Engineering"
				return new CivilEngineeringStudent(name, DateOfBirth);
			break;
		}
	}
}
```

Agora podemos criar estudantes para os dois cursos como poderemos ver no código que segue. Vamos criar dois estudantes, uma de Biologia outro de Engenharia Civil.

```cs

var factory = new ConcreteCreator();

var studenteOfBiology = factory.GetGraduation("Biology", "Judith", "01/01/2003");

var studenteOfCivilEngineering = factory.GetGraduation("Civil Engineering", "Kirk W.", "01/01/1958");

List<IGraduation> students = new();

students.Add(studenteOfBiology);
students.Add(studenteOfCivilEngineering);

foreach(var student in students)
	student.PrintStudent();


```

#### Prós e Contras

> Prós
> - Baixo acoplamento
> - Auxilia a implementar os conceitos de SOLID, em especial: 
> 	- Princípio de responsabilidade única 
> 	- Princípio aberto/fechado

 
> Contras
>- Aumento significativo da complexidade do código