Command em Zig
O padrão Command encapsula uma requisição como um objeto, permitindo parametrizar, enfileirar, registrar e desfazer operações. Em Zig, isso é implementado com tagged unions (para conjunto fixo de comandos) ou interfaces via ponteiros de função (para extensibilidade).
Quando Usar
- Sistemas de undo/redo (editores, jogos)
- Filas de tarefas e job queues
- Logging de operações para replay
- Macros e gravação de ações do usuário
- Transações que precisam ser revertíveis
Implementação com Tagged Union
const std = @import("std");
const Comando = union(enum) {
inserir: struct { posicao: usize, texto: []const u8 },
deletar: struct { posicao: usize, tamanho: usize },
substituir: struct { posicao: usize, antigo: []const u8, novo: []const u8 },
pub fn executar(self: Comando, buffer: *std.ArrayList(u8)) !void {
switch (self) {
.inserir => |cmd| {
try buffer.insertSlice(cmd.posicao, cmd.texto);
},
.deletar => |cmd| {
buffer.replaceRange(cmd.posicao, cmd.tamanho, &.{});
},
.substituir => |cmd| {
buffer.replaceRange(cmd.posicao, cmd.antigo.len, cmd.novo);
},
}
}
pub fn desfazer(self: Comando, buffer: *std.ArrayList(u8)) !void {
switch (self) {
.inserir => |cmd| {
buffer.replaceRange(cmd.posicao, cmd.texto.len, &.{});
},
.deletar => |cmd| {
_ = cmd;
// Precisaria do texto deletado salvo para desfazer
},
.substituir => |cmd| {
buffer.replaceRange(cmd.posicao, cmd.novo.len, cmd.antigo);
},
}
}
};
const EditorHistorico = struct {
historico: std.ArrayList(Comando),
posicao: usize = 0,
pub fn init(allocator: std.mem.Allocator) EditorHistorico {
return .{
.historico = std.ArrayList(Comando).init(allocator),
};
}
pub fn deinit(self: *EditorHistorico) void {
self.historico.deinit();
}
pub fn executar(self: *EditorHistorico, cmd: Comando, buffer: *std.ArrayList(u8)) !void {
try cmd.executar(buffer);
// Descartar comandos após posição atual (novo branch)
self.historico.shrinkRetainingCapacity(self.posicao);
try self.historico.append(cmd);
self.posicao += 1;
}
pub fn desfazer(self: *EditorHistorico, buffer: *std.ArrayList(u8)) !void {
if (self.posicao == 0) return;
self.posicao -= 1;
try self.historico.items[self.posicao].desfazer(buffer);
}
pub fn refazer(self: *EditorHistorico, buffer: *std.ArrayList(u8)) !void {
if (self.posicao >= self.historico.items.len) return;
try self.historico.items[self.posicao].executar(buffer);
self.posicao += 1;
}
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const alloc = gpa.allocator();
var buffer = std.ArrayList(u8).init(alloc);
defer buffer.deinit();
var editor = EditorHistorico.init(alloc);
defer editor.deinit();
try editor.executar(.{ .inserir = .{ .posicao = 0, .texto = "Olá Mundo" } }, &buffer);
std.debug.print("Após inserir: '{s}'\n", .{buffer.items});
try editor.desfazer(&buffer);
std.debug.print("Após desfazer: '{s}'\n", .{buffer.items});
try editor.refazer(&buffer);
std.debug.print("Após refazer: '{s}'\n", .{buffer.items});
}
Fila de Comandos
const std = @import("std");
const Tarefa = union(enum) {
enviar_email: struct { para: []const u8, assunto: []const u8 },
processar_imagem: struct { caminho: []const u8, largura: u32 },
gerar_relatorio: struct { periodo: []const u8 },
pub fn executar(self: Tarefa) void {
switch (self) {
.enviar_email => |t| std.debug.print("Enviando email para {s}: {s}\n", .{ t.para, t.assunto }),
.processar_imagem => |t| std.debug.print("Processando {s} ({d}px)\n", .{ t.caminho, t.largura }),
.gerar_relatorio => |t| std.debug.print("Gerando relatório: {s}\n", .{t.periodo}),
}
}
};
const FilaTarefas = struct {
fila: std.ArrayList(Tarefa),
pub fn init(allocator: std.mem.Allocator) FilaTarefas {
return .{ .fila = std.ArrayList(Tarefa).init(allocator) };
}
pub fn deinit(self: *FilaTarefas) void {
self.fila.deinit();
}
pub fn enfileirar(self: *FilaTarefas, tarefa: Tarefa) !void {
try self.fila.append(tarefa);
}
pub fn processarTodas(self: *FilaTarefas) void {
for (self.fila.items) |tarefa| {
tarefa.executar();
}
self.fila.clearRetainingCapacity();
}
};
Quando Evitar
- Operações simples sem necessidade de undo ou enfileiramento
- Quando o overhead de encapsular cada ação não se justifica
- Sistemas onde a ordem de execução não importa
Veja Também
- State Machine — Transições de estado estruturadas
- Observer — Notificar após executar comandos
- Pipeline — Processamento sequencial de etapas
- Producer-Consumer — Fila assíncrona de tarefas
- Error Handling — Erros em comandos