---
title: "Factory Method em Rust"
url: "https://rustlang.com.br/padroes/factory/"
markdown_url: "https://rustlang.com.br/padroes/factory.MD"
description: "Aprenda o padrao Factory Method em Rust: criacao de objetos via trait objects, enums como fabricas, Box\u003cdyn Trait\u003e para polimorfismo em tempo de execucao e exemplos praticos."
date: ""
author: "Equipe Rust Brasil"
---

# Factory Method em Rust

Aprenda o padrao Factory Method em Rust: criacao de objetos via trait objects, enums como fabricas, Box<dyn Trait> para polimorfismo em tempo de execucao e exemplos praticos.


## Introducao

O **Factory Method** (Metodo Fabrica) e um padrao criacional que define uma interface para
criar objetos, mas permite que as subclasses (ou, em Rust, os implementadores de traits)
decidam qual tipo concreto instanciar. Em vez de chamar `new()` diretamente em um tipo
concreto, voce delega a criacao para uma funcao ou trait que decide *qual* tipo criar
com base em parametros ou configuracao.

Em Rust, o Factory Method se manifesta de formas diferentes das linguagens orientadas a
objetos tradicionais. Sem heranca de classes, usamos **traits**, **enums** e **funcoes
livres** para obter o mesmo resultado, muitas vezes com mais seguranca e performance.

---

## Problema

Voce esta construindo um sistema que precisa processar arquivos de configuracao em
diversos formatos: JSON, YAML e TOML. Cada formato tem sua propria logica de parsing,
mas o resto do sistema nao deve se preocupar com qual formato esta sendo usado.

Sem o Factory, seu codigo ficaria cheio de condicionais:

```rust
// Codigo fragil - cada novo formato exige modificacao aqui
fn carregar_config(caminho: &str) -> Result<Config, Erro> {
    if caminho.ends_with(".json") {
        // logica para JSON...
        let conteudo = std::fs::read_to_string(caminho)?;
        // parse JSON manualmente
        todo!()
    } else if caminho.ends_with(".yaml") || caminho.ends_with(".yml") {
        // logica para YAML...
        todo!()
    } else if caminho.ends_with(".toml") {
        // logica para TOML...
        todo!()
    } else {
        // E se quisermos adicionar XML? INI? HCL?
        // Precisamos mexer nessa funcao toda vez!
        Err(Erro::FormatoDesconhecido)
    }
}
```

Esse codigo viola o principio Aberto/Fechado: cada novo formato requer modificacao
no codigo existente, em vez de simplesmente estender o sistema.

---

## Solucao em Rust

### Abordagem 1: Factory com Trait Objects

A abordagem mais flexivel usa `Box<dyn Trait>` para retornar diferentes implementacoes:

```rust
use std::collections::HashMap;
use std::path::Path;

/// Erro unificado para operacoes de parsing
#[derive(Debug)]
pub enum ParserError {
    FormatoDesconhecido(String),
    ErroSintaxe { linha: usize, mensagem: String },
    IoError(std::io::Error),
}

/// Valor generico representando dados de configuracao
#[derive(Debug, Clone)]
pub enum Valor {
    Texto(String),
    Inteiro(i64),
    Decimal(f64),
    Booleano(bool),
    Lista(Vec<Valor>),
    Mapa(HashMap<String, Valor>),
    Nulo,
}

/// Trait que define a interface comum para todos os parsers
pub trait Parser: Send + Sync {
    /// Faz o parse de uma string e retorna a estrutura de dados
    fn parse(&self, conteudo: &str) -> Result<Valor, ParserError>;

    /// Retorna o nome do formato suportado
    fn formato(&self) -> &str;

    /// Retorna as extensoes de arquivo suportadas
    fn extensoes(&self) -> &[&str];
}

/// Parser para formato JSON
pub struct JsonParser;

impl Parser for JsonParser {
    fn parse(&self, conteudo: &str) -> Result<Valor, ParserError> {
        // Implementacao simplificada para demonstracao
        // Em producao, usaria serde_json
        println!("[JSON] Fazendo parse de {} bytes", conteudo.len());

        // Simulacao de parse basico
        if conteudo.trim().starts_with('{') {
            Ok(Valor::Mapa(HashMap::new()))
        } else {
            Err(ParserError::ErroSintaxe {
                linha: 1,
                mensagem: "JSON deve comecar com '{'".to_string(),
            })
        }
    }

    fn formato(&self) -> &str {
        "JSON"
    }

    fn extensoes(&self) -> &[&str] {
        &["json"]
    }
}

/// Parser para formato TOML
pub struct TomlParser;

impl Parser for TomlParser {
    fn parse(&self, conteudo: &str) -> Result<Valor, ParserError> {
        println!("[TOML] Fazendo parse de {} bytes", conteudo.len());

        // Simulacao: procura por pares chave=valor
        let mut mapa = HashMap::new();
        for linha in conteudo.lines() {
            if let Some((chave, valor)) = linha.split_once('=') {
                mapa.insert(
                    chave.trim().to_string(),
                    Valor::Texto(valor.trim().trim_matches('"').to_string()),
                );
            }
        }
        Ok(Valor::Mapa(mapa))
    }

    fn formato(&self) -> &str {
        "TOML"
    }

    fn extensoes(&self) -> &[&str] {
        &["toml"]
    }
}

/// Parser para formato YAML
pub struct YamlParser;

impl Parser for YamlParser {
    fn parse(&self, conteudo: &str) -> Result<Valor, ParserError> {
        println!("[YAML] Fazendo parse de {} bytes", conteudo.len());
        Ok(Valor::Mapa(HashMap::new()))
    }

    fn formato(&self) -> &str {
        "YAML"
    }

    fn extensoes(&self) -> &[&str] {
        &["yaml", "yml"]
    }
}

/// Fabrica que cria o parser adequado com base na extensao do arquivo
pub struct ParserFactory {
    parsers: Vec<Box<dyn Parser>>,
}

impl ParserFactory {
    pub fn new() -> Self {
        Self {
            parsers: Vec::new(),
        }
    }

    /// Registra um novo parser na fabrica
    pub fn registrar(mut self, parser: Box<dyn Parser>) -> Self {
        self.parsers.push(parser);
        self
    }

    /// Cria o parser adequado para o caminho do arquivo
    pub fn criar_para_arquivo(&self, caminho: &str) -> Result<&dyn Parser, ParserError> {
        let extensao = Path::new(caminho)
            .extension()
            .and_then(|ext| ext.to_str())
            .unwrap_or("");

        self.parsers
            .iter()
            .find(|p| p.extensoes().contains(&extensao))
            .map(|p| p.as_ref())
            .ok_or_else(|| ParserError::FormatoDesconhecido(extensao.to_string()))
    }

    /// Lista todos os formatos suportados
    pub fn formatos_suportados(&self) -> Vec<&str> {
        self.parsers.iter().map(|p| p.formato()).collect()
    }
}

fn main() {
    // Configura a fabrica com todos os parsers disponiveis
    let fabrica = ParserFactory::new()
        .registrar(Box::new(JsonParser))
        .registrar(Box::new(TomlParser))
        .registrar(Box::new(YamlParser));

    println!(
        "Formatos suportados: {:?}",
        fabrica.formatos_suportados()
    );

    // A fabrica escolhe o parser correto automaticamente
    let arquivos = vec![
        "config.json",
        "Cargo.toml",
        "docker-compose.yaml",
        "dados.yml",
    ];

    for arquivo in arquivos {
        match fabrica.criar_para_arquivo(arquivo) {
            Ok(parser) => {
                println!("\n{}: usando parser {}", arquivo, parser.formato());
                let _ = parser.parse("{ \"exemplo\": true }");
            }
            Err(e) => println!("\n{}: erro - {:?}", arquivo, e),
        }
    }
}
```

### Abordagem 2: Factory com Enum (Despacho Estatico)

Quando os tipos possiveis sao conhecidos em tempo de compilacao, enums evitam alocacoes:

```rust
/// Enum que representa todos os parsers possiveis
#[derive(Debug, Clone)]
pub enum ParserEnum {
    Json,
    Toml,
    Yaml,
}

impl ParserEnum {
    /// Factory method: cria o parser baseado na extensao
    pub fn para_extensao(ext: &str) -> Result<Self, String> {
        match ext {
            "json" => Ok(Self::Json),
            "toml" => Ok(Self::Toml),
            "yaml" | "yml" => Ok(Self::Yaml),
            outro => Err(format!("Extensao '{}' nao suportada", outro)),
        }
    }

    /// Factory method: cria baseado no conteudo (detecta formato)
    pub fn detectar_formato(conteudo: &str) -> Self {
        let trimmed = conteudo.trim();
        if trimmed.starts_with('{') || trimmed.starts_with('[') {
            Self::Json
        } else if trimmed.contains("---") {
            Self::Yaml
        } else {
            Self::Toml // padrao
        }
    }

    /// Executa o parse usando o parser apropriado
    pub fn parse(&self, conteudo: &str) -> Result<String, String> {
        match self {
            Self::Json => {
                println!("Parseando como JSON...");
                Ok(format!("JSON parseado: {} bytes", conteudo.len()))
            }
            Self::Toml => {
                println!("Parseando como TOML...");
                Ok(format!("TOML parseado: {} bytes", conteudo.len()))
            }
            Self::Yaml => {
                println!("Parseando como YAML...");
                Ok(format!("YAML parseado: {} bytes", conteudo.len()))
            }
        }
    }
}

fn main() {
    // Factory via extensao
    let parser = ParserEnum::para_extensao("json").unwrap();
    let resultado = parser.parse(r#"{"chave": "valor"}"#).unwrap();
    println!("{}", resultado);

    // Factory via deteccao automatica
    let parser = ParserEnum::detectar_formato("[1, 2, 3]");
    let resultado = parser.parse("[1, 2, 3]").unwrap();
    println!("{}", resultado);
}
```

---

## Diagrama

```
FACTORY COM TRAIT OBJECTS:

    +------------------+
    |  ParserFactory   |
    |  (fabrica)       |
    +--------+---------+
             |
             | criar_para_arquivo("config.json")
             |
             v
    +------------------+        +------------------+
    | dyn Parser       |<-------| JsonParser       |
    | (trait object)   |        +------------------+
    +------------------+        | TomlParser       |
                                +------------------+
                                | YamlParser       |
                                +------------------+

FACTORY COM ENUM:

    ParserEnum::para_extensao("toml")
             |
             v
    +------------------+
    | ParserEnum::Toml |----> parse() via match
    +------------------+

    Sem alocacao no heap!
    Despacho estatico, nao virtual.
```

---

## Exemplo do Mundo Real

Uma fabrica de conexoes de banco de dados que suporta multiplos backends:

```rust
use std::collections::HashMap;

/// Resultado de uma consulta ao banco de dados
#[derive(Debug)]
pub struct ResultadoConsulta {
    pub colunas: Vec<String>,
    pub linhas: Vec<Vec<String>>,
}

/// Trait para conexoes de banco de dados
pub trait ConexaoBanco: Send + Sync {
    fn conectar(&mut self) -> Result<(), String>;
    fn executar(&self, sql: &str) -> Result<ResultadoConsulta, String>;
    fn desconectar(&mut self) -> Result<(), String>;
    fn nome_driver(&self) -> &str;
}

/// Conexao PostgreSQL
pub struct ConexaoPostgres {
    url: String,
    conectado: bool,
}

impl ConexaoBanco for ConexaoPostgres {
    fn conectar(&mut self) -> Result<(), String> {
        println!("[PostgreSQL] Conectando a {}...", self.url);
        self.conectado = true;
        Ok(())
    }

    fn executar(&self, sql: &str) -> Result<ResultadoConsulta, String> {
        if !self.conectado {
            return Err("Nao conectado ao PostgreSQL".to_string());
        }
        println!("[PostgreSQL] Executando: {}", sql);
        Ok(ResultadoConsulta {
            colunas: vec!["id".into(), "nome".into()],
            linhas: vec![vec!["1".into(), "Maria".into()]],
        })
    }

    fn desconectar(&mut self) -> Result<(), String> {
        println!("[PostgreSQL] Desconectando...");
        self.conectado = false;
        Ok(())
    }

    fn nome_driver(&self) -> &str {
        "PostgreSQL"
    }
}

/// Conexao SQLite
pub struct ConexaoSqlite {
    caminho: String,
    conectado: bool,
}

impl ConexaoBanco for ConexaoSqlite {
    fn conectar(&mut self) -> Result<(), String> {
        println!("[SQLite] Abrindo banco em {}...", self.caminho);
        self.conectado = true;
        Ok(())
    }

    fn executar(&self, sql: &str) -> Result<ResultadoConsulta, String> {
        if !self.conectado {
            return Err("Banco SQLite nao aberto".to_string());
        }
        println!("[SQLite] Executando: {}", sql);
        Ok(ResultadoConsulta {
            colunas: vec!["resultado".into()],
            linhas: vec![vec!["ok".into()]],
        })
    }

    fn desconectar(&mut self) -> Result<(), String> {
        println!("[SQLite] Fechando banco...");
        self.conectado = false;
        Ok(())
    }

    fn nome_driver(&self) -> &str {
        "SQLite"
    }
}

/// Fabrica de conexoes de banco de dados
pub fn criar_conexao(url: &str) -> Result<Box<dyn ConexaoBanco>, String> {
    if url.starts_with("postgres://") || url.starts_with("postgresql://") {
        Ok(Box::new(ConexaoPostgres {
            url: url.to_string(),
            conectado: false,
        }))
    } else if url.starts_with("sqlite://") || url.ends_with(".db") {
        let caminho = url.strip_prefix("sqlite://").unwrap_or(url);
        Ok(Box::new(ConexaoSqlite {
            caminho: caminho.to_string(),
            conectado: false,
        }))
    } else {
        Err(format!("Protocolo de banco nao suportado: {}", url))
    }
}

fn main() {
    let urls = vec![
        "postgres://localhost/meu_app",
        "sqlite://dados.db",
    ];

    for url in urls {
        let mut conexao = criar_conexao(url).expect("Falha ao criar conexao");
        conexao.conectar().expect("Falha ao conectar");

        println!("Driver: {}", conexao.nome_driver());

        let resultado = conexao
            .executar("SELECT * FROM usuarios LIMIT 5")
            .expect("Falha na consulta");

        println!("Colunas: {:?}", resultado.colunas);
        println!("Linhas: {:?}\n", resultado.linhas);

        conexao.desconectar().expect("Falha ao desconectar");
    }
}
```

---

## Quando Usar

- **Voce nao sabe em tempo de compilacao qual tipo concreto criar** (decisao em tempo de execucao)
- **Precisa desacoplar a criacao do uso** do objeto
- **Novos tipos podem ser adicionados** sem modificar o codigo existente
- **Plugins ou extensoes** que registram novos tipos em tempo de execucao
- **Configuracao externa** determina qual implementacao usar (arquivo de config, variavel de ambiente)

---

## Quando NAO Usar

- **Apenas um ou dois tipos concretos** que nunca mudarao - use construtores diretos
- **Performance critica** onde a indirection de `Box<dyn Trait>` e inaceitavel - use enums ou genericos
- **O tipo e conhecido em tempo de compilacao** - use genericos em vez de trait objects

```rust
// Se o tipo e conhecido estaticamente, genericos sao melhores:
fn processar<P: Parser>(parser: P, dados: &str) -> Result<Valor, ParserError> {
    parser.parse(dados)
}

// Em vez de:
fn processar(parser: Box<dyn Parser>, dados: &str) -> Result<Valor, ParserError> {
    parser.parse(dados)
}
```

---

## Variacoes em Rust

### 1. Factory Function (funcao livre)
A forma mais simples e idiomatica:

```rust
pub fn criar_parser(formato: &str) -> Box<dyn Parser> {
    match formato {
        "json" => Box::new(JsonParser),
        "toml" => Box::new(TomlParser),
        _ => panic!("formato desconhecido: {}", formato),
    }
}
```

### 2. Factory com Closures
Para fabricas customizaveis sem definir novas structs:

```rust
type FabricaFn = Box<dyn Fn(&str) -> Box<dyn Parser>>;

pub struct RegistroFabricas {
    fabricas: HashMap<String, FabricaFn>,
}

impl RegistroFabricas {
    pub fn registrar(&mut self, ext: &str, fabrica: FabricaFn) {
        self.fabricas.insert(ext.to_string(), fabrica);
    }

    pub fn criar(&self, ext: &str, config: &str) -> Option<Box<dyn Parser>> {
        self.fabricas.get(ext).map(|f| f(config))
    }
}
```

### 3. Factory via Associated Function em Trait
Usando metodos associados no trait:

```rust
pub trait Serializador {
    /// Factory method definido no proprio trait
    fn criar(formato: &str) -> Box<dyn Serializador>
    where
        Self: Sized;

    fn serializar(&self, dados: &Valor) -> String;
}
```

---

## Padroes Relacionados

- **[Builder](/padroes/builder)** - Constroi objetos complexos passo a passo; Factory cria de uma so vez
- **[Strategy](/padroes/strategy)** - Muito similar ao Factory com traits; a diferenca e a intencao
- **[Prototype](/padroes/prototype)** - Cria objetos clonando um prototipo em vez de usar uma fabrica
- **[Singleton](/padroes/singleton)** - Factory pode retornar sempre a mesma instancia (Singleton)

---

## Conclusao

O Factory Method em Rust se beneficia enormemente do sistema de traits, que substitui
a heranca de classes das linguagens orientadas a objetos. A escolha entre trait objects
(`Box<dyn Trait>`) e enums depende do seu caso de uso: trait objects oferecem extensibilidade
em tempo de execucao, enquanto enums oferecem melhor performance e verificacao exaustiva
pelo compilador. Em muitos projetos Rust reais, a "fabrica" e simplesmente uma funcao
livre que retorna `Box<dyn Trait>`, demonstrando que nem sempre e necessario criar
estruturas complexas para aplicar o padrao.
