Model-view–controller
NB
Este artigo WIP eventualmente se tornará um - ou uma série - de posts no blog sobre este padrão de desenvolvimento e derivados.
A idéia é motivar, analisar e desenvolver uma incarnação específica deste padrão para uso no desenvolvimento de nossos jogos.
Introdução
Descrição rápida e Motivação
Quotando a wikipedia em inglês: "Model-View-Controller (MVC) é um modelo de desenvolvimento de software que separa a representação da informação da interação do usuário com ela. o Model é os dados do aplicativo e as regras de interação, o Controller media o input, convertendo-o em comandos para o Model ou View, e o View pode ser qualquer tipo de output ou representação dos dados."
A idéia é que esta separação melhora a organização do programa em geral, impondo restrições que facilitam a compreensão do programa e sua generalização:
- O usuário vê o View, e não a maquinária interna do programa, o Model.
- O usuário interage com o programa através do Controller.
- (O usuário interage diretamente com o controller, ou seus inputs passam por ele? Veja abaixo).
- O Model não sabe qual View está representando ele, isso permite que vários Views possam representar o mesmo Model.
- O Model sendo um aglomerado puro de dados, regras e rotinas, várias interfaces podem ser usadas para manipulá-lo
- (Quem é a interface? o View ou o Controller? Novamente, veja abaixo)
Sendo assim, a interação do MVC é basicamente:
- o Controller controla o programa.
- o Model é o estado e a maquinária do programa.
- o View apresenta o Model de maneira legível para o usuário.
(TODO: colocar organigramas)
Aplicação em Software e em Games
Sendo assim, este padrão ajuda muito a guiar e manter escalável o desenvolvimento de quase qualquer tipo de software, o "usuário" podendo ser interpretado como qualquer entidade que usa o software. Particularmente, ele estende a idéia de desenvolvimento orientado a objetos: neste contexto ele pode ser usado como uma organização dos objetos, dividindo-os em três classes de tipos que interagem seguindo um padrão definido. Quando usado no escopo do padrão "componente" de objetos, ele ajuda a guiar a estrutura interna destes.
Para o desenvolvimento de jogos ambas estas aplicações são muito úteis: como jogos são programas grandes, fazê-los com orientação-objeto ajuda muito no processo de desenvolvimento, e eles também se beneficiam muito do padrão componente para as entidades do jogo, pois isto resolve de maneira muito simples os problemas de Data Loading e de Skill Targeting.
(TODO: exemplos)
Problemas de Definição
Agora, uma pequena pesquisa por Model View Controller no google imagens mostra vários organigramas sobre como o padrão funciona... mas eles não são nem um pouco parecidos!
(TODO: mostrar alguns claramente diferentes)
A questão é que, como apontado na lista acima, algumas definições não são precisas, e dependendo do tipo de software que você está desenvolvendo, uma ou outra solução podem parecer mais naturais. Algumas incarnações ou derivados do Model-view-controller inclusive se tornaram padrões por si só, como por exempo o Model-view-presenter, ou Model-view-adapter. Um dos pontos deste artigo é fixar uma definição mais útil e prática para o desenvolvimento de jogos.
De qualquer forma, vai ser útil fixar algumas restrições constantes do padrão:
Algumas constantes
As coisas listadas acima ainda valem. Adicionadas a elas, temos:
- O Model não pode depender do estado do View, para não quebrar a possibilidade de vários Views consistentemente apresentando o mesmo Model.
- O Model deve ser único, e sempre precisa existir (faz sentido ele ser uma espécie singleton, por exemplo).
- Suporte a interação interna (eventos ou callbacks) tem que existir. O View tem que ser independente dela.
- Todo tipo de input externo (input) ou interno (eventos ou callbacks) ao programa deve passar pelo Controller.
- nota: o Controller não precisa a priori distinguir entre inputs externos e internos.
- Todo tipo de output (externo) deve passar pelo View.
- Múltiplas interfaces, além de múltiplos Views, devem ser possíveis.
- O Controller apenas recebe, e não gera inputs.
Detalhes de definição
Análises
Interface
O usuário não tem escolha, ele tem que interagir com o programa através de uma - ou várias - interfaces externas. A questão é quem é esta interface. A definição do padrão MVC por si só não a define. Vamos analisar dois casos:
- Se você interpreta o View como uma janela de um programa por exemplo, faz sentido o View ser a interface. Ele então passa chamadas para o Controller, que organiza estes "inputs generalizados" em algo coeso, e altera o Model desta maneira.
- Um "input manager", por lidar com input, precisa existir e ser unificado por questões de organização. Inputs precisam passar por ele, logo ele precisa fazer parte ou do View (no caso do View ser a interface acima), ou do Controller. Ele não pode ser um View por vários motivos: ele precisa ser unificado, logo fazer uma cópia dele para cada View seria ruim, e ele não apresenta nada ao usuário, logo não satisfaz a definição de View. Portanto ele precisa fazer parte do Controller.
A primeira interpretação é muito mais direta e prática: o View é o que aparece para o usuário, logo o View é a interface. Como inputs pracisam passar pelo Controller, o View passa os inputs para o Controller. Isto também provê uma separação dos inputs do usuário de inputs internos.
O problema é que como o segundo caso exemplifica, algumas interfaces necessárias, como o Input Manager, não podem ser ou fazer parte de Views. Não parece haver uma solução boa para este dilema, internamente o View vai ter que passar inputs ao Controller de qualquer maneira. A melhor que consigo pensar é a seguinte:
- O View pode apresentar tanto o Model quanto o Controller.
Isto evita que um input passe pelo Model antes de passar pelo Controller. No caso do View ser uma Janela com botões, por exemplo, os botões são uma apresentação de um Controller especial.
Múltiplas interfaces são possíveis com múltiplos controllers. Para isso é importante que:
- Apenas um Controller pode existir para cada tipo de input.
Por exemplo, um Input Manager, um Window Interface, etc... Controllers então são um tipo de serviço. Classes de permissão de inputs, se necessárias, podem ser tratadas tanto internamente a estes controllers, como podem ser incarnadas em um controller para cada classe de permissão (isto depende de qual é mais fácil de fazer para o seu jogo).
Inputs internos
Alguém precisa monitorar o Model para causar inputs internos. Analogamente ao problema da definição da Interface, um tal monitor pode fazer parte ou do Model ou do Controller. No entanto, neste caso, o padrão MVC define uma resposta:
- Uma Task, um tipo de objeto básico que causa inputs internos, tem substância, logo faz sentido ela fazer parte do Model, e não do Controller.
- O Model possui as regras de comportamento do programa, logo ele que gera inputs internos.
Isto resolve o problema, e é análoga à solução do problema do View.
Conclusão
Definições
Nossa incarnação do MVC, portanto, é a seguinte:
View
- Podem existir vários deles, todos são independentes entre si.
- Eles cuidam de output, apresentando o Model e componentes de interface de Controllers.
- Eles podem repassar inputs do usuário para os componentes de interface que eles apresentam.
- As interações do View são:
- View -> Model : O View enxerga o Model e cria uma apresentação dele para o User. (lookup)
- View -> Controller : O View enxerga os Controllers relevantes, e cria uma apresentação deles para o User. (lookup)
- View -> User : O Output para o User, apresentação do Model e de Controllers.
- View <- User : User interage com componentes de interface, como apresentados pelo View.
- View -> Controller : View repassa os inputs do usuário para os componentes corretos de interface.
- note que o View só muda por ações baseadas no estado do Model ou dos Controllers
Controller
- Podem existir vários deles, desde que cada um cuide de uma classe de inputs em específico.
- Controlam todo tipo de input, externo ou interno, antes que ele passe pelo Model.
- Controllers podem ser vistos como serviços.
- As interações do Controller são:
- Controller <- View : View repassa os inputs do usuário para os componentes corretos de interface.
- Controller <- User : User interage com componentes de interface do Controller diretamente.
- Controller <- Model : Model manda inputs internos para o Controller.
- Controller -> Model : O único meio de modificar o Model externamente, passando pelo Controller.
- Controller -> Controller : O Controller pode modificar a si mesmo.
- note que toda flecha não-lookup que chega no Model passa pelo Controller pelas vias especificadas
Model
- O core de dados e lógica do programa ou componente de objeto.
- É único, no sentido de que um programa ou objeto não pode ter vários Models de um mesmo tipo ativos ao mesmo tempo.
- As interações do Controller são:
- Model -> View : O View enxerga o Model e cria uma apresentação dele para o User. (lookup)
- Model -> Controller: Model manda inputs internos para o Controller.
- Model <- Controller: O único meio de modificar o Model externamente, passando pelo Controller.
Organigrama
(TODO: fazer)