Wagner Abrantes
Wagner Abrantes12/12/2020 14:17
Compartilhe

Shin Programming Tensei (POO - Abstração)

  • #Ruby

image

POO e como a Abstração pode te tornar mais criativo

A Orientação a Objetos tem quatro conhecidos pilares:

Abstração

Herança

Polimorfismo

Encapsulamento

O que eu acho mais divertido é com certeza a Abstração. Desde criança eu era bom em desenhar, escrever, inventar histórias e etc. Então eu me considero um sujeito criativo.

Pensar que em programação você pode simplesmente usar a imaginação para representar coisas do mundo real é para mim a coisa que mais me agrada e que faz do paradigma POO aquilo que vai levar não apenas eu, mas vários outros programadores ao próximo nível em seus estudos, projetos e trabalho.

Existe um livro chamado “Megami Tensei: A Digital Devil Story”

image

A história fala sobre um estudante arrogante chamado Akemi Nakajima que é apaixonado por programação e ocultismo.

Uma nova aluna chamada Kyoko chega em sua classe e logo se apaixona por Nakajima, que só quer saber de programar e a rejeita. Kyoko que se sente humilhada combina com delinquentes conhecidos da escola para dar uma surra em Nakajima que após apanhar na frente de toda a classe decide se vingar. Ele então cria uma mistura de ritual ocultista com um programa que ele mesmo escreveu para trazer a sua realidade um demônio chamado Loki, que logo após matar os delinquentes e Kyoko exige a Nakajima mais sacrifícios.

Essa história deu início de uma saga de jogos chamada “Shin Megami Tensei” que é um jogo de RPG onde a mecânica lembra muito a de Pokémon (inclusive alguns fãs da série o apelidaram carinhosamente de Pokémon do capiroto hahah) onde você pode não apenas batalhar contra os inimigos como também coletá-los.

Não se engane, apesar da franquia de jogos da Nintendo criada em 1996 ser muito mais famosa, essa mecânica foi criada por Shin Megami Tensei muito antes em 1987.

image

Talvez você conheça os Spin-offs da Saga que ultimamente anda bem mais famosa entre jogadores mais casuais, como Persona.

image

Esse é um dos meu jogos favoritos, pois eu gosto muito de jogos com mecânicas complexas, liberdade de gameplay e etc. Então ao invés de lutar com um grupo pré determinado pela trama como em Final Fantasy eu posso convidar os inimigos do jogo por meio de conversas que rolam no meio da batalha.

E ter uma história sobre um programador que cria um script que invoca demônios de outros mundos é bem maluca e eu gosto de coisas malucas.

E ainda por cima tem esse mascote fofinho que é o Jack Frost que também é mascote da desenvolvedora do jogo a ATLUS, bastante conhecida pelos jogos da série Dark Souls.

image

Mas o que isso tem a ver com POO e abstração?

A Idéia é utilizar a POO, para abstrair um código que represente um dos demônios do jogo.

Quem eu irei representar aqui é esse carinha:

image

Apesar do jogo ser sobre Demônios, a história envolve diversas entidades de várias culturas como Shiva, Thor, Samael e Baphomet.

Esse na Imagem é Mastema, ele é um anjo usado por Deus, para testar a fé dos humanos conhecido também como Anjo do Desastre. Diz-se que Mastema é o anjo que desencadeou as dez pragas do Egito e tentou matar Moisés.

A abstração requer que de certo objeto possamos descrever uma entidade, características e ações. Como você já deve ter visto por aí o exemplo do Cachorro.

Cachorro é a Entidade ele pode ter várias características, como cor, tamanho, peso, altura, raça e um nome. Pode também ter ações, como comer, latir, marcar território.

Cachorro pode ter mais característica que essas? Pode fazer mais coisa?

Claro que pode, só que a abstração que você deve fazer do seu objeto precisa ter as características e ações que você precisa naquele momento, tentar usar toda a abstração possível não vai te ajudar. Selecione aquilo que você acha essencial, se no final sentir que precisa de algo a mais, ai sim você bota a mão na massa de novo.

Não precisa ser perfeito. Precisa estar feito!

Na Imagem do Mastema que temos do jogo, temos várias coisinhas ali que são típicas de jogos de RPG. O próprio jogo em si já é uma abstração de algo, o que deixa as coisas ainda mais fáceis.

Então o Mastema tem um nome (Name), aquele Herald é a raça (Breed) dele. Temos uma bolinha roxa ali em cima também que representa o (Archetype) do Demon, isso serve para definir as Skill iniciais dele, existem Purple, Teal, Yellow, Red e Clear.

Cada Mastema com um Archetype diferente vem com um padrão de Skills diferente o que muda totalmente a sua atuação em um time.

Onde Vemos 83 é o (Grade), que é um nível entre os Personagens do jogo, Demons com um Grade alto tem um maior número de estrelas que são os desbloqueadores de niveis.

Ali a esquerda podemos ver o (Level) atual que é 50 e mais centralizado temos os (Stats) de Força, Magia, Vitalidade, Agilidade e Sorte.

Bem abaixo, temos sete (Elements) que indicam se esse Demon tem alguma fraqueza contra algum elemento específico. Físico, Fogo, Gelo, Trovão, Vento, Luz e Trevas.

Pronto, já temos aqui várias das características mais importantes dele.

Agora temos as Habilidades bem alí no meio. Porém se queremos imaginar o Demon como entidade não podemos atribuir essas mesmas Skills para todos os personagens, afinal cada um tem habilidades diferentes.

Uma coisa que todos eles têm em comum no jogo é atacar fisicamente (Attack) e passar (Pass) de turno. Então essas sim serão as ações, pois esse código será um molde para vários objetos e não apenas um.

Então temos uma entidade, características e ações.

image

Com essas informações bem esclarecidas podemos agora pensar em como seria um código que gera um Demon com essas características.

Os exemplos em código que farei serão em Ruby, porém são extremamente simples e podem ser feitos com qualquer linguagem orientada a objetos. Porém o que conta aqui é entender os conceitos da POO pois isso é o mais importante. Se você apenas levar em consideração o código sempre será refém de tutoriais e nunca conseguirá fazer algo por conta própria.

Então para iniciar esse código a gente sempre começa abrindo uma classe:

class Demon
end

Ela tem um início um final e um nome o Nome das classes em Ruby sempre são iniciados com letra maiúscula. Aqui eu dei o nome da classe como Demon, pois queremos que era crie diversos objetos e não apenas um, a questão toda aqui não é só representar o Mastema, mas sim a entidade que gera ele.

Sabemos também que todos os Demons tem 7 atributos diferentes

:name, :breed, :grade, :level, :archetype, :stats, :elements

Mais tarde, teremos que criar objetos através dessa entidade, e para fazer isso cada objeto terá que receber essas características de uma forma única, pois cada um deles tem um estado diferente. Pode ter menos ST, um Level menor e etc. E para isso iremos usar o nosso amigão: attr_accessor

Ele funciona como um facilitador para usar todos os atributos de uma classe sem precisar definir um método para cada atributo.

Sem isso attr_accessor :name

Você teria que fazer isso:

def name
  @name
end

def name=(value)
  @name = value
end

Agora imagina se você tivesse mais de 1 atributo, ou quem sabe 7? Usando o attr_accessor nosso código ficará assim*:*

class Demon
  attr_accessor :name, :breed, :grade, :level, :archetype, :stats, :elements
end

Para que possamos instanciar uma classe usamos o método Initialize que é um método construtor. (Instânciar é quando realmente criamos um objeto a partir da nossa entidade)

class Demon
  arttr_accessor :name, :breed, :grade, :level, :archetype, :stats, :elements

  def initialize(name, breed, grade, level, archetype, stats, elements)
      @name = name
      @breed = breed
      @grade = grade
      @level = level
      @archetype = archetype
      @stats = {}
      @elements = {}
  end
end

Certo o código já está com um tamanho maior, mas não se assuste.

Tudo que fizemos aqui foi atribuir os atributos que a gente definiu lá em cima realmente é muito simples.

Todos os atributos que definimos na nossa fase de abstração estão definidos com “@” seguido de seu nome. Isso são variáveis de instância, elas funcionam para armazenar valores, porém elas não podem ser usadas fora da sua classe.

Depois de Def Initialize temos todos os dados como argumentos para a criação do nosso objeto. Se você é atento já viu que no attr_accessor temos 7 atributos E no initialize temos 5 argumentos, a conta não bate.

Acontece que os Stats e Elements iremos definir seus valores fora da classe. Por isso definimos eles em attr-accessor e não em initialize.

Toda a parte de Entidade e atributos está praticamente pronta, agora temos as ações. As ações em POO serão métodos que esses objetos poderão realizar. Como definimos que cada Demon por padrão pode atacar fisicamente e passar de turno, serão somente dois métodos.

class  Demon

  attr_accessor  :name, :breed, :grade, :level, :archetype, :stats, :elements

  def  initialize
      @name  = name
      @breed  = breed
      @grade  = grade
      @level  = level
      @archetype  = archetype
      @stats  = {}
      @elements  = []
  end

  def  attack
      'Physical Attack'
  end

  def  pass_turn
      'Give action for the next demon'
  end
end

O escopo de como se escreve um método é como em uma classe, porém ao invés de class usamos a palavra reservada def, e o nome dos nosso métodos seguem um padrão de escrita conhecido como snake case, letras minúsculas e caso seja um nome composto usamos o underscore para uni-las.

No miolo do método temos a própria ação.

Nos dois casos eu defini apenas uma string que diz o que a ação faz, pois a ideia aqui não é escrever o jogo, não estamos usando nenhuma biblioteca para isso. Definir um metodo que envolve as mecânicas de um jogo realmente implicaria em fazer cálculos com base no HP, Na Skill usada, Stats e etc.

Eventualmente você poderia escrever algo assim para um jogo, ou para um database, mas se você não vê utilidade no seu código neste momento, não fique desesperado. Nem todo código que você escrever vai ser útil ainda mais caso você esteja iniciando em programação, e não tem nenhum problema nisso se você estiver se divertindo enquanto aprende já é suficiente.

Voltando ao assunto, todas as variáveis de instância dentro do método initialize recebem o mesmo valor usado como argumento, exceto duas delas. @stats e @elements que recebem duas Hashes vazias.

Hash é um método ruby para estruturas de dados indexadas onde temos um valor e uma chave para esse valor, o que faz todo sentido serem atribuídos para stats e elements, pois um valor de stats como ST sempre vem acompanhado de um valor como o do Mastema que tem 97.

Cria-se uma relação entre chave e valor

:St=>97

Assim como no caso dos elements que tem a relação de :Wind=>’Repel’

Antes de enfiar esses valores no hashe vamos definir esses valores como constantes. Constantes são definidas com a inicial em maiuscula e o restante em minuscula.

# constantes de stats
St = 'Strength'
Ma = 'Magic'
Vi = 'Vitality'
Ag = 'Agility'
Lu = 'Lucky'

#constantes de elementos
Physical = 'Physical'
Fire = 'Fire'
Ice = 'Ice'
Thunder = 'Thunder'
Wind = 'Wind'
Light = 'Light'
Dark = 'Dark'

Apesar de que no Ruby não se tem constantes de verdade, ele apenas gera um Warning caso algum programa mude um valor dessa variável.

Agora depois definir tudo isso, você pode instanciar seu objeto e criar um Mastema assim.

mastema  =  Demon.new('Mastema', 'Herald', '83', '50', 'Purple')

Temos uma variável que vai receber o objeto como valor.

Seguido do nome da classe e do método New, logo após definirmos os valores dos argumentos solicitados lá no initialize. Aqui eu usei o Mastema, mas poderia ser Shiva, Lúcifer ou qualquer outro Demon com as mesmas características.

Agora se você quiser instanciar seu objeto com os atributos você pode fazer o seguinte.

mastema.stats  = {
  ST: 97,
  MA: 221,
  VI: 165,
  AG: 133,
  LU: 182
}

mastema.elements  = {
  PHYSICAL: nill,
  FIRE: 'Weak',
  ICE: 'Null',
  THUNDER: nill,
  WIND: 'Rp',
  LIGHT: 'Rp',
  DARK: nill
}

Declaramos a variável do objeto mastema e usamos como método S*tats* ou E*lements, seguidos das chaves e valores dentro do hash. No caso nosso *Mastema** tem um S*tats* bem alto por estar no Level 50, mas você poderia definir aqui os valores de Base Stats, que são aqueles que o personagem tem, quando ele está no level 1 por exemplo.

Para verificar cada detalhe, você pode usar um puts e depois executar em terminal. Se eu quero verificar os E*lements* por exemplo, posso usar:

puts mastema.elements

image

puts mastema.name  
puts mastema.breed  
puts mastema.grade  
puts mastema.level  
puts mastema.archetype

image

puts mastema.stats

image

Acredito que isso não é nem 1% do que se pode aprender em POO mas com 1% já é possível fazer muita coisa.

Agora eu deixo o desafio para você, faça a abstração de algo que você goste, pode ser um jogo, personagem, regra de negócio você que decide, mas o importante é que você crie algo seu. Busque as coisas que você gosta e conhece e tente usá-las no seu estudo, assim o processo de aprendizado fica muito mais agradável.

Compartilhe
Comentários (5)
Leandro Carvalho
Leandro Carvalho - 15/12/2021 10:53

Incrível, parabens pelo ótimo artigo.

Lilit Costa
Lilit Costa - 29/12/2020 10:28

Muito bom

Felipe Aguiar
Felipe Aguiar - 28/12/2020 23:52

WOW, que artigo top!!!! eu veria um curso com esse material f'acil! vai colaborar muito prao caminho ninja de varios dev!

Dimas Pereira
Dimas Pereira - 27/11/2021 18:24

Show de bola o artigo! Usar esse anime como referência criativa foi muito bom... estou aproveitando e assistindo os OVA´s também, para inspirar. rs

Yvson Ferreira
Yvson Ferreira - 30/12/2020 07:42

Parabéns