DEV Community

Cover image for O Padrão Revealing Module no Javascript
Gabriel Rufino
Gabriel Rufino

Posted on

O Padrão Revealing Module no Javascript

Essa é uma tradução de um gist escrito por Zach Caceres.

Javascript não tem os especificadores private e public das linguagens orientadas a objeto mais tradicionais como C# ou Java. Contudo, você pode conseguir o mesmo efeito através da aplicação inteligente do escopo a nível de função do Javascript.

O princípio central do padrão Revealing Module é que todas as funcionalidades e variáveis devem ser ocultadas, a menos que deliberadamente expostas.

Vamos imaginar que nós temos um aplicativo de música onde um arquivo musicPlayer.js manipula grande parte da experiência do usuário. Precisamos acessar alguns métodos, mas não devemos mexer com outros métodos ou variáveis.

Usando Escopo de Função para Criar Métodos Públicos e Privados

Vamos primeiro ver como o escopo a nível de função do Javascript pode nos ajudar a criar métodos públicos e privados.

Podemos mover todas as funcionalidades para dentro de um escopo de função. Depois, retornamos um objeto com as funções que gostaríamos que fossem acessíveis em outros arquivos.

// musicPlayerModule.js

var musicPlayer = function () {
  // Vamos garantir que ninguém possa acessar diretamente nossa songList
  var songList = ['California Girls', 'California Dreaming', 'Hotel California'];  

  // Nós vamos expor todas essas funções para o usuários
  function play () {
    console.log('Im playing the next song!');
  }

  function pause () {
    console.log('Im paused!');
  }

  function addTrackToMusicQueue (track) {
    songList.push(track);
    console.log('I added a song');
  }

  function showNextTrack () {
    console.log('My next track is', songList[0]);
  }

  // Vamos ocultar essa função
  function loadSong() {
    filesystem.loadNextSong();
  }

  return {
    playMusic: play,
    pauseMusic: pause,
    showNextTrack: showNextTrack,
    addTrack: addTrackToMusicQueue
  }
}

const musicModule = musicPlayer(); // Chama nossa musicPlayer para retornar seu objeto (módulo)
musicModule.playMusic(); // 'Im playing the next song!'
musicModule.pauseMusic(); // 'I'm paused!'
musicModule.showNextTrack(); // 'The next track is California Girls'

// Coisas que não podemos acessar...
musicModule.loadSong(); // error: not a function
musicModule.songList.push('White Rabbit'); // undefined error
Enter fullscreen mode Exit fullscreen mode

Agora, podemos acessar todos os métodos que nós precisamos em nosso objeto musicModule. Mas, não podemos mexer no nosso songList ou acessar o método loadSong(). Ambos são privados.

Isso funciona bem. Mas há um grande problema.

Estamos usando musicPlayer como um namespace para manter nossas funções. Mas espere, nosso musicPlayer é uma função que ainda está exposta ao escopo global!

Alguém poderia aparecer e invocá-lo novamente, criando um novo musicPlayer. Então, teríamos várias instância de musicPlayer flutuando, poluindo nosso ambiente e causando todo tipo de confusão.

Ocultando Seu Módulo com uma IIFE

A melhor maneira de evitar a exposição de sua função top-level ao escopo global é agrupar tudo em uma IIFE. Uma IIFE é uma immediately-invoked function expression (expressão de função chamada imediatamente). É um nome e tanto. Significa apenas que nós chamamos (invocamos) essa função assim que o arquivo é executado (imediatamente).

Como nossa função não tem nome, nós a chamamos de expressão. Como não tem nome, nunca pode ser invocado em outro lugar.

Veja como fica:

var musicModule = (function () {
  // Vamos garantir que ninguém possa acessar diretamente nossa songList
  var songList = ['California Girls', 'California Dreaming', 'Hotel California'];  

  // Nós vamos expor todas essas funções para o usuários
  function play () {
    console.log('Im playing the next song!');
  }

  function pause () {
    console.log('Im paused!');
  }

  function addTrackToMusicQueue (track) {
    songList.push(track);
    console.log('I added a song');
  }

  function showNextTrack () {
    console.log('My next track is', songList[0]);
  }

  // Vamos ocultar essa função
  function loadSong() {
    filesystem.loadNextSong();
  }

  return {
    playMusic: play,
    pauseMusic: pause,
    showUpNext: showNextTrack,
    addTrack: addTrackToMusicQueue
  }
})(); // Nossa função IIFE (cercada por parênteses) é chamada aqui

musicModule.playMusic(); // 'Im playing the next song!'
musicModule.pauseMusic(); // 'I'm paused!'
musicModule.showUpNext(); // 'The next track is California Girls'
musicModule.loadSong(); // error: not a function
musicModule.songList.push('White Rabbit'); // undefined
Enter fullscreen mode Exit fullscreen mode

Nosso escopo a nível de função ainda mantém nossos métodos e variáveis pública e privadas, com base em se os expomos no objeto de retorno.

Mas dessa vez, nós evitamos o risco de podermos invocar nosso módulo em outro lugar no nosso código.

Em outros arquivo nós podemos agora usar as funcionalidades de musicModule - Um módulo bem encapsulado em nosso escopo global!

Top comments (0)