DEV Community

Yuri Peixinho
Yuri Peixinho

Posted on

Hollywood Principle

Introdução

O Hollywood Principle é um princípio de design de software resumido na frase "Don't call us, we'll call you" — não nos chame, nós chamaremos você. É a mesma frase clássica usada por produtores em Hollywood para dispensar atores em audições sem se comprometer a retornar contato.

Em código tradicional, o seu programa controla o fluxo: ele chama bibliotecas, decide a ordem das operações e "puxa" o que precisa. No Hollywood Principle isso se inverte — um componente de nível mais alto (um framework, por exemplo) mantém o controle do fluxo, e é ele quem chama o seu código, no momento que julgar apropriado, e não o contrário.

Por isso o princípio é praticamente sinônimo de Inversão de Controle (IoC): você não chama o framework, o framework chama você.

Biblioteca vs. framework

Essa é a forma mais prática de sentir o princípio no dia a dia:

  • Biblioteca: você importa, você chama os métodos, você está no comando. Ex.: usar Math.Sqrt() ou uma lib de parsing de JSON.
  • Framework: ele define a estrutura da aplicação, e o seu código só "encaixa" nos pontos que ele espera. Ex.: você escreve um método OnClick() e o WinForms decide quando chamá-lo; você escreve uma classe Controller no ASP.NET Core e o runtime decide quando instanciá-la e invocar seus métodos.

Resumindo em uma frase: não chame o framework, o framework chama você.

Exemplos em C

1. Loop manual vs. evento

// Fluxo tradicional: você fica perguntando, você está no comando
while (true)
{
    var evento = filaDeEventos.PegarProximo();  // você decide quando pegar
    if (evento.Tipo == TipoEvento.Clique)
    {
        TratarClique(evento);                   // você chama sua própria função
    }
}
Enter fullscreen mode Exit fullscreen mode
// Hollywood principle: você registra e espera ser chamado
void TratarClique(object sender, EventArgs e)
{
    Console.WriteLine("botão clicado!");
}

botao.Click += TratarClique;   // "não me chame, eu te chamo"
Application.Run(formulario);   // o framework entra em loop e chama
                                // TratarClique quando o clique acontecer
Enter fullscreen mode Exit fullscreen mode

No primeiro caso, TratarClique aparece no seu código sendo chamada por você. No segundo, ela nunca é chamada por você em lugar nenhum — só é passada como referência (um delegate). Quem decide quando ela roda é o Application.

2. OrderBy com seletor: um mini-Hollywood dentro do LINQ

int Prioridade(Pessoa p) => p.Idade;

var pessoasOrdenadas = pessoas.OrderBy(Prioridade);
Enter fullscreen mode Exit fullscreen mode

Você nunca escreve Prioridade(pessoas[0]), Prioridade(pessoas[1])... Quem chama Prioridade, quantas vezes e em que ordem, é o algoritmo de ordenação dentro do OrderBy. Isso já é o princípio agindo, mesmo dentro de um método LINQ comum.

3. xUnit (framework de testes)

public class CalculadoraTests
{
    [Fact]
    public void Soma_DeveRetornarQuatro()
    {
        Assert.Equal(4, 2 + 2);
    }

    [Fact]
    public void Subtracao_DeveRetornarDois()
    {
        Assert.Equal(2, 5 - 3);
    }
}
Enter fullscreen mode Exit fullscreen mode

Você roda dotnet test — nunca chama Soma_DeveRetornarQuatro() você mesmo. É o xUnit que varre a classe por reflection, encontra métodos marcados com [Fact] e os chama um por um, decidindo ordem, capturando falhas, etc.

Compare com uma versão "biblioteca", onde você está no controle:

void RodarTestes()
{
    TestarSoma();
    TestarSubtracao();
    Console.WriteLine("tudo passou!");
}

RodarTestes();   // você chama, você decide a ordem
Enter fullscreen mode Exit fullscreen mode

4. Template Method: o princípio dentro de uma única classe

public abstract class Relatorio
{
    public string Gerar()                    // o "molde" — fica no controle
    {
        var dados = BuscarDados();           // chama método que a subclasse define
        return Formatar(dados);
    }

    protected abstract List<int> BuscarDados();
    protected abstract string Formatar(List<int> dados);
}

public class RelatorioVendas : Relatorio
{
    protected override List<int> BuscarDados() => new() { 100, 200, 150 };

    protected override string Formatar(List<int> dados) => $"Total: {dados.Sum()}";
}

new RelatorioVendas().Gerar();
Enter fullscreen mode Exit fullscreen mode

BuscarDados e Formatar são definidos na subclasse, mas quem os chama é o método Gerar lá na classe base. A subclasse nunca se auto-chama — ela só fornece as peças que a classe base vai encaixar quando quiser.

5. Socket cru vs. ASP.NET Core

// Baixo nível: você escreve o loop, você está no controle
var servidor = new TcpListener(IPAddress.Any, 8080);
servidor.Start();

while (true)
{
    var cliente = servidor.AcceptTcpClient();   // você chama AcceptTcpClient
    var stream = cliente.GetStream();
    var buffer = new byte[1024];
    stream.Read(buffer, 0, buffer.Length);      // você chama Read
    TratarRequisicao(buffer);                   // você chama sua própria função
}
Enter fullscreen mode Exit fullscreen mode
// Framework: ASP.NET Core está no controle
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/ola", () => "Olá, mundo!");

app.Run();
Enter fullscreen mode Exit fullscreen mode

A lambda passada a MapGet nunca é chamada em nenhuma linha que você escreveu. O ASP.NET Core recebe a requisição HTTP e decide chamar esse handler quando a rota /ola é acessada.

6. Injeção de dependência: o Hollywood Principle institucionalizado

public class PedidoService
{
    private readonly IPedidoRepository _repositorio;

    public PedidoService(IPedidoRepository repositorio)   // você não faz "new PedidoRepository()"
    {
        _repositorio = repositorio;                        // o container injeta a implementação
    }
}

builder.Services.AddScoped<IPedidoRepository, PedidoRepository>();
Enter fullscreen mode Exit fullscreen mode

Você nunca escreve new PedidoService(new PedidoRepository()). O container de DI do ASP.NET Core resolve as dependências e chama o construtor de PedidoService por você, no momento em que a classe é necessária.

O padrão nos seis exemplos

Situação Quem chama quem
loop manual você chama TratarClique
evento (botao.Click += ...) o framework/runtime chama TratarClique
OrderBy(lista) sem seletor não há callback
OrderBy(lista, p => p.Idade) OrderBy chama o seletor
RodarTestes() manual você chama TestarSoma
xUnit ([Fact]) o test runner chama o método de teste
classe abstrata chamando método virtual a classe base chama, a subclasse implementa
TcpListener cru você chama AcceptTcpClient/Read
ASP.NET Core (MapGet) o framework chama o handler
container de DI o container chama o construtor

Repare que isso aparece muito mais em OOP (polimorfismo, herança, interfaces) do que em programação estruturada pura — é por isso que frameworks orientados a objetos (ASP.NET Core, Spring, Angular, React) são o habitat natural do Hollywood Principle: eles dependem de você implementar um "contrato" (uma interface, uma classe abstrata, um delegate com assinatura esperada) para poderem chamá-lo de volta.

Relação com SOLID

O Hollywood Principle é primo próximo do Dependency Inversion Principle (o "D" do SOLID): módulos de alto nível não devem depender de módulos de baixo nível — ambos devem depender de abstrações. A ideia de "inverter" quem depende de quem é a mesma raiz conceitual por trás dos dois.

Vantagens e custos

Vantagem: baixo acoplamento — seu código não precisa saber os detalhes de quando ou como é chamado, só precisa implementar o contrato esperado (uma interface, um delegate, um método abstrato).

Custo: o fluxo de controle fica menos explícito. Ao ler o código isoladamente, não é óbvio quando uma função vai ser executada — isso pode dificultar o debugging, já que você precisa entender o framework para saber a ordem das chamadas. Esse "fluxo invertido" costuma ser citado como uma das razões pelas quais frameworks têm curva de aprendizado maior que bibliotecas.

Top comments (0)