Skip to content

Padrão Builder - Currículo

1. Introdução

Este documento descreve a implementação do padrão de projeto Builder (Construtor) para a construção de objetos Currículo no sistema DicasDeEstagio. O Builder é um padrão criacional que permite construir objetos complexos passo a passo, especialmente útil quando o objeto possui muitos atributos opcionais.

2. Objetivo

O padrão Builder foi escolhido para o Currículo devido à:

  • Complexidade: Currículos possuem múltiplos campos opcionais (formação, habilidades, competências, etc.)

  • Flexibilidade: Permite criar currículos com diferentes combinações de informações

  • Legibilidade: Interface fluente torna o código mais legível e expressivo

3. Estrutura do Padrão

3.1 Diagrama de Classes

Diagrama
classDiagram class Curriculo { +id: int +user: User +competencias: str +arquivo: File +formacao: str +habilidade: str +validar() +exportar_pdf() } class CurriculoBuilder { -curriculo: Curriculo +__init__(user: User) +with_competencias(competencias: str) +with_formacao(formacao: str) +with_habilidade(habilidade: str) +with_arquivo(arquivo: File) +build() +reset() } class CurriculoDirector { -builder: CurriculoBuilder +__init__(builder: CurriculoBuilder) +construir_curriculo_basico() +construir_curriculo_completo() +construir_curriculo_estagio() } CurriculoBuilder --> Curriculo : constrói CurriculoDirector --> CurriculoBuilder : usa

4. Implementação em Django

4.1 Modelo Currículo (models.py)

Código
from django.db import models
from users.models import User

class Curriculo(models.Model):
    competencias = models.TextField()
    arquivo = models.FileField(upload_to='curriculos/')
    formacao = models.TextField()
    habilidade = models.TextField()
    user = models.ForeignKey('users.User', on_delete=models.CASCADE)

    def __str__(self):
        return f'Currículo de {self.user.email}'

4.2 Builder (builders.py)

Código
from typing import Optional
from django.core.files.uploadedfile import UploadedFile
from django.core.exceptions import ValidationError
from .models import Curriculo

class CurriculoBuilder:
    """
    Builder para construção de objetos Currículo.
    Permite construir currículos de forma incremental e fluente.
    """

    def __init__(self, user):
        """
        Inicializa o builder com o usuário obrigatório.

        Args:
            user: Usuário dono do currículo
        """
        self.user = user
        self._curriculo = None
        self.reset()

    def reset(self):
        """Reinicia o builder com um novo currículo vazio"""
        self._curriculo = Curriculo(user=self.user)
        return self

    def with_competencias(self, competencias: str):
        """
        Adiciona competências ao currículo.

        Args:
            competencias: String com as competências

        Returns:
            self: Para permitir encadeamento de métodos
        """
        self._curriculo.competencias = competencias
        return self

    def with_formacao(self, formacao: str):
        """
        Adiciona formação acadêmica ao currículo.

        Args:
            formacao: String com a formação

        Returns:
            self: Para permitir encadeamento de métodos
        """
        self._curriculo.formacao = formacao
        return self

    def with_habilidade(self, habilidade: str):
        """
        Adiciona habilidades ao currículo.

        Args:
            habilidade: String com as habilidades

        Returns:
            self: Para permitir encadeamento de métodos
        """
        self._curriculo.habilidade = habilidade
        return self

    def with_arquivo(self, arquivo: UploadedFile):
        """
        Adiciona arquivo PDF/DOC ao currículo.

        Args:
            arquivo: Arquivo enviado pelo usuário

        Returns:
            self: Para permitir encadeamento de métodos
        """
        self._curriculo.arquivo = arquivo
        return self

    def _validar(self):
        """Valida os dados do currículo antes de salvar"""
        if not self._curriculo.competencias:
            raise ValidationError("Competências é obrigatório")
        if not self._curriculo.formacao:
            raise ValidationError("Formação é obrigatório")
        if not self._curriculo.habilidade:
            raise ValidationError("Habilidade é obrigatório")
        if not self._curriculo.arquivo:
            raise ValidationError("Arquivo é obrigatório")

    def build(self) -> Curriculo:
        """
        Constrói e retorna o objeto Currículo final.

        Returns:
            Curriculo: Objeto currículo construído

        Raises:
            ValidationError: Se os dados forem inválidos
        """
        self._validar()
        curriculo = self._curriculo
        curriculo.save()
        self.reset()
        return curriculo

    def build_without_save(self) -> Curriculo:
        """
        Constrói o objeto sem salvar no banco.
        Útil para testes ou pré-visualização.

        Returns:
            Curriculo: Objeto currículo não salvo
        """
        self._validar()
        curriculo = self._curriculo
        self.reset()
        return curriculo

class CurriculoDirector:
    """
    Director que orquestra a construção de diferentes tipos de currículos.
    Encapsula a lógica de construção de currículos pré-definidos.
    """

    def __init__(self, builder: CurriculoBuilder):
        """
        Inicializa o director com um builder.

        Args:
            builder: Builder a ser usado na construção
        """
        self._builder = builder

    @property
    def builder(self) -> CurriculoBuilder:
        """Retorna o builder atual"""
        return self._builder

    @builder.setter
    def builder(self, builder: CurriculoBuilder):
        """Define um novo builder"""
        self._builder = builder

    def construir_curriculo_basico(self, arquivo: UploadedFile) -> Curriculo:
        """
        Constrói um currículo básico com informações mínimas.

        Args:
            arquivo: Arquivo do currículo (obrigatório)

        Returns:
            Curriculo: Currículo básico construído
        """
        return (self._builder
                .with_formacao("Formação a ser preenchida")
                .with_habilidade("Habilidades a serem preenchidas")
                .with_competencias("Competências a serem preenchidas")
                .with_arquivo(arquivo)
                .build())

    def construir_curriculo_completo(
        self,
        competencias: str,
        formacao: str,
        habilidade: str,
        arquivo: UploadedFile
    ) -> Curriculo:
        """
        Constrói um currículo completo com todas as informações.

        Args:
            competencias: Competências do candidato
            formacao: Formação acadêmica
            habilidade: Habilidades técnicas e comportamentais
            arquivo: Arquivo do currículo

        Returns:
            Curriculo: Currículo completo construído
        """
        return (self._builder
                .with_competencias(competencias)
                .with_formacao(formacao)
                .with_habilidade(habilidade)
                .with_arquivo(arquivo)
                .build())

    def construir_curriculo_estagio(
        self,
        formacao: str,
        habilidade: str,
        arquivo: UploadedFile
    ) -> Curriculo:
        """
        Constrói um currículo específico para candidatos a estágio.
        Foca em formação e habilidades.

        Args:
            formacao: Formação acadêmica em andamento
            habilidade: Habilidades relevantes para estágio
            arquivo: Arquivo do currículo

        Returns:
            Curriculo: Currículo para estágio construído
        """
        competencias_estagio = (
            "Proatividade, Vontade de aprender, "
            "Trabalho em equipe, Comunicação"
        )

        return (self._builder
                .with_competencias(competencias_estagio)
                .with_formacao(formacao)
                .with_habilidade(habilidade)
                .with_arquivo(arquivo)
                .build())

4.3 Serializers (serializers.py)

Código
from rest_framework import serializers
from .models import Curriculo
from .builders import CurriculoBuilder, CurriculoDirector

class CurriculoSerializer(serializers.ModelSerializer):
    """Serializer padrão para Currículo"""

    class Meta:
        model = Curriculo
        fields = ['id', 'user', 'competencias', 'arquivo', 'formacao', 'habilidade']

class CurriculoBuilderSerializer(serializers.Serializer):
    """
    Serializer que usa o Builder Pattern para criar currículos.
    """
    competencias = serializers.CharField(required=True)
    formacao = serializers.CharField(required=True)
    habilidade = serializers.CharField(required=True)
    arquivo = serializers.FileField(required=True)
    tipo_curriculo = serializers.ChoiceField(
        choices=['basico', 'completo', 'estagio'],
        default='completo',
        required=False
    )

    def create(self, validated_data):
        """
        Cria um currículo usando o Builder Pattern.
        """
        user = self.context['request'].user
        tipo = validated_data.pop('tipo_curriculo', 'completo')
        arquivo = validated_data['arquivo']

        builder = CurriculoBuilder(user)
        director = CurriculoDirector(builder)

        if tipo == 'basico':
            curriculo = director.construir_curriculo_basico(arquivo=arquivo)
        elif tipo == 'estagio':
            curriculo = director.construir_curriculo_estagio(
                formacao=validated_data['formacao'],
                habilidade=validated_data['habilidade'],
                arquivo=arquivo
            )
        else:  # completo
            curriculo = director.construir_curriculo_completo(
                competencias=validated_data['competencias'],
                formacao=validated_data['formacao'],
                habilidade=validated_data['habilidade'],
                arquivo=arquivo
            )

        return curriculo

    def to_representation(self, instance):
        """Usa o serializer padrão para representação"""
        return CurriculoSerializer(instance).data

4.4 Views (views.py)

Código
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from .models import Curriculo
from .serializers import CurriculoSerializer, CurriculoBuilderSerializer
from .builders import CurriculoBuilder, CurriculoDirector

class CurriculoViewSet(viewsets.ModelViewSet):
    """
    ViewSet para gerenciar currículos.
    Implementa o padrão Builder para criação.
    """
    queryset = Curriculo.objects.all()
    serializer_class = CurriculoSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        """Retorna apenas currículos do usuário logado"""
        return Curriculo.objects.filter(user=self.request.user)

    def get_serializer_class(self):
        """Usa BuilderSerializer na criação"""
        if self.action == 'create':
            return CurriculoBuilderSerializer
        return CurriculoSerializer

    def perform_create(self, serializer):
        """Criação usando Builder já está no serializer"""
        serializer.save()

    @action(detail=False, methods=['post'], url_path='criar-rapido')
    def criar_curriculo_rapido(self, request):
        """
        Endpoint para criar currículo básico rapidamente.

        POST /api/curriculos/criar-rapido/
        Body: {"arquivo": <file>}
        """
        arquivo = request.FILES.get('arquivo')

        if not arquivo:
            return Response(
                {'error': 'Arquivo é obrigatório'},
                status=status.HTTP_400_BAD_REQUEST
            )

        builder = CurriculoBuilder(request.user)
        director = CurriculoDirector(builder)

        try:
            curriculo = director.construir_curriculo_basico(arquivo=arquivo)
            serializer = CurriculoSerializer(curriculo)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        except Exception as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_400_BAD_REQUEST
            )

    @action(detail=False, methods=['post'], url_path='criar-estagio')
    def criar_curriculo_estagio(self, request):
        """
        Endpoint para criar currículo otimizado para estágio.

        POST /api/curriculos/criar-estagio/
        Body: {
            "formacao": "...",
            "habilidade": "...",
            "arquivo": <file>
        }
        """
        formacao = request.data.get('formacao')
        habilidade = request.data.get('habilidade')
        arquivo = request.FILES.get('arquivo')

        if not all([formacao, habilidade, arquivo]):
            return Response(
                {'error': 'Formação, habilidade e arquivo são obrigatórios'},
                status=status.HTTP_400_BAD_REQUEST
            )

        builder = CurriculoBuilder(request.user)
        director = CurriculoDirector(builder)

        try:
            curriculo = director.construir_curriculo_estagio(
                formacao=formacao,
                habilidade=habilidade,
                arquivo=arquivo
            )
            serializer = CurriculoSerializer(curriculo)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        except Exception as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_400_BAD_REQUEST
            )

5. Exemplo de Uso

5.1 Uso Direto do Builder

Código
from .builders import CurriculoBuilder

# Criar currículo passo a passo
builder = CurriculoBuilder(user=request.user)
curriculo = (builder
    .with_formacao("Engenharia de Software - UnB")
    .with_habilidade("Python, Django, React")
    .with_competencias("Trabalho em equipe, Comunicação")
    .build())

5.2 Uso com Director

Código
from .builders import CurriculoBuilder, CurriculoDirector

builder = CurriculoBuilder(user=request.user)
director = CurriculoDirector(builder)

# Currículo completo
curriculo = director.construir_curriculo_completo(
    competencias="Proatividade, Liderança",
    formacao="Engenharia de Software - UnB",
    habilidade="Python, Django, JavaScript",
    arquivo=arquivo_upload
)

5.3 Requisições API

Código
# Criar currículo completo
POST /api/curriculos/
Content-Type: application/json

{
    "tipo_curriculo": "completo",
    "competencias": "Trabalho em equipe, Comunicação",
    "formacao": "Engenharia de Software - UnB",
    "habilidade": "Python, Django, React, Git",
    "arquivo": "<file>"
}

# Criar currículo rápido
POST /api/curriculos/criar-rapido/

# Criar currículo para estágio
POST /api/curriculos/criar-estagio/
{
    "formacao": "Engenharia de Software - 5º semestre",
    "habilidade": "Python, Java, Git, Scrum"
}

6. Vantagens da Implementação

  • Flexibilidade: Fácil adicionar novos campos ao currículo
  • Legibilidade: Interface fluente torna o código autoexplicativo
  • Reusabilidade: Director permite criar templates de currículos
  • Testabilidade: Fácil testar diferentes combinações
  • Validação: Validação centralizada no método build()

7. Testes

Código
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.core.files.uploadedfile import SimpleUploadedFile
from .builders import CurriculoBuilder, CurriculoDirector

User = get_user_model()

class CurriculoBuilderTestCase(TestCase):

    def setUp(self):
        self.user = User.objects.create_user(
            email='teste@example.com',
            name='Teste User',
            password='senha123'
        )
        self.arquivo = SimpleUploadedFile(
            "test.pdf", 
            b"file_content", 
            content_type="application/pdf"
        )

    def test_construir_curriculo_completo(self):
        """Testa construção de currículo completo"""
        builder = CurriculoBuilder(self.user)
        curriculo = (builder
            .with_competencias("Competência 1")
            .with_formacao("Formação 1")
            .with_habilidade("Habilidade 1")
            .with_arquivo(self.arquivo)
            .build())

        self.assertIsNotNone(curriculo.id)
        self.assertEqual(curriculo.user, self.user)
        self.assertEqual(curriculo.competencias, "Competência 1")
        self.assertEqual(curriculo.formacao, "Formação 1")
        self.assertEqual(curriculo.habilidade, "Habilidade 1")

    def test_director_curriculo_basico(self):
        """Testa criação via director"""
        builder = CurriculoBuilder(self.user)
        director = CurriculoDirector(builder)

        curriculo = director.construir_curriculo_basico(arquivo=self.arquivo)
        self.assertIsNotNone(curriculo.id)
        self.assertEqual(curriculo.user, self.user)

    def test_validacao_campos_obrigatorios(self):
        """Testa que campos obrigatórios devem ser preenchidos"""
        builder = CurriculoBuilder(self.user)

        # Testa sem competencias
        with self.assertRaises(Exception):
            (builder
             .with_formacao("Formação 1")
             .with_habilidade("Habilidade 1")
             .with_arquivo(self.arquivo)
             .build())

        # Testa sem arquivo
        with self.assertRaises(Exception):
            (builder
             .with_competencias("Competência 1")
             .with_formacao("Formação 1")
             .with_habilidade("Habilidade 1")
             .build())

    def test_build_without_save(self):
        """Testa construção sem salvar no banco"""
        builder = CurriculoBuilder(self.user)
        curriculo = (builder
            .with_competencias("Competência 1")
            .with_formacao("Formação 1")
            .with_habilidade("Habilidade 1")
            .with_arquivo(self.arquivo)
            .build_without_save())

        self.assertIsNone(curriculo.id)
        self.assertEqual(curriculo.competencias, "Competência 1")

8. Referências

  • Gamma, E. et al. (1994). Design Patterns: Elements of Reusable Object-Oriented Software
  • Django Documentation: https://docs.djangoproject.com/
  • Django REST Framework: https://www.django-rest-framework.org/

Histórico de Versões

Versão Data Descrição Autor(es) Revisor(es)
1.0 24/10/2025 Criação do documento Breno Alexandre, Víctor Moreira, Felipe Nunes