Builder em Zig
O padrão Builder separa a construção de um objeto complexo de sua representação, permitindo criar diferentes configurações passo a passo. Em Zig, este padrão é extremamente natural graças aos valores padrão em structs e à convenção de Options structs.
Quando Usar
- Objetos com muitos parâmetros opcionais
- Configuração de servidores, clientes e conexões
- Construção de queries, comandos ou requisições
- Quando a ordem de configuração importa ou requer validação
Padrão Idiomático: Options Struct
A abordagem mais idiomática em Zig para o Builder é o Options struct com valores padrão:
const std = @import("std");
const Servidor = struct {
host: []const u8,
porta: u16,
max_conexoes: u32,
timeout_ms: u64,
tls: bool,
workers: u16,
const Options = struct {
host: []const u8 = "0.0.0.0",
porta: u16 = 8080,
max_conexoes: u32 = 1024,
timeout_ms: u64 = 30_000,
tls: bool = false,
workers: u16 = 4,
};
pub fn init(opcoes: Options) Servidor {
return .{
.host = opcoes.host,
.porta = opcoes.porta,
.max_conexoes = opcoes.max_conexoes,
.timeout_ms = opcoes.timeout_ms,
.tls = opcoes.tls,
.workers = opcoes.workers,
};
}
pub fn iniciar(self: *const Servidor) !void {
std.debug.print("Servidor {s}:{d} ({d} workers, TLS: {})\n", .{
self.host, self.porta, self.workers, self.tls,
});
}
};
pub fn main() !void {
// Usar valores padrão
const srv1 = Servidor.init(.{});
try srv1.iniciar();
// Customizar apenas o necessário
const srv2 = Servidor.init(.{
.porta = 443,
.tls = true,
.workers = 8,
});
try srv2.iniciar();
}
Builder com API Fluente (Method Chaining)
const std = @import("std");
const QueryBuilder = struct {
tabela: []const u8 = "",
campos: [10][]const u8 = undefined,
num_campos: usize = 0,
condicao: ?[]const u8 = null,
limite: ?u32 = null,
ordenar_por: ?[]const u8 = null,
pub fn select(campos: []const []const u8) QueryBuilder {
var qb = QueryBuilder{};
for (campos, 0..) |campo, i| {
if (i >= 10) break;
qb.campos[i] = campo;
qb.num_campos = i + 1;
}
return qb;
}
pub fn from(self: QueryBuilder, tabela: []const u8) QueryBuilder {
var qb = self;
qb.tabela = tabela;
return qb;
}
pub fn where(self: QueryBuilder, condicao: []const u8) QueryBuilder {
var qb = self;
qb.condicao = condicao;
return qb;
}
pub fn limit(self: QueryBuilder, n: u32) QueryBuilder {
var qb = self;
qb.limite = n;
return qb;
}
pub fn orderBy(self: QueryBuilder, campo: []const u8) QueryBuilder {
var qb = self;
qb.ordenar_por = campo;
return qb;
}
pub fn build(self: *const QueryBuilder) !void {
const stdout = std.io.getStdOut().writer();
try stdout.writeAll("SELECT ");
for (self.campos[0..self.num_campos], 0..) |campo, i| {
if (i > 0) try stdout.writeAll(", ");
try stdout.writeAll(campo);
}
try stdout.print(" FROM {s}", .{self.tabela});
if (self.condicao) |c| try stdout.print(" WHERE {s}", .{c});
if (self.ordenar_por) |o| try stdout.print(" ORDER BY {s}", .{o});
if (self.limite) |l| try stdout.print(" LIMIT {d}", .{l});
try stdout.writeAll(";\n");
}
};
pub fn main() !void {
const query = QueryBuilder
.select(&.{ "nome", "email", "idade" })
.from("usuarios")
.where("idade > 18")
.orderBy("nome")
.limit(10);
try query.build();
// SELECT nome, email, idade FROM usuarios WHERE idade > 18 ORDER BY nome LIMIT 10;
}
Builder com Validação
const std = @import("std");
const ConfigDB = struct {
host: []const u8,
porta: u16,
database: []const u8,
usuario: []const u8,
senha: []const u8,
pool_size: u16,
const BuildError = error{
HostObrigatorio,
DatabaseObrigatoria,
UsuarioObrigatorio,
PortaInvalida,
};
const Builder = struct {
host: ?[]const u8 = null,
porta: u16 = 5432,
database: ?[]const u8 = null,
usuario: ?[]const u8 = null,
senha: []const u8 = "",
pool_size: u16 = 10,
pub fn setHost(self: Builder, host: []const u8) Builder {
var b = self;
b.host = host;
return b;
}
pub fn setPorta(self: Builder, porta: u16) Builder {
var b = self;
b.porta = porta;
return b;
}
pub fn setDatabase(self: Builder, db: []const u8) Builder {
var b = self;
b.database = db;
return b;
}
pub fn setUsuario(self: Builder, usuario: []const u8) Builder {
var b = self;
b.usuario = usuario;
return b;
}
pub fn setSenha(self: Builder, senha: []const u8) Builder {
var b = self;
b.senha = senha;
return b;
}
pub fn build(self: *const Builder) BuildError!ConfigDB {
return .{
.host = self.host orelse return error.HostObrigatorio,
.porta = self.porta,
.database = self.database orelse return error.DatabaseObrigatoria,
.usuario = self.usuario orelse return error.UsuarioObrigatorio,
.senha = self.senha,
.pool_size = self.pool_size,
};
}
};
};
pub fn main() !void {
const config = try (ConfigDB.Builder{})
.setHost("db.exemplo.com")
.setDatabase("meu_app")
.setUsuario("admin")
.setSenha("seguro123")
.build();
std.debug.print("Conectando a {s}:{d}/{s}\n", .{
config.host, config.porta, config.database,
});
}
Quando Evitar
- Structs simples com poucos campos — use inicialização direta
.{ .campo = valor } - Quando o Options struct resolve o problema de forma mais simples
- Objetos imutáveis que não precisam de construção incremental