Padrão Singleton
Olá. Desta vez escrevo sobre um padrão de projeto para programação Orientada a Objetos, também muito conhecido: O Singleton. Esse padrão, quando aplicado ao projeto de software OO, visa que se utilize o objeto de uma determinada classe em qualquer parte do sistema, com a garantia que esse a classe desse objeto só é instanciada uma vez.
A idéia é, primeiramente, criar um construtor privado na classe para impedir a instanciação direta da mesma através do operador "new", chamado fora do escopo da classe e, depois, criar um método estático que retorna a única instância criada da classe. Veja o diagrama abaixo:
Perceba que o método "getInstance" é estático, assim como, também, o atributo privado "instance". O atributo "instance" representa a única instância da classe. Agora, veja abaixo qual a lógica do singleton na classe Singleton:
class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if ( instance == null )
instance = new Singleton();
return instance;
}
public void algumMetodo() {
}
}
public class Main {
public static void main(String[] args) {
Singleton.getInstance().algumMetodo();
}
}
Esta é a implementação mais básica do singleton, onde, o método "getInstance" instancia a classe Singleton, apenas, se a mesma ainda não foi instanciada nenhuma vez e retorna a instância. Caso contrário, apenas retorna a única instância.
O método algumMetodo é apenas um método qualquer da classe singleton que pode ser chamado em qualquer parte do sistema através da chamada: Singleton.getInstance().algumMetodo();.
Singleton para player de audio
Agora, vamos a um exemplo mais interessante: Um player de áudio. Esse player de áudio é um programa de linha de comandos que recebe um argumento: o caminho de um arquivo de música em formato ".wav". Caso, nenhum argumento seja passado, o programa assume que no diretório atual da linha de comandos tem um arquivo de nome: "picapau.wav" e toca ele. Veja o código fonte da classe principal:
package singleton;
import javax.swing.JOptionPane;
import singleton.audio.AudioPlayer;
public class Main {
public static void main(String[] args) {
String audio = "picapau.wav";
if ( args.length == 1 )
audio = args[ 0 ];
try {
AudioPlayer.getInstance().play( audio );
} catch (RuntimeException ex) {
JOptionPane.showMessageDialog( null, ex.getMessage() );
}
}
}
No programa acima, se o número de argumentos for 1, então, é passado para o player de áudio, a string correspondente ao valor do argumento. Caso, contrário, é passado para o player de áudio a string "picapau.wav".
Agora, repare na seguinte chamada: AudioPlayer.getInstance().play( audio );. Claramente, podemos perceber aqui que a classe AudioPlayer é uma classe Singleton. E, o método "play", toca o áudio.
Veja abaixo o código fonte da classe AudioPlayer:
package singleton.audio;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
public class AudioPlayer {
private static AudioPlayer instance = null;
private AudioPlayer() {}
public static AudioPlayer getInstance() {
if ( instance == null )
instance = new AudioPlayer();
return instance;
}
public void play( String arquivoAudio ) throws Exception {
try {
InputStream in = new FileInputStream( arquivoAudio );
InputStream bufIn = new BufferedInputStream( in );
AudioInputStream ain = AudioSystem.getAudioInputStream( bufIn );
Clip clip = AudioSystem.getClip();
clip.open( ain );
clip.start();
Thread.sleep( clip.getMicrosecondLength() / 1000 );
clip.close();
} catch ( UnsupportedAudioFileException ex ) {
throw new Exception( "Formato de arquivo de áudio não suportado." );
} catch ( LineUnavailableException ex ) {
throw new Exception( "Erro na abertura do arquivo de áudio." );
} catch ( FileNotFoundException ex ) {
throw new Exception( "\""+arquivoAudio+"\"\nArquivo não encontrado." );
} catch ( IOException ex ) {
throw new Exception( "Erro na leitura do arquivo de áudio." );
}
}
}
Observe primeiramente o método "getInstance", a variável privada e estática "instance" e o construtor privado como o único na classe. São características de uma classe Singleton!
Agora, você pode entender como a chamada ao método "play" no método principal funciona. Mas, entenda também que a classe AudioPlayer tem, de fato, o método de nome "play" que recebe o caminho do arquivo de áudio para tocar.
O método "play"
Pronto! Vamos agora focar no método "play" para entender como ele funciona. Perceba que há a possibilidade de uma exceção tipo Exception ser lançada com uma mensagem personalizada e capturada no método principal. O ideal era criar uma classe de exceção personalizada. Mas, por questões de simplicidade, utilizei a classe Exception mesmo. Se uma exception é lançada pelo método "play", é mostrada uma janelinha com a mensagem personalizada.
Singleton com multithreading
Como o singleton utiliza um atributo e um método estático, talvez não seja uma boa idéia utilizar essa solução em um ambiente concorrente.
O download do código fonte
Abaixo o link de download do código fonte do projeto que toca um áudio.
Link de download: singleton-exemplo.zip
Após baixar o projeto, você pode compilar se estiver com o JDK. Então, use a linha de comandos para entrar na pasta base do projeto descompactado e utilize o seguinte comando para compilar:
javac -d bin src/singleton/*.java src/singleton/audio/*.java
Agora para rodar, basta utilizar o seguinte comando:
java -cp bin singleton.Main oi-meu-chapa.wav
No comando acima, você pode passar qualquer arquivo de áudio em formato ".wav" no lugar de "oi-meu-chapa.wav". Se não passar o arquivo, o "picapau.wav" é executado por padrão, conforme o comando abaixo:
java -cp bin singleton.Main
Finalizando...
Esse é o final de mais um artigo sobre padrões de projeto. Desta vez, o padrão singleton foi discutido aqui. Espero que tenham gostado e, até o próximo!