Lei de Demeter
Publicado: Maio 4, 2011 Filed under: Java | Tags: Java Deixe um comentário »Um método f de uma classe C só deve chamar métodos de:
- C
- Um objecto criado por f
- Um objecto passado como argumento para f
- Um objecto guardado numa variável de instância de C
Kerberos MIT
Publicado: Abril 21, 2011 Filed under: Kerberos | Tags: kerberos, single sign on Deixe um comentário »Como funciona:
O Kerberos foi desenvolvido a pensar em autenticação e não em autorização. Podemos comparar o Kerberos a uma espécie de serviço supremo que nos diz: “sim, podes confiar em mim e esta pessoa é quem ela diz que é”.
Kerberos só trabalha a parte da autenticação. Não faz autorização (conceder ou negar acesso a serviços baseados no desejo de o utilizador usá-los) e também não trata a parte de gestão de contas, ou seja, não guarda nenhuma informação relacionada com UIDs, GIDs, home paths.
Sendo Kerberos um protocolo, têm várias implementações, desenvolvidas para vários propósitos:
- Kerberos MIT: O original. Devido a restrições de exportação da tecnologia de encriptação, foi desenvolvido na Europa o Kerberos Heimdal
- Kerberos Heimdal: A versão Suiça do Kerberos. É compatível com o Kerberos MIT. As restrições do Kerberos MIT foram levantadas em 2000 de forma que ambas as implementações coexistem em larga escala.
- Active Directory: Não é propriamente uma implementação do Kerberos. É um diretório Microsoft com uma implementação Kerberos e mais alguns serviços associados (LDAP). Não é directamente compatível com o Kerberos MIT ou Heimdal.
- TrustBroker: Uma implementação comercial do protocolo Kerberos, desenvolvida pela CyberSafe. Suporta uma série de sistemas operativos (Windows, Linux, Unix,…) e oferece interoperabilidade com outras implementações.
- Shishi: Uma implementação do Kerberos 5 para GNU.
- Um Authentication Server que responde a pedidos de autenticação por parte dos clientes. Aqui estamos na fase de AS_REQUEST e AS_REPLY, onde o cliente recebe um Ticket Granting Ticket (TGT)
- Um Ticket Granting Server, que emite Ticket Granting Service (TGS) para um cliente. Esta é a fase TGS_REQUEST e TGS_REPLY onde o cliente recebe um TGS que vai permiti-lo autenticar-se junto de um serviço acessível na rede.
- Uma base de dados que guarda todas as chaves secretas (dos clientes e dos serviços) e mais alguma informação relacionada com as contas Kerberos (datas de criação, políticas…)
- O principal nome do cliente
- O principal Ticket Granting Server (chamado “krbtgt principal”) que vai ser necessário mais tarde para obter o TGS.
- O timestamp do cliente
- O tempo de vida do ticket pedido (normalmente entre 8 a 10 horas)
- encriptado com a chave do utilizador
- uma cópia da chave da sessão para o utilizador
- o tempo de duração do ticket
- o nome principal do krbtgt
- primeiro encriptado com a chave do Ticket Granting Server e depois com a chave do utilizador.Este é o TGT
- uma cópia da chave de sessão
- o tempo de vida do ticket
- o timestamp do KDC
- o principal do cliente
- o endereço IP do cliente
- o próprio pedido do TGS, contendo o serviço principal e o tempo de vida pedido.
- o TGT adquirido anteriormente (após uma autenticação bem sucedida)
- um autenticador
- encriptada com a chave de sessão adquirida durante o processo AS:
- cópia de uma nova chave de sessão para o utilizador
- tempo de vida efetivo do ticket
- nome do serviço principal
- primeiro encriptado com a chave do serviço de longo termo, depois com a chave de sessão actual. Isto é o TGS:
- cópia de uma nova chave de sessão, para o serviço
- tempo de vida efetivo do bilhete
- KDC timestamp
- principal do cliente
- endereço IP do cliente
- Processo de autenticação onde o utilizador (e anfitrião) obtém um Ticket Granting Ticket (TGT) como um token de autenticação.
- Processo de requisito de um serviço, onde o utilizador obtém um Ticket Granting Service (TGS) para aceder ao serviço.
- Acesso ao serviço, onde o utilizador (e anfitrião) utilizam o TGS para se autenticarem e acederem a determinado serviço.
Problema da Distância Documental
Publicado: Abril 19, 2011 Filed under: Java, Matemática | Tags: algoritmos, Java, Matemática Deixe um comentário »Motivação: Dados dois documentos, quão semelhantes eles são?
- Idênticos?
- Modificados ou relacionados (plágio, DNA…)
- Palavra: sequência de caracteres alfanuméricos (por exemplo, a frase “666.6 é quase o número da besta” tem 8 palavras).
- Frequência de palavras: Definimos a frequência de uma palavra , D(w) como o número de vezes que ela ocorre num documento D.
- Distância Métrica: A distância métrica entre documentos é definida como o produto interno entre os vectores D1 e D2 que contêm as frequências de todas as palavras nos dois documentos. Matematicamente,
D1 . D2 = ∑w[D1(w).D2(w)] (1) - Ângulo Métrico: o ângulo entre os vectores D1 e D2 dá-nos uma indicação da sobreposição dos dois documentos. Matematicamente,
θ(D1.D2) = arccos(D1.D2 / ||D1|| * ||D2||) com 0 ≤ θ ≤ π/2. Um ângulo métrico de 0 significa que os dois documentos são idênticos enquanto que um ângulo métrico de π/2 indica-nos que não há palavras em comum. - Número de palavras em um Documento: a magnitude do vector D que contém frequências de palavras de todas as palavras no documento.. Matematicamente, N(D) = ||D|| = √(D.D)
/**
* @author Manuel Leiria
*
*/
public class DocumentMatcher {
private Map<String, Integer> mapWordFrequencyDoc1 =
new TreeMap<String, Integer>();
private Map<String, Integer> mapWordFrequencyDoc2 =
new TreeMap<String, Integer>();
/**
* @return the mapWordFrequencyDoc1
*/
public Map<String, Integer> getMapWordFrequencyDoc1() {
return Collections.unmodifiableMap(mapWordFrequencyDoc1);
}
/**
* @return the mapWordFrequencyDoc2
*/
public Map<String, Integer> getMapWordFrequencyDoc2() {
return Collections.unmodifiableMap(mapWordFrequencyDoc2);
}
/**
*
* @param file1
* @param file2
*/
public DocumentMatcher(final String file1, final String file2) {
buildFrequencyMap(file1, mapWordFrequencyDoc1);
buildFrequencyMap(file2, mapWordFrequencyDoc2);
synchronizeWordMaps();
}
/**
* Constroi um Mapa na forma [palavra, num. de vezes que
* ela ocorre no documento]
* @param file
* @param mapWordFrequency
*/
private void buildFrequencyMap
(final String file, Map<String, Integer> mapWordFrequency){
try{
Scanner input = new Scanner(new File(file));
while(input.hasNextLine()){
final String line = input.nextLine();
StringTokenizer parser =
new StringTokenizer(line, ",.;:()-!?' ");
while(parser.hasMoreTokens()){
final String word =
parser.nextToken().toUpperCase();
Integer listing = mapWordFrequency.get(word);
if(listing == null){
listing = 1;
}else{
listing += 1;
}
mapWordFrequency.put(word, listing);
}
}
input.close();
}catch(IOException e){
System.out.println(e);
}
}
/**
* Adiciona as palavras que nao existem nos respectivos mapas
*/
private void synchronizeWordMaps(){
for(final Map.Entry<String, Integer> entry :
mapWordFrequencyDoc1.entrySet()){
if(!mapWordFrequencyDoc2.containsKey(entry.getKey())){
mapWordFrequencyDoc2.put(entry.getKey(), 0);
}
}
for(final Map.Entry<String, Integer> entry :
mapWordFrequencyDoc2.entrySet()){
if(!mapWordFrequencyDoc1.containsKey(entry.getKey())){
mapWordFrequencyDoc1.put(entry.getKey(), 0);
}
}
}
/**
*
* @return o angulo theta entre os dois documentos
* Um theta = 0 => documetos identicos
* Um theta = pi/2 => Nao ha palavras em comum
*/
public double getTheta(){
double [] frequency1 =
new double[mapWordFrequencyDoc1.size()];
double [] frequency2 =
new double[mapWordFrequencyDoc2.size()];
int cnt = 0;
for(final Map.Entry<String, Integer> entry :
mapWordFrequencyDoc1.entrySet()){
frequency1[cnt] = entry.getValue();
cnt++;
}
cnt = 0;
for(final Map.Entry<String, Integer> entry :
mapWordFrequencyDoc2.entrySet()){
frequency2[cnt] = entry.getValue();
cnt++;
}
double theta = 0;
for(int i=0,n=frequency1.length;i<n;i++){
theta += frequency1[i]*frequency2[i];
}
return Math.acos(theta /
(getNorm(mapWordFrequencyDoc1) *
getNorm(mapWordFrequencyDoc2)));
}
/**
* Calcula a norma de D(w)
* @param vector
* @return ||D(w)||
*/
private double getNorm(Map<String, Integer> vector ){
double norm = 0.0;
for(final Map.Entry<String, Integer> entry :
vector.entrySet()){
norm += Math.pow(entry.getValue(), 2);
}
return Math.sqrt(norm);
}
/**
* Escreve o resultado para um ficheiro
* @param file
* @param mapWordFrequency
*/
public void writeString(final String file,
final Map<String, Integer> mapWordFrequency){
try {
final PrintWriter output = new PrintWriter(file);
for(final Map.Entry<String, Integer> entry :
mapWordFrequency.entrySet()){
output.println(entry);
}
output.close();
} catch (FileNotFoundException e) {
System.out.println(e);
}
}
}
public class TestDocumentMatcher {
public static final String PATH =
"/home/manuel/Documentos/Books/";
public static final String IN_FILE_1 =
"New_Testament.txt";
public static final String IN_FILE_2 =
"Old_Testament.txt";
public static final String OUT_FILE_1 =
"New_Testament.out";
public static final String OUT_FILE_2 =
"Old_Testament.out";
/**
* @param args
*/
public static void main(String[] args) {
long initTime = Calendar.getInstance().getTimeInMillis();
DocumentMatcher dc =
new DocumentMatcher(PATH + IN_FILE_1,
PATH + IN_FILE_2);
dc.writeString(PATH + OUT_FILE_1,
dc.getMapWordFrequencyDoc1());
dc.writeString(PATH + OUT_FILE_2,
dc.getMapWordFrequencyDoc2());
System.out.println("THETA-->" + dc.getTheta());
System.out.println("Pi/2-->" + Math.PI/2);
long elapsedTime =
Calendar.getInstance().
getTimeInMillis() - initTime;
System.out.println("TIME ELAPSED-->" +
elapsedTime/1000 + " seconds");
}
}
THETA-->0.2850240436473282
Pi/2-->1.5707963267948966
TIME ELAPSED-->1 seconds
1 – Probabilidade e Valor Expectável no Poker Holdem
Publicado: Março 7, 2011 Filed under: Matemática, poker Deixe um comentário »Vamos começar pelos conceitos mais básicos e a partir daí construir as fundações da teoria matemática para (tentar) analisar um jogo de poker na variante Holdem. Vão ser muitos posts
A ideia vai ser apresentar a teoria e de seguida concretizar com um exemplo de uma situação real num jogo de poker.
Probabilidade
Se n tentativas de um experimento (tal como dar uma mão de holdem) produzir n0 ocorrências de um evento x, definimos a probabilidade p de x ocorrer p(x) como
p(x) = lim(n→∞) n0/n
Comecemos com uma pergunta muito simples: Qual é a probabilidade de sair um ás?
Temos 4 ases num baralho de 52 cartas, ou seja, 4 chances em 52: p(A)=4/52=1/13. Note-se que estamos a somar as probabilidades individuais (a probabilidade de sair um ás mais a probabilidade de sair outro ás…) porque são mutuamente exclusivas, i.e., uma carta não pode ser ao mesmo tempo um ás de copas e um ás de espadas.
Eventos Independentes
Consideremos agora os seguintes eventos:
- A carta é uma copa : p(H) = 13/52=1/4 (há 13 copas possíveis num baralho de 52 cartas)
- A carta é um ás: p(A) = 4/52=1/13 (há 4 ases possíveis num baralho de 52 cartas)
mas neste caso, se quisermos calcular a probabilidade de sair um ás ou uma copa, não podemos adicionar como anteriormente porque pode sair uma carta que seja um ás e uma copa (um ás de copas).
Podemos então lançar a ideia de eventos independentes: se a probabilidade de dois eventos ocorrerem for igual ao produto das probabilidades individuais => os eventos são independentes. Neste caso, a probabilidade de A e B ocorrerem chama-se probabilidade conjunta.
A probabilidade conjunta de uma carta ser uma copa e um ás é 1/4 * 1/13
Eventos Dependentes
Para introduzir este conceito, consideremos os seguintes eventos:
- B: a primeira carta a sair é um ás
- A: a segunda carta a sair é um ás
e façamos a seguinte pergunta: qual a probabilidade de A acontecer sabendo que B aconteceu (ou seja, qual é a probabilidade de sair um ás na segunda carta sabendo que já saiu um ás na primeira carta)?. A isto chama-se probabilidade condicional. É fácil ver que os eventos são independentes se a probabilidade condicional de A dado B for igual à probabilidade de A (o fato de B ter acontecido não influencia em nada o A).
Vamos utilizar a seguinte notação:
- p(A U B) = Probabilidade de A ou B ocorrerem
- p(A ∩ B) = Probabilidade de A e B ocorrerem
- p(A|B) = Probabilidade de A ocorrer dado que B já ocorreu
Eventos mutuamente exclusivos:
- p(A U B) = p(A) + p(B) (1.1)
Eventos independentes
- p(A ∩ B) = p(A)p(B) (1.2)
Para todos os eventos
- p(A U B) = p(A) + p(B) - p(A ∩ B) (1.3)
Para eventos dependentes
- p(A ∩ B) = p(A)p(B|A) (1.4)
Podemos ver que a eq.(1.1) é um caso especial da eq.(1.3).
Podemos ver que a eq.(1.2) é um caso especial da eq(1.4). Facilmente concluímos que, para eventos independentes, p(B|A) = p(B).
Com os dois eventos definidos anteriormente, façamos a seguinte pergunta: com que frequência uma mão de holdem contém dois ases?
p(A) = 1/13
p(B) = 1/13
no entanto os eventos são dependentes! Se
p(A ∩ B) = p(A)p(B|A) = 1/13 * 1/17 = 1/221
Antes de avançarmos com uma pergunta bem mais interessante, vamos expor algumas propriedades das probabilidades (para ver algo mais formal sobre este assunto, consultar por exemplo Probability). Definimos C como o evento certo e I o evento impossível. Então,
- 0 ≤p(A) ≤ 1 qualquer que seja o evento A
- p(C) = 1
- p(I) = 0
- p(A) + p(‾A) = 1
Já temos material suficiente para conseguirmos responder à seguinte questão: Tendo uma mão do mesmo naipe, qual a probabilidade de sair um flush no flop?
Com duas cartas no mesmo naipe na mão, sobram 11 cartas do mesmo naipe no baralho. Consideremos os 3 eventos:
- A: a primeira carta do flop ser uma carta de flush
- B: a segunda carta do flop ser uma carta de flush, sabendo que a primeira carta é de flush
- C: a terceira carta do flop ser uma carta de flush sabendo que as duas anteriores são cartas de flush
Na notação definida anteriormente, o que queremos calcular é a p(A ∩ B ∩ C). Temos os seguintes dados:
- p(A) = 11/50 (há 11 cartas possíveis do mesmo naipe que as que temos na mão em 50 cartas)
- p(B\A) = 10/49 (como já sairam 3 cartas do mesmo naipe, sobram 10 cartas em 49 possíveis)
- p(C|(A ∩ B) = 9/48 (há 9 cartas possíveis em 48)
Da eq.(1.4), p(A ∩ B) = p(A)p(B|A) = 11/50 x 10/49 = 11/245
Definimos D = A ∩ B. Então, p(D ∩ C) = p(D)p(C\D)
Reunindo tudo, p(A ∩ B ∩ C) = p(C|(A ∩ B) p(A ∩ B) = 9/48 * 11/245 = 33/3920 (um pouco menos de 1%)
Distribuições de Probabilidades
Quando uma distribuição de probabilidades tem valores numéricos associados às possíveis saídas, podemos encontrar o valor expectável (EV) dessa distribuição, que é o valor de cada saída multiplicada pela sua probabilidade, tudo somado no fim. De outra forma, podemos dizer que para uma distribuição de probabilidades P onde cada uma das n saídas tem um valor xi e uma probabilidade pi, então o valor expectável de P é
<P> = ∑ pi * xi
e esta é uma ideia central do poker: para ganhar é necessário estar constantemente a maximizar o valor expectável.
Há uma propriedade importante do valor expectável: O EV é aditivo, i.e., de n apostas diferentes em linha, o EV é igual à soma dos EVs individuais para cada aposta.
Quatro gatinhos
Publicado: Maio 24, 2010 Filed under: Matemática Deixe um comentário »Sr. Félix: Querida, quantos gatinhos tivemos na última ninhada?
Sra. Félix: Quatro.
Sr. Félix:e quantos machos.
Sra. Félix: ainda não sei.
Sr. Félix: é pouco provável que sejam todos machos.
Sra. Félix: tão-pouco provável que sejam todos fêmeas
Sr. Félix: Não é assim tão difícil de calcular. Há 50% de probabilidades de cada gatinho ser macho ou fêmea. Por isso, o resultado mais provável são dois machos e duas fêmeas.
Será que o pensamento do Sr. Félix está correcto??
Analisemos as combinações possíveis:
| M | M | M | M |
| M | M | M | F |
| M | M | F | M |
| M | M | F | F |
| M | F | M | M |
| M | F | M | F |
| M | F | F | M |
| M | F | F | F |
| F | M | M | M |
| F | M | M | F |
| F | M | F | M |
| F | M | F | F |
| F | F | M | M |
| F | F | M | F |
| F | F | F | M |
| F | F | F | F |
Só dois em dezasseis casos é que são todos do mesmo sexo. Então, a probabilidade disso acontecer será 2/16 = 1/8. É muito pouco provável que sejam todos do mesmo sexo.
Analisemos a hipótese de dois machos e duas fêmeas. Ocorre 6 vezes, ou seja a probabilidade é de 6/16 = 3/8. É, sem dúvida superior a 1/8.
Então, até agora, podemos dizer que é mais provável o casal ter dois machos e duas fêmeas do que quatro machos ou quatro fêmeas.
Mas a parte mais interessante é esta: consideremos 3 de um sexo e um de outro. Esta possibilidade ocorre em oito casos e a respectiva probabilidade é 8/16=1/2 o que é claramente superior à do caso 2-2
Quase toda a gente se surpreende com o facto de em famílias com quatro filhos ser mais provável haver três do mesmo sexo e um do outro do que dois de cada sexo.
Calculadora RPN
Publicado: Junho 17, 2008 Filed under: Java | Tags: Java, reverse polishnotation, rpn Deixe um comentário »Uma expressão aritmética está em notação postfix (também chamada reverse polish notation ou RPN)se cada operador for colocado depois dos seus operandos. Por exemplo 3*(4+5) fica 3 4 5 + *. A notação 3*(4+5) chama-se infix. As expressões quando estão em notação postfix, são mais fáceis de serem processadas pelos computadores do que as expressões infix.
A seguinte classe processa expressões rpn e efectua as respectivas operações aritméticas. O centro nevrálgico do programa é o interface Deque e a classe ArrayDeque. Um deque é uma queue com duas saídas, ou seja, é uma estrutura linear que permite inserções e remoções só nos dois extremos (Deque).
Então, temos:
/**
* Esta classe faz o parsing de expressões em postfix
* (reverse polish notation)
* e efectua as respectivas operações aritméticas
*
* @author Manuel
*
*/
public class RPNCalculator {
private static Deque stack =
new ArrayDeque();
/**
*
* @param input
*/
public static void calculate(String input) {
char ch = input.charAt(0);
if (ch == '+' || ch == '-' || ch == '*'
|| ch == '/') {
double y =
Double.parseDouble(
stack.pop());
double x =
Double.parseDouble(
stack.pop());
double z = 0.0;
switch (ch) {
case '+':
z = x + y;
break;
case '-':
z = x - y;
break;
case '*':
z = x * y;
break;
case '/':
z = x / y;
break;
}
System.out.printf
("\t%.2f %c %.2f = %.2f%n",
x, ch, y, z);
stack.push(new Double(z).toString());
} else {
stack.push(input);
}
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (true) {
String input = in.nextLine();
calculate(input);
if(input.equalsIgnoreCase("q"))
break;
}
}
}
A parte chata é que para fazer uma qualquer operação, temo primeiro que converter a expresão para a sua correspondente versão RPN. Segue um programa que faz isso mesmo:
public class ConvertInfixToPostfix {
public static void main(String[] args){
Deque stack =
new ArrayDeque();
String line =
new Scanner(System.in).nextLine();
System.out.println(line);
Scanner scanner = new Scanner(line);
while(scanner.hasNext()){
if(scanner.hasNextInt()){
System.out.print(
scanner.nextInt() + " ");
}else{
String str = scanner.next();
if("+-*/".indexOf(str) >= 0){
stack.push(str);
}else if(str.equals(")")){
System.out.print(
stack.pop() + " ");
}
}
}
while(!stack.isEmpty()){
System.out.print(stack.pop() + " ");
}
System.out.println();
}
}
Dicas: pesquisa binária e pesquisa sequêncial
Publicado: Junho 5, 2008 Filed under: Java | Tags: algoritmos, Java 2 Comments »
/**
* Pesquisa um numero x num array ordenado.É
* muito mais eficiente do que a pesquisa sequencial
* mas requer que os elementos do array estejam ordenados.
* Ele repetidamente divide a sequência em dois e em cada
* vez restringe a pesquisa à metade que contém o elemento.
*
* Este algoritmo corre no tempo O(log(n)) o que quer dizer que
* em média, o tempo de execução é proporcional ao logaritmo
* do número de elementos no array.
*
*
* @return o indice do numero x ou -1 se não encontrou
*/
public int binarySearch(int[] a, int x){
int low = 0;
int high = a.length;
while(low < high){
int i = (low + high) / 2;
if(a[i] == x){
return i;
}else if(a[i] < x){
low = i + 1;
}else{
high = i;
}
}
return -1;
}
/**
* Pesquisa sequencial (ou pesquisa linear) é o algoritmo
* de pesquisa mais simples mas também é o menos eficiente.
* Ele examina cada elemento sequencialmente começando
* pelo primeiro elemento até chegar ao fim do array(se
* eu estiver à procura de alguém num combóio, utilizo a
* pesquisa sequencial)
*
* Este algoritmo corre no tempo O(n) o que quer dizer que
* em média, o tempo de execução é proporcional ao número
* de elementos no array.
*
* @return o indice do elemento a pesquisar ou 0 se não
* o encontrar
*/
public int sequentialSearch(int[] a, int x){
for(int i = 0, n = a.length; i < n; i++){
if(a[i] == x){
return i;
}
}
return -1;
}
/**
* O algoritmo de binarysearch pode encontrar elementos rapidamente
* num array que já está ordenado o que sugere que devamos ter os
* elementos ordenados no array.No entanto, inserir novos elementos
* num array ordenado é difícil pois temos de transladar os elementos
* maiores para arranjar espaço para o novo elemento.Podemos faze-lo
* assim:
* @param a o array
* @param n o numero de elementos que já estão ordenados no array
* @param x o elemento a ser inserido no meio dos elementos
*/
void insert(int[] a, int n, int x){
/**PreCondicao: a[0] <= ... <= a[n-1], e n < a.length */
/**PosCondicao: a[0] <= ... <= a[n], e x está entre eles */
int i = 0;
while(i < n && a[i] <= x){
i++;
}
System.arraycopy(a, i, a, i+1, n-i);
a[i] = x;
}
Brincadeiras matemáticas 2
Publicado: Junho 5, 2008 Filed under: Matemática | Tags: Matemática, probabilidades 2 Comments »Este também é interessante:
Provar que qualquer número a é igual a um número menor b:
Sejam a, b, c quaisquer números tais que
a = b + c
então, multiplicando os dois lados por a – b, obtemos
a (a – b) = (b + c)(a – b)
a^2 -ab = ba – b^2 + ca – cb
passemos ac para o lado esquerdo,
a^2 – ab -ac = ab – b^2 – bc
factorizar,
a(a – b – c) = b(a – b – c)
dividindo ambos os lados por a – b – c obtemos
a = b
e esta?
Brincadeiras matemáticas
Publicado: Junho 4, 2008 Filed under: Matemática | Tags: Matemática, probabilidades 6 Comments »Eu tenho dois filhos e pelo menos um deles é rapaz.Qual é a probabilidade do outro ser rapaz?
Singleton: duas abordagens
Publicado: Maio 15, 2008 Filed under: Java | Tags: Java, singleton Deixe um comentário »Um Singleton é uma classe que só pode ser instanciada exactamente uma vez.
Temos duas abordagens possíveis. Em ambas mantemos o construtor privado e providenciamos membros to tipo public static de forma a que os clientes
possam aceder a uma e uma só instância da classe.
-
O membro public static é uma variável do tipo final:
//Singleton com uma variável final
public class A {
public static final A INSTANCE = new A();
private A() {}
}
O construtor privado é chamado uma única vez para inicializar a variável A.INSTANCE que é do tipo public static final. -
Providenciamos um método do tipo public static em vez da variável.
// Singleton with static factory
public class A {
private static final A INSTANCE = new A();
private A() {}
public static A getInstance() {
return INSTANCE;
}
}
Todas as chamadas ao método estático a.getInstance()
retornam uma referência ao mesmo objecto e não é possível criar mais nenhuma
instância de A
A primeira abordagem deverá ser usada se tivermos a certeza absoluta que a classe
A será um Singleton para toda a vida enquanto que
a segunda abordagem dá-nos alguma flexibilidade no código, no sentido em que
agora a classe é do tipo Singleton mas mais tarde poderemos alterar esta
propriedade sem termos que alterar a API.
Uma nota sobre a serialização de Singletons:para tornarmos uma classe
singleton serializable, não é condição suficiente implementar o
interface Serializable á declaração da classe, i.e.,
public class A implements Serializable, temos de
implementar o método readResolve() caso contrário,
cada deserialização da instância irá resultar na criação de uma nova instância.
Para prevenir isto, deveremos adicionar o seguinte método à classe
A:
// metodo readResolve para preservar a propriedade singleton
private Object readResolve() throws ObjectStreamException {
/*
* Retorna a unica classe A e deixa o garbage colector
* tratar do resto
*/
return INSTANCE;
}



