DEV Community

Lucas Pereira de Souza
Lucas Pereira de Souza

Posted on

Entendendo e Implementando Design Patterns em TypeScript

Image description
Design Patterns são soluções generalistas para problemas recorrentes no desenvolvimento de software. Esses padrões ajudam a estruturar o código de forma organizada, facilitando a manutenção, a reutilização e a escalabilidade do sistema. Em TypeScript, um superconjunto de JavaScript, os Design Patterns podem ser implementados de forma ainda mais eficiente devido à forte tipagem e recursos de orientação a objetos.

Neste post, exploraremos três categorias principais de Design Patterns (Criacionais, Estruturais e Comportamentais) e como implementá-los em TypeScript.


1. Padrões Criacionais

Os padrões criacionais lidam com a criação de objetos, ajudando a encapsular o processo de instância e a promover a reutilização do código.

Exemplo: Singleton

O Singleton garante que uma classe tenha apenas uma única instância durante todo o ciclo de vida da aplicação.

class Singleton {
    private static instance: Singleton;

    private constructor() {}

    static getInstance(): Singleton {
        if (!Singleton.instance) {
            Singleton.instance = new Singleton();
        }
        return Singleton.instance;
    }

    someMethod() {
        console.log("Método do Singleton");
    }
}

const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();

console.log(instance1 === instance2); // true
Enter fullscreen mode Exit fullscreen mode

No exemplo acima, o método getInstance() garante que apenas uma instância da classe Singleton seja criada.

2. Padrões Estruturais

Os padrões estruturais tratam da composição de classes e objetos, garantindo que grandes estruturas de código possam ser construídas a partir de peças menores e mais simples.

Exemplo: Adapter

O padrão Adapter permite que duas interfaces incompatíveis trabalhem juntas. Isso é útil quando você quer utilizar uma classe que tem uma interface diferente da que o seu código espera.

// Interface antiga
class OldAPI {
    oldRequest() {
        return "Dados da API antiga";
    }
}

// Interface nova
class NewAPI {
    newRequest() {
        return "Dados da API nova";
    }
}

// Adapter que adapta a interface antiga para a nova
class APIAdapter {
    private oldAPI: OldAPI;

    constructor(oldAPI: OldAPI) {
        this.oldAPI = oldAPI;
    }

    newRequest() {
        return this.oldAPI.oldRequest();
    }
}

const oldAPI = new OldAPI();
const adapter = new APIAdapter(oldAPI);

console.log(adapter.newRequest()); // "Dados da API antiga"
Enter fullscreen mode Exit fullscreen mode

Nesse exemplo, o Adapter (APIAdapter) permite que a classe OldAPI seja utilizada com a interface esperada pela NewAPI.

3. Padrões Comportamentais

Os padrões comportamentais lidam com a interação e comunicação entre objetos, promovendo a flexibilidade e a desacoplagem no código.

Exemplo: Observer

O padrão Observer define uma dependência um-para-muitos entre objetos de forma que, quando um objeto muda de estado, todos os seus dependentes são notificados e atualizados automaticamente.

interface Observer {
    update(data: any): void;
}

class Subject {
    private observers: Observer[] = [];

    addObserver(observer: Observer) {
        this.observers.push(observer);
    }

    removeObserver(observer: Observer) {
        this.observers = this.observers.filter(obs => obs !== observer);
    }

    notifyObservers(data: any) {
        this.observers.forEach(observer => observer.update(data));
    }
}

class ConcreteObserver implements Observer {
    update(data: any) {
        console.log("Observer atualizado com dados:", data);
    }
}

const subject = new Subject();
const observer1 = new ConcreteObserver();
const observer2 = new ConcreteObserver();

subject.addObserver(observer1);
subject.addObserver(observer2);

subject.notifyObservers("Alguma informação importante");
// Ambos observers recebem a atualização
Enter fullscreen mode Exit fullscreen mode

No exemplo acima, o padrão Observer permite que vários objetos observem e reajam às mudanças de estado de um objeto sujeito (Subject).


Conclusão

Os Design Patterns são ferramentas poderosas para a construção de código robusto e escalável. TypeScript, com suas características de tipagem estática e orientação a objetos, é um ambiente excelente para implementar esses padrões, proporcionando maior segurança e produtividade no desenvolvimento.

Ao utilizar padrões criacionais, estruturais e comportamentais, você estará adotando práticas que melhoram a legibilidade e manutenção do código, garantindo soluções eficazes para problemas comuns de desenvolvimento.


Espero que este post tenha ajudado a entender como aplicar Design Patterns em TypeScript. Experimente essas implementações nos seus projetos e veja como elas podem melhorar a qualidade do seu código!

Top comments (0)