Qual é a diferença entre MVC e MVVM (para Flutter)?

Feb 14 2023
Se você perguntar a n pessoas sobre a diferença entre MVC (Model-View-Controller) e MVVM (Model-View-ViewModel), obterá pelo menos n^n opiniões diferentes sobre o que é. Até Allen Wyma, apresentador do podcast “Flying high with flutter” e seu convidado Richard Coutts, criador e mantenedor do pacote MVVM+ (pacote que implementa o padrão MVVM para Flutter), admitem que mesmo para eles é difícil para obter a diferença.

Se você perguntar a n pessoas sobre a diferença entre MVC ( Model - View - Controller ) e MVVM ( Model - View - ViewModel ), você obterá pelo menos n^n opiniões diferentes sobre o que é. Até Allen Wyma, apresentador do podcast “Flying high with flutter” e seu convidado Richard Coutts, criador e mantenedor do pacote MVVM+ (pacote que implementa o padrão MVVM para Flutter), admitem que mesmo para eles é difícil para obter a diferença. Mas como um estudante, aspirante a programador ou empreendedor entusiasta de tecnologia deve entender esse tópico, se até mesmo os especialistas acham difícil articular a diferença?

Neste artigo, darei a você uma melhor compreensão do MVVM e do MVC, explicando os dois padrões e fornecendo uma implementação de contra-aplicativo de ambos os padrões no Flutter.

Definindo as definições

Um problema com a compreensão do MVC e do MVVM é que existem diferentes definições de cada padrão, que variam em aspectos menores e maiores. Para entender as diferenças, precisamos estabelecer uma definição para cada padrão que comparamos.

A seguir, referir-me-ei à definição MVC de Glenn E. Krasner e Stephen T. Pope (1988), que desenvolveram ainda mais o padrão original cunhado por Trygve Reenskau (1979). Escolhi não usar a criação original de Reenskau porque ela difere da compreensão moderna do MVC, por exemplo, incluindo o editor como uma das quatro partes importantes do padrão.

Para MVVM, refiro-me à definição de Josh Smith (2009) que trabalhou na Microsoft onde o princípio MVVM foi inventado. Aqui, escolhi a definição de Smith porque ela é mais bem documentada do que a definição do inventor de Ken Cooper e Ted Peters (2005).

Definições gerais

Então, vamos começar com algumas definições gerais:

MVVM e MVC são padrões de arquitetura usados ​​no desenvolvimento de software. Os padrões de arquitetura orientam você sobre como estruturar seu código e como separar diferentes níveis de preocupação, como a interface do usuário (UI) e a lógica de negócios.

Ambos os padrões têm uma View e um Model que são semelhantes em muitos aspectos. O Modelo representa os dados e a lógica de negócios do aplicativo. É responsável por recuperar e armazenar dados e realizar quaisquer cálculos ou transformações necessárias nesses dados. A View é responsável por exibir os dados (burros) para o usuário. A View não deve conter nenhuma lógica de negócios e não deve modificar os dados do Model de forma alguma.

Embora a View e o Model tenham responsabilidades semelhantes em ambos os padrões, há uma diferença fundamental na forma como eles interagem.

MVVM

No MVVM, a View está conectada a um ViewModel , que é uma representação do estado da View , e o ViewModel está relacionado ao Model . A View se atualiza com base nas alterações do ViewModel , em vez de diretamente no Model . O ViewModel atua como um intermediário entre o View e o Model e é responsável por coordenar o fluxo de dados entre os dois. O ViewModel é responsável por manter o estado da View , o estado da Viewcomportamento e a lógica que interage com o Model . Também é responsável por traduzir os dados do Model para um formato que a View possa entender.

A vinculação de dados entre a Visualização e o Modelo garante que os dados entre eles sejam sincronizados. Para isso, pode ser utilizado o padrão observer que notifica a View quando o Model muda. Além disso, o View também pode notificar o ViewModel quando o usuário interage com ele. Isso permite que o ViewModel atualize o Model e, por sua vez, o View .

MVC

No MVC, a View acessa diretamente o Model para obter os dados e atualiza os dados com base nas ações do usuário. A View depende do Model . O Controller é o componente que fica entre a View e o Model . Ele é responsável por interpretar a entrada do usuário e então tomar a ação apropriada no Model e/ou View . Ele também atua como um tomador de decisão sobre o que deve ser exibido na Visualização , podendo decidir atualizar a Visualização ou alterar a Visualização com base em uma ação específica do usuário.

Em resumo, o ViewModel no MVVM e o Controller no MVC são semelhantes, pois ambos atuam como intermediários entre o View e o Model , mas o ViewModel é mais focado em manter o estado do View , enquanto no MVC o View obtém seus dados diretamente do Modelo .

Exemplo de código: aplicativo contador

Como implementação exemplar, escolhi um app contador simples com dois botões e texto com o contador atual. Um botão aumenta o contador em um e o outro aumenta o contador em dois. Escolhi um exemplo com dois botões para enfatizar que o processamento de entrada funciona de maneira diferente. Para gerenciamento de estado, usei o ChangeNotifier do pacote Provider .

Captura de tela do aplicativo contador de exemplo

Exemplo de código MVC

O código a seguir mostra a implementação do aplicativo contador para o MVC. O fluxo principal começa na View onde um dos dois botões é acionado em seu método onPressed (1). Em seguida, o Controller chama a função de incremento apropriada do Modelo (2). Por fim, o Model se atualiza (3) e então notifica a View para atualizar a UI (4).

Os documentos originais do MVC mostram que a entrada do usuário é recebida diretamente pelo Controller . No Flutter, não é possível receber a entrada diretamente em um Controller (me diga nos comentários se eu supervisionei algo), mas apenas através do View . Comportamento semelhante é imitado delegando diretamente a entrada do View para o Controller com sua própria função para cada entrada possível (1).

Você encontra o código completo que pode ser executado diretamente neste DartPad .


class MyModel with ChangeNotifier {
  int _counter = 0;
  int get counter => _counter;

  void incrementCounter(int increment) {
    _counter += increment; // (3)
    notifyListeners(); // (4)
  }
}

class MyView extends StatelessWidget {
  final MyController controller;
  final MyModel model;

  const MyView({super.key, required this.controller, required this.model});

  @override
  Widget build(_) {
    return ChangeNotifierProvider<MyModel>(
      create: (_) => model,
      builder: (_, __) => Consumer<MyModel>(
        builder: (_, model, __) => Scaffold(
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text('Counter: ${model.counter}'), // (4)
                TextButton(
                  onPressed: controller.onButtonIncrement1Pressed, // (1)
                  child: const Text('Increment by 1'),
                ),
                TextButton(
                  onPressed: controller.onButtonIncrement2Pressed, // (1)
                  child: const Text('Increment by 2'),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class MyController {
  final MyModel model;

  MyController({required this.model});

  void onButtonIncrement1Pressed() {
    model.incrementCounter(1); // (2)
  }

  void onButtonIncrement2Pressed() {
    model.incrementCounter(2); // (2)
  }
}

No código MVVM, a entrada do usuário é registrada pela View . A View chama a função correspondente no ViewModel (1). O ViewModel então chama a função para incrementar o contador no Model (2). Posteriormente, o ViewModel notifica todos os ouvintes (3). A View se atualiza porque o valor do Model é passado para ela através do ViewModel (4).

Você pode ver e executar o código completo neste DartPad .


class MyModel {
  int _counter = 0;
  int get counter => _counter;

  void incrementCounter(int increment) {
    _counter += increment;
  }
}

class MyView extends StatelessWidget {
  final MyViewModel viewModel;

  const MyView({super.key, required this.viewModel});

  @override
  Widget build(_) {
    return ChangeNotifierProvider(
      create: (_) => MyViewModel(model: MyModel()),
      child: Consumer<MyViewModel>(
        builder: (_, viewModel, __) => Scaffold(
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text('Counter: ${viewModel.counter}'), // (4)
                TextButton(
                  onPressed: () => viewModel.incrementCounter(1), // (1)
                  child: const Text('Increment by 1'),
                ),
                TextButton(
                  onPressed: () => viewModel.incrementCounter(2), // (1)
                  child: const Text('Increment by 2'),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class MyViewModel with ChangeNotifier {
  final MyModel _model;
  int get counter => _model.counter; // (4)

  MyViewModel({required MyModel model}) : _model = model;

  void incrementCounter(int increment) {
    _model.incrementCounter(increment); // (2)
    notifyListeners(); // (3)
  }
}

As principais diferenças nas implementações de MVC e MVVM são como a entrada é tratada e as conexões entre os elementos. Pela definição de Krasner e Pope, a entrada deve ser recebida pelo Controlador. Esse comportamento pode ser ridicularizado, mas não é realmente possível de implementar no Flutter. A diferença mais importante é que no MVC o Model está conectado ao View , mas no MVVM eles estão desconectados.

Na internet existem muitos recursos que desconectam a View e o Model em MVC. Com o tempo a definição de MVC aproximou-se cada vez mais no sentido de MVVM. Mesmo que cada pessoa possa interpretar os padrões arquitetônicos à sua maneira, isso, na minha opinião, é a principal razão pela qual muitas pessoas não veem diferença entre os dois padrões.

Além disso, deve-se mencionar que MVVM e MVC não necessariamente se excluem. Se você estiver interessado em se aprofundar, recomendo ler sobre MVCVM. Uma abordagem que combina Model , View , Controller e ViewModel , mas isso não será abordado aqui.

Espero que agora você entenda melhor a diferença entre MVC e MVVM e possa usá-lo para articular melhor os tópicos de arquitetura de software em seus projetos com sua equipe. Qualquer feedback é muito apreciado.