"Olá, amigo!"

"Oi, Bilaabo!"

"Nosso tópico hoje não será apenas interessante - será totalmente épico."

"Hoje vou falar para vocês o que são padrões de projeto. "

"Legal! Já ouvi falar muito deles. Mal posso esperar!"

"Programadores experientes precisam escrever muitas classes. Mas a parte mais difícil desse trabalho é decidir quais classes criar e como dividir o trabalho entre elas."

"Quanto mais eles resolviam essas questões, mais eles percebiam que algumas soluções são boas, enquanto outras são ruins."

"Más soluções geralmente criam mais problemas do que resolvem. Elas se estendem mal, criam muitas limitações desnecessárias, etc. E boas soluções são o oposto."

"Existe alguma analogia que você pode fazer?"

"Digamos que você esteja construindo uma casa. E pensando no que ela será feita. Você decide que precisa de paredes, piso e teto. Como resultado, você constrói uma casa com telhado plano e sem fundação. Essa casa vai rachar e o telhado vai vazar. Esta é uma solução ruim."

"Por outro lado, uma casa consistindo de uma fundação, paredes e um telhado de duas águas seria uma boa solução. Uma forte nevasca não representa problema, pois a neve escorregará do telhado. E solos movediços não devem ser temidos, porque a fundação garantirá estabilidade. Diríamos que essa solução é boa."

"Entendo. Obrigado."

"OK. Então vou continuar."

"Com o tempo, as boas soluções passaram a ser conhecidas como padrões de projeto, enquanto as ruins eram chamadas de antipadrões."

"Um padrão de projeto é como uma resposta a uma pergunta. É difícil entender se você nunca ouviu a pergunta."

" A primeira categoria de padrões são os padrões de criação. Esses padrões descrevem boas soluções relacionadas à criação de objetos."

"O que há de tão complicado em criar objetos?"

"Acontece que é exatamente isso que vamos explorar agora."

Padrão singleton.

Padrões de design: singleton, fábrica, método de fábrica, fábrica abstrata - 1

"Muitas vezes, em programas, pode existir apenas uma única cópia de alguns objetos. Por exemplo, o console, logger, coletor de lixo, etc."

" Solução ruim: não crie nenhum objeto — apenas crie uma classe cujos métodos sejam todos estáticos."

" Boa solução:  criar um único objeto e armazená-lo em uma variável estática. Impedir a criação de um segundo objeto da classe. Por exemplo:"

Exemplo
class Sun
{
 private static Sun instance;
 public static Sun getInstance()
 {
  if (instance == null)
  instance = new Sun();

  return instance;
 }

 private Sun()
 {
 }
}
Como é chamado
Sun sun = Sun.getInstance();

"É simples."

"Primeiro, tornamos o construtor privado. Agora ele só pode ser chamado de dentro de nossa classe. Bloqueamos a criação de objetos Sun em todos os lugares, exceto nos métodos da classe Sun."

"Segundo, um objeto desta classe só pode ser obtido chamando o método getInstance(). Este não é apenas o único método que pode criar um objeto Sun, mas também garante que haja apenas um objeto desse tipo."

"Eu vejo."

"Quando você pensa: 'Agora, como exatamente eu faria isso?', um padrão diz: 'você pode tentar isso - esta é uma das melhores soluções.'"

"Obrigado. Agora as coisas estão começando a ficar claras."

"Você também pode ler sobre esse padrão  aqui ."

Padrão de fábrica.

Padrões de design: singleton, fábrica, método de fábrica, fábrica abstrata - 2

"Aqui está uma situação que os programadores enfrentam com muita frequência. Você tem alguma classe base e muitas subclasses. Por exemplo, uma classe de personagem de jogo (GamePerson) e classes para todos os outros personagens que a herdam."

"Digamos que você tenha as seguintes classes:"

Exemplo
abstract class GamePerson
{
}

class Warrior extends GamePerson
{
}

class Mag extends GamePerson
{
}

class Troll extends GamePerson
{
}

class Elf extends GamePerson
{
}

"A questão é como podemos gerenciar de forma flexível e conveniente a criação de objetos dessas classes."

"Se esse problema parece absurdo para você, imagine que no jogo você precise criar dezenas de espadas e escudos, centenas de feitiços mágicos e milhares de monstros. Você não pode prescindir de uma abordagem conveniente para a criação de objetos aqui. "

"O padrão Factory oferece uma boa solução."

"Primeiro, precisamos criar uma enumeração cujos valores correspondam às várias classes."

"Em segundo lugar, crie uma classe Factory especial que tenha métodos estáticos que criem objetos com base em um valor enum."

"Por exemplo:"

Exemplo
public enum PersonType
{
 UNKNOWN,
 WARRIOR,
 MAG,
 TROLL,
 ELF,
}

public class PersonFactory
{
 public static GamePerson createPerson(PersonType personType)
 {
  switch(personType)
  {
   WARRIOR:
   return new Warrior();
   MAG:
   return new Mag();
   TROLL:
   return new Troll();
   ELF:
   return new Elf();
   default:
   throw new GameException();
  }
 }
}
Como é chamado
GamePerson person = PersonFactory.createPerson(PersonType.MAG);

"Em outras palavras, criamos uma classe especial para gerenciar a criação de objetos?"

"Sim."

"Então, quais vantagens isso oferece?"

"Primeiro, nesta classe, os objetos podem ser inicializados com os dados necessários."

"Em segundo lugar, você pode passar o valor de enumeração necessário entre os métodos o quanto quiser para criar o objeto desejado."

"Terceiro, o número de campos de enumeração não precisa corresponder ao número de classes. Pode haver muitos tipos de caracteres, mas poucas classes."

"Por exemplo, um Mag & Warrior pode usar uma classe (Human), mas com força e propriedades mágicas diferentes (argumentos do construtor)."

"Aqui está o que pode parecer (para maior clareza, também adicionei elfos negros):"

Exemplo
public enum PersonType
{
 UNKNOWN,
 WARRIOR,
 MAG,
 TROLL,
 ELF,
 DARK_ELF
}

public class PersonFactory
{
 public static GamePerson createPerson(PersonType personType)
 {
  switch(personType)
  {
   WARRIOR:
   return new Human(10, 0); // Strength, magic
   MAG:
   return new Human(0, 10); // Strength, magic
   TROLL:
   OGR:
   return new Troll();
   ELF:
   return new Elf(true); // true – good, false – evil
   DARK_ELF:
   return new Elf(false); // true – good, false – evil
   default:
   throw new GameException();
  }
 }
}
Como é chamado
GamePerson person = PersonFactory.createPerson(PersonType.MAG);

"No exemplo acima, usamos apenas três classes para criar seis tipos diferentes de objetos. Isso é muito conveniente. Além disso, temos toda essa lógica concentrada em uma classe e um método."

"Agora suponha que decidamos criar uma classe separada para Ogre - simplesmente mudamos algumas linhas de código aqui, e não metade do aplicativo."

"Eu concordo. Essa é uma boa solução."

"E é disso que estou falando: padrões de projeto são coleções de boas soluções."

"Eu também gostaria de saber onde usá-los..."

"Sim. Concordo, você não vai entender de imediato. Ainda assim, é melhor saber e não poder fazer do que não saber e não poder fazer. Aqui está outro link útil para você sobre este padrão: Padrão de fábrica "

"Ah, obrigado."

" Padrão abstrato de fábrica ."

"Às vezes, quando você tem muitos objetos, surge a ideia de criar uma fábrica para fábricas. Essa fábrica é chamada de fábrica abstrata ."

"Onde isso seria necessário?!"

"Suponha que você tenha vários grupos de objetos idênticos. Isso é mais fácil de mostrar com um exemplo."

"Agora, digamos que você tenha três raças em seu jogo: humanos, elfos e demônios. E para equilibrar, cada raça tem guerreiros, arqueiros e magos. Um jogador só pode criar objetos pertencentes à raça pela qual está jogando. no jogo. Veja como ficaria no código:"

Declaração de classes de soldados
class Warrior
{
}
class Archer
{
}
class Mag
{
}
Humanos
class HumanWarrior extends Warrior
{
}

class HumanArcher extends Archer
{
}

class HumanMag extends Mag
{
}
elfos
class ElfWarrior extends Warrior
{
}

class ElfArcher extends Archer
{
}

class ElfMag extends Mag
{
}
demônios
class DaemonWarrior extends Warrior
{
}

class DaemonArcher extends Archer
{
}

class DaemonMag extends Mag
{
}

E agora vamos criar as raças, ou também podemos chamá-las de exércitos.

exércitos
abstract class Army
{
 public Warrior createWarrior();
 public Archer createArcher();
 public Mag createMag();
}
exército humano
class HumanArmy extends Army
{
 public Warrior createWarrior()
 {
  return new HumanWarrior();
 }
 public Archer createArcher()
 {
  return new HumanArcher();
 }
 public Mag createMag()
 {
  return new HumanMag();
 }
}
exército de elfos
class ElfArmy extends Army
{
 public Warrior createWarrior()
 {
  return new ElfWarrior();
 }
 public Archer createArcher()
 {
  return new ElfArcher();
 }
 public Mag createMag()
 {
  return new ElfMag();
 }
}
exército demoníaco
class DaemonArmy extends Army
{
 public Warrior createWarrior()
 {
  return new DaemonWarrior();
 }
 public Archer createArcher()
 {
  return new DaemonArcher();
 }
 public Mag createMag()
 {
  return new DaemonMag();
 }
}

"Mas como você usa isso?"

"Você pode usar as classes Exército, Guerreiro, Arqueiro e Mago em qualquer lugar do programa e criar os objetos necessários - basta passar um objeto da subclasse Exército desejada."

"Por exemplo:"

Exemplo
Army humans = new HumanArmy();
Army daemons = new DaemonArmy();

Army winner = FightSimulator.simulate(humans, daemons);

"No exemplo acima, temos uma classe que simula batalhas entre diferentes raças (exércitos). Basta passar dois objetos Exército. A própria classe os utiliza para criar diversas tropas e conduzir batalhas virtuais entre elas a fim de identificar o vencedor ."

"Entendo. Obrigado. Uma abordagem realmente interessante."

"Uma boa solução, independentemente do que você diz."

"Sim."

"Aqui está outro bom link sobre o tema:  Abstract factory pattern "