Browse Source

In Portuguese

master 20210722
Julio Biason 3 years ago
parent
commit
51f3eaa0e6
  1. 76
      content/code/command-pattern-rust.md
  2. 149
      content/code/command-pattern-rust.pt.md

76
content/code/command-pattern-rust.md

@ -6,27 +6,34 @@ date = 2021-07-22
tags = ["design patterns", "command", "rust"]
+++
I've been playing a bit with the command pattern in Rust and found at least two
ways to write it.
I've been doing some experiments in using the command pattern in Rust and found
at least two ways to write it.
<!-- more -->
## But first... why?
## But first... Why?
There is one thing I'm trying to do with all this: I want to have a library
with all the actions and then plug interfaces on top of, being either a CLI
interface or a Web interface or whatever interface. For that, the logic behind
the action should be somewhat isolated from whatever source it is calling it.
And the command interface fits perfectly in this.
There is one thing I'm trying to do in which the command pattern fits
perfectly: I want to have a library with all the actions and then plug
interfaces on top of, being either a CLI interface or a Web interface or
whatever interface. For that, the logic behind the action should be somewhat
isolated from whatever source it is calling it.
## What It Is
The Command Pattern is described as having one object for each action ('cause,
you know, the patterns focused more on OO designs) and each of those have an
`execute` method which... well... execute the command.
## The Enum Solution
Because you have a list of actions, one of the ideas was to use `Enums`.
As what you have is a list of actions, one of the ideas was to use `Enums`,
even if it is not exactly what the pattern describes.
Say, we have two actions the user can trigger: deposit money and withdraw
money. Simple.
Say, we have two actions can be called: deposit money and withdraw money.
Simple.
So one could have the following Enum:
So one could have the following Enum[^1]:
```rust
enum Command {
@ -39,7 +46,7 @@ Because Rust allows enum variants to carry a value with them, the amount to be
deposited/withdraw is attached directly to the variant.
And then you have the `execute()` function. And, again, 'cause Rust allows
adding functions to almost everything, what I did was:
adding functions to almost everything, what I did was add a method in the Enum:
```rust
impl Command {
@ -54,9 +61,21 @@ impl Command {
... and so on.
To use it, I put something pretty close to this in my interface layer:
```rust
let value = incoming_external_request.value()
let command = match incoming_external_request.command() {
"deposit" => Command::Deposit(value),
"withdraw" => Command::Withdraw(value),
}
command.execute();
```
It feels fine and all, but it tends to make a mess with the amount of content
that goes in or around the `impl`, in my opinion. But, at the same time, means
that our interface layer must just return `Command` and that's all fine.
that goes in or around the `impl`, in my opinion. But, at the same time, the
dispatch layer (between the service/enum layer and the interface layer) is
pretty basic.
One solution to the amount of "content in or around `impl`" could be use
multiple `impl`: So I could have a module `deposit.rs` which `impl`s the
@ -66,8 +85,9 @@ to do the proper "dispatch" of the calls.
## The Trait Solution
The trait solution is very close to what the pattern is: You create a trait and
"impl" it for all the commands. For example:
The trait solution is very close to what the pattern is: You create a trait
(interface) and "impl" it for all the commands, which are just structs. For
example:
```rust
trait Command {
@ -91,10 +111,13 @@ impl Command for Withdraw {
}
```
This causes a slight problem with the interface layer: Now it can't just return
one sized thing: It needs to return a dynamic dispatchable content, like
`Box<dyn Command>`, which isn't as pretty as the direct Enum/Struct/sized
content from the Enum.
... which feels a bit cleaner, since all related things to Deposit or Withdraw
are now tied together.
However, this causes a slight problem with the interface layer: Now it can't
just return one sized thing: It needs to return a dynamic dispatchable content,
like `Box<dyn Command>`, which isn't as pretty as the direct Enum/Struct/sized
content.
On the other hand, since `Box` implements `Deref`, once the interface throws
something-that-implements-Command, one could just call `execute()` directly on
@ -105,9 +128,6 @@ let command = interface_that_returns_a_box_dyn_command();
command.execute();
```
Also, because all commands are structures, they can hold the same information
as Enum variants would.
## Where I see those two
I can see the Enum being used for simple, single domain architectures. Since
@ -115,4 +135,10 @@ all things are related, they can reside correctly under the Enum.
But when dealing with multiple domains, the trait/dynamic dispatch feels more
at home: Related things get into their own module, in their own space and the
idea of mixing them goes on layer above.
idea of mixing them (for example, if you have a money domain and a tag domain,
and you want to tag money operations) goes on layer above.
---
[^1]: `Decimal` is not part of Rust Standard Library, but can be used from the
[rust_decimal crate](https://crates.io/crates/rust_decimal).

149
content/code/command-pattern-rust.pt.md

@ -0,0 +1,149 @@
+++
title = "Experimentos com Command Pattern em Rust"
date = 2021-07-22
[taxonomies]
tags = ["design patterns", "command", "rust"]
+++
Eu tenho feito alguns experimentos implementando o command pattern em Rust e
encontrei pelo menos duas formas de implementar.
<!-- more -->
## Mas Primeiro... Por que?
Existe uma coisa que eu estou tentando fazer em que o command pattern se
encaixa perfeitamente: Eu quero ter uma biblioteca com todas as ações do
sistema e implementar uma interface em cima disso, sendo que pode ser uma CLI
ou uma interface web ou uma interface qualquer. Para isso, a lógica por trás da
ação deve estar de alguma forma isolada da origem da chamada.
## O Que É
O command pattern é descrito como ter um objeto para cada ação (porque,
basicamente, os patterns são mais focados em projetos orientados a objetos) e
cada um destes tem um método chamado `execute` que... bem... executa o comando.
## A Solução Enum
Como o que você têm é uma lista de ações, uma das ideias foi usar `Enum`, mesmo
que isso não seja exatamente o que pattern descreve.
Digamos que nós temos duas ações que podem ser chamadas: Depositar dinheiro e
sacar dinheiro. Simples.
Assim, podemos ter o seguinte Enum[^1]:
```rust
enum Command {
Depositar(Decimal),
Sacar(Decimal),
}
```
Como Rust permite que as variantes de um Enum carreguem um valor com elas, o
valor a ser depositado ou sacado fica anexado junto com a variante.
E então você tem a função `execute()`. E, de novo, porque Rust permite que
sejam adicionadas funções em basicamente tudo, o que eu fiz foi adicionar um
método diretamente no Enum:
```rust
impl Command {
fn execute(&self) -> Result<...> {
match self {
Depositar(valor) => faz_o_deposito(valor),
Sacar(valor) => sacar_dinheiro(valor),
}
}
}
```
E assim por diante.
Para usar, eu coloquei algo parecido com isso na minha camada de interface:
```rust
let valor = requisicao_externa.valor();
let comando = match requisicao_externa.comando() {
"depositar" => Command::Depositar(valor),
"sacar" => Command::Sacar(valor),
}
comando.execute();
```
Tudo fica simples e tal, mas existe uma tendência a deixar uma bagunça com a
quantidade de conteúdo que fica dentro ou ao redor do `impl`, na minha opinião.
Mas, ao mesmo tempo, a camada de dispatch (que fica entre a camada de
serviço/enum e a camada de interface) é bem básica.
Uma solução para para a quantidade de "conteúdo dentro ou ao redor do `impl`"
seria o uso de múltiplos `impl`: Ter um módulo `deposito.rs` que faz o `impl`
de `faz_o_deposito` e outro módulo `saque.rs` que também faz o `impl` dentro do
enum com o conteúdo de `sacar_dinheiro`. Mas eu ainda precisaria centrar todas
as operações no `execute` para ter um dispatch correto.
## A Solução com Traits
A solução com trait é bem parecida com o que o pattern diz: Você cria uma trait
(interface) e "impl" em todos os comandos, que são structs. Por exemplo:
```rust
trait Command {
fn execute(&self) -> Result<...>;
}
```
```rust
struct Depositar(Decimal);
impl Command for Depositar {
fn execute(&self) -> Result <...> {
// o que era o `faz_o_deposito` vai aqui.
}
}
struct Sacar(Decimal);
impl Command for Sacar {
fn execute(&self) -> Result <...> {
// o que era o `sacar_dinheiro` vai aqui.
}
}
```
... o que parece um pouco mais limpo, já que todas as coisas relacionadas com
Deposito ou Saque estão juntas agora.
Entretanto, isso causa um pequeno problema com a camada de interface: Agora ela
não pode mais retorna algo com tamanho fixo: É necessário usar um conteúdo com
dispatch dinâmico, como `Box<dyn Command>`, o que não é tão direto quando um
Enum/Struct/conteúdo com tamanho.
Por outro lado, como `Box` implementa `Deref`, uma vez que a interface retorne
algo-que-implementa-Command, basta chamada `execute()` diretamente nele.
```rust
let comando = interface_que_retorna_um_comando_num_box_dyn();
comando.execute();
```
## Onde Eu Vejo Esses Dois
Eu consigo ver o uso do Enum em arquiteturas simples, com apenas um domínio.
Como toas as coisas são relacionadas, elas podem viver tranquilamente dentro do
Enum.
Mas quando estamos lidando com múltiplos domínios, a solução de trait/dispatch
dinâmico parece fazer mais sentido: Coisas relacionadas nos seus próprios
módulos, nos seus próprios espaços e a ideia de misturar os mesmos (por
exemplo, se você tiver um domínio de tags e um domínio de dinheiro, e quer
colocar tags nas operações de dinheiro) ficaria na camada acima deles.
---
[^1]: `Decimal` não faz parte da biblioteca padrão do Rust, mas pode ser usada
a partir da [crate rust_decimal](https://crates.io/crates/rust_decimal).
<!--
vim:spelllang=pt:
-->
Loading…
Cancel
Save