Singleton

Intenção

Garantir que uma classe tenha somente uma instância e fornecer um ponto global de acesso para a mesma.

Vantagens

Desvantagens

Exemplos

Exemplo #1

Deseja-se implementar uma engine (biblioteca) para criar jogos de batalhas espaciais utilizando o conceito de Universo, que deve ser único por definição. Assim, a engine deve garantir que um único exemplar de Universo seja criado em um jogo. A classe Universe age como um Singleton. Essa classe não tem um construtor público, então a única maneira de obter seu objeto é chamando o método estático getSoleInstance. Esse método coloca o primeiro objeto criado em cache e o retorna em todas as chamadas subsequentes. 

Diagrama de Classe

Exemplo #1 - Diagrama

Participantes

Singleton(Universe):

Define uma operação getSoleInstance que permite aos clientes acessarem sua única instância. 

Exemplo #2

Seja um processador de requisições (Request Processor) cuja responsabilidade é receber uma requisição e processá-la sobre a camada de objetos de negócio (Product, Customer) do domínio (domain model). Um objeto Request é criado para representar a requisição, encapsulando sua lógica de execução. Assim, para cada requisição, o correspondente objeto Request deve se criado para executá-la. Como não é necessário armazenar o histórico de execução das requisições, é possível ter todos os objetos Request sem estado intrínseco específico da requisição, e compartilhar objetos Request entre requisições do mesmo tipo, diminuindo o overhead de criação e gerencimento destes objetos, que podem ser muitos. Para tal, valores específicos da requisição precisam ser modelados como estado extrínseco do objeto Request, sendo passados como parâmetros. As classes dos objetos Request (EditProductRequest, EditCustomerRequest) garantem que esses objetos sejam compartilhados e únicos, viabilizando o reúso. Esses objetos são criados sob demanda no primeiro acesso. 

Diagrama de Classe

Exemplo #2 - Diagrama

Participantes

Singleton(RequestProcessor, RequestFactory, EditProductRequest, EditCustomerRequest):

Define uma operação getSoleInstance que permite aos clientes acessarem sua única instância. 

Código



	CommerceSingleton
	
	
	
	
		
			org.eclipse.jdt.core.javabuilder
			
			
		
	
	
		org.eclipse.jdt.core.javanature
	

eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=11
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=11
Êþº¾71(br/uff/commerce/domain/customer/Customerjava/lang/ObjectidJnameLjava/lang/String;(JLjava/lang/String;)VCode

	()V	
setName(Ljava/lang/String;)VLineNumberTableLocalVariableTablethis*Lbr/uff/commerce/domain/customer/Customer;getId()JgetName()Ljava/lang/String;	
 "!java/lang/String#trim
 %&'isEmpty()Z)"java/lang/IllegalArgumentException+Name is mandatory.
(-	
StackMapTable
SourceFile
Customer.java!	
Y*·*µ*-¶±
	 /*´­/*´°h+Æ
+¶¶$™
»(Y*·,¿*+µ±.	/0
Êþº¾7"2br/uff/commerce/domain/customer/CustomerRepositoryjava/lang/ObjectsoleInstance4Lbr/uff/commerce/domain/customer/CustomerRepository;()VCode	LineNumberTableLocalVariableTablegetSoleInstance6()Lbr/uff/commerce/domain/customer/CustomerRepository;

StackMapTable
thisinsert-(Lbr/uff/commerce/domain/customer/Customer;)Vproduct*Lbr/uff/commerce/domain/customer/Customer;removeupdatefindById-(J)Lbr/uff/commerce/domain/customer/Customer;idJ
SourceFileCustomerRepository.java!
	%³
±

		E²
Ç
»Y·³
²
°
	3*·±

	5±
	5±!
	5±&
	6°+
 !
Êþº¾7f3br/uff/commerce/domain/customer/EditCustomerRequestjava/lang/Objectbr/uff/commerce/request/RequestNAMELjava/lang/String;
ConstantValueeditCustomersoleInstance5Lbr/uff/commerce/domain/customer/EditCustomerRequest;()VCode	
LineNumberTableLocalVariableTablegetSoleInstance7()Lbr/uff/commerce/domain/customer/EditCustomerRequest;

StackMapTable
thisexecute(Ljava/util/Map;)V
Exceptions!(br/uff/commerce/request/RequestException	Signature8(Ljava/util/Map;)V%
customerId')(
java/util/Map*+get&(Ljava/lang/Object;)Ljava/lang/Object;-java/lang/Long
/102br/uff/commerce/domain/customer/CustomerRepository26()Lbr/uff/commerce/domain/customer/CustomerRepository;
,456	longValue()J
/89:findById-(J)Lbr/uff/commerce/domain/customer/Customer;<java/lang/StringBuilder>Customer with id 
;@A(Ljava/lang/String;)V
;CDEappend-(Ljava/lang/Object;)Ljava/lang/StringBuilder;G not found.
;IDJ-(Ljava/lang/String;)Ljava/lang/StringBuilder;
;LMNtoString()Ljava/lang/String;
 @QnameSjava/lang/String
UWV(br/uff/commerce/domain/customer/CustomerXAsetName
/Z[\update-(Lbr/uff/commerce/domain/customer/Customer;)V
parametersLjava/util/Map;Ljava/lang/Long;subject*Lbr/uff/commerce/domain/customer/Customer;LocalVariableTypeTable5Ljava/util/Map;
SourceFileEditCustomerRequest.java!	


%³±	E²Ç
»Y·³²°3*·±

 "#ÞO+$¹&À,M¸.,¶3¶7N-Ç » Y»;Y=·?,¶BF¶H¶K·O¿-+P¹&ÀR¶T¸.-¶Y±*
#&%()(*4)8,G.N0*O
O]^C%_8`abO]cý8,Ude
Êþº¾7f1br/uff/commerce/domain/product/EditProductRequestjava/lang/Objectbr/uff/commerce/request/RequestNAMELjava/lang/String;
ConstantValueeditProductsoleInstance3Lbr/uff/commerce/domain/product/EditProductRequest;()VCode	
LineNumberTableLocalVariableTablegetSoleInstance5()Lbr/uff/commerce/domain/product/EditProductRequest;

StackMapTable
thisexecute(Ljava/util/Map;)V
Exceptions!(br/uff/commerce/request/RequestException	Signature8(Ljava/util/Map;)V%	productId')(
java/util/Map*+get&(Ljava/lang/Object;)Ljava/lang/Object;-java/lang/Long
/100br/uff/commerce/domain/product/ProductRepository24()Lbr/uff/commerce/domain/product/ProductRepository;
,456	longValue()J
/89:findById+(J)Lbr/uff/commerce/domain/product/Product;<java/lang/StringBuilder>Product with id 
;@A(Ljava/lang/String;)V
;CDEappend-(Ljava/lang/Object;)Ljava/lang/StringBuilder;G not found.
;IDJ-(Ljava/lang/String;)Ljava/lang/StringBuilder;
;LMNtoString()Ljava/lang/String;
 @QnameSjava/lang/String
UWV&br/uff/commerce/domain/product/ProductXAsetName
/Z[\update+(Lbr/uff/commerce/domain/product/Product;)V
parametersLjava/util/Map;Ljava/lang/Long;subject(Lbr/uff/commerce/domain/product/Product;LocalVariableTypeTable5Ljava/util/Map;
SourceFileEditProductRequest.java!	


%³±	E²Ç
»Y·³²°3*·±

 "#ÞO+$¹&À,M¸.,¶3¶7N-Ç » Y»;Y=·?,¶BF¶H¶K·O¿-+P¹&ÀR¶T¸.-¶Y±*
#&%()(*4)8,G.N0*O
O]^C%_8`abO]cý8,Ude
Êþº¾71&br/uff/commerce/domain/product/Productjava/lang/ObjectidJnameLjava/lang/String;(JLjava/lang/String;)VCode

	()V	
setName(Ljava/lang/String;)VLineNumberTableLocalVariableTablethis(Lbr/uff/commerce/domain/product/Product;getId()JgetName()Ljava/lang/String;	
 "!java/lang/String#trim
 %&'isEmpty()Z)"java/lang/IllegalArgumentException+Name is mandatory.
(-	
StackMapTable
SourceFileProduct.java!	
Y*·*µ*-¶±
	 /*´­/*´°h+Æ
+¶¶$™
»(Y*·,¿*+µ±.	/0
Êþº¾7"0br/uff/commerce/domain/product/ProductRepositoryjava/lang/ObjectsoleInstance2Lbr/uff/commerce/domain/product/ProductRepository;()VCode	LineNumberTableLocalVariableTablegetSoleInstance4()Lbr/uff/commerce/domain/product/ProductRepository;

StackMapTable
thisinsert+(Lbr/uff/commerce/domain/product/Product;)Vproduct(Lbr/uff/commerce/domain/product/Product;removeupdatefindById+(J)Lbr/uff/commerce/domain/product/Product;idJ
SourceFileProductRepository.java!
	%³
±

		E²
Ç
»Y·³
²
°
	3*·±

	5±
	5±!
	5±&
	6°+
 !
Êþº¾7br/uff/commerce/request/Requestjava/lang/Objectexecute(Ljava/util/Map;)V
Exceptions	(br/uff/commerce/request/RequestException	Signature8(Ljava/util/Map;)V
SourceFileRequest.java

Êþº¾7(br/uff/commerce/request/RequestExceptionjava/lang/ExceptionserialVersionUIDJ
ConstantValue()VCode

LineNumberTableLocalVariableTablethis*Lbr/uff/commerce/request/RequestException;(Ljava/lang/String;)V

messageLjava/lang/String;
SourceFileRequestException.java!
3*·
±

>*+·±

Êþº¾7O0br/uff/commerce/requestprocessing/RequestFactoryjava/lang/ObjectsoleInstance2Lbr/uff/commerce/requestprocessing/RequestFactory;()VCode	LineNumberTableLocalVariableTablegetSoleInstance4()Lbr/uff/commerce/requestprocessing/RequestFactory;

StackMapTable
this
createRequest5(Ljava/lang/String;)Lbr/uff/commerce/request/Request;
java/lang/Stringtrim()Ljava/lang/String;
 !isEmpty()Z#"java/lang/IllegalArgumentException%Request name is mandatory.
"'((Ljava/lang/String;)V*editProduct
,-.equals(Ljava/lang/Object;)Z
0211br/uff/commerce/domain/product/EditProductRequest35()Lbr/uff/commerce/domain/product/EditProductRequest;5editCustomer
7983br/uff/commerce/domain/customer/EditCustomerRequest:7()Lbr/uff/commerce/domain/customer/EditCustomerRequest;<java/lang/StringBuilder>Request object not found for 
;'
;ABCappend-(Ljava/lang/String;)Ljava/lang/StringBuilder;
;EFtoStringrequestNameLjava/lang/String;result!Lbr/uff/commerce/request/Request;Lbr/uff/commerce/request/Request
SourceFileRequestFactory.java!
	%³
±

		E²
Ç
»Y·³
²
°
	/*·±
	ÚR+Æ
+¶¶™
»"Y$·&¿+)¶+™
¸/M§++4¶+™
¸6M§»"Y»;Y=·?+¶@¶D·&¿,°*
 !!("1#8%<&L%P(
4RRGH%IJ5IJPIJ	üKMN
Êþº¾7:2br/uff/commerce/requestprocessing/RequestProcessorjava/lang/ObjectsoleInstance4Lbr/uff/commerce/requestprocessing/RequestProcessor;factory2Lbr/uff/commerce/requestprocessing/RequestFactory;()VCode	
LineNumberTableLocalVariableTablegetSoleInstance6()Lbr/uff/commerce/requestprocessing/RequestProcessor;


StackMapTable

0br/uff/commerce/requestprocessing/RequestFactory4()Lbr/uff/commerce/requestprocessing/RequestFactory;	thisprocess$(Ljava/lang/String;Ljava/util/Map;)V
Exceptions#(br/uff/commerce/request/RequestException	SignatureJ(Ljava/lang/String;Ljava/util/Map;)V
'()
createRequest5(Ljava/lang/String;)Lbr/uff/commerce/request/Request;+-,br/uff/commerce/request/Request./execute(Ljava/util/Map;)VrequestNameLjava/lang/String;
parametersLjava/util/Map;request!Lbr/uff/commerce/request/Request;LocalVariableTypeTable5Ljava/util/Map;
SourceFileRequestProcessor.java!
	
%³±	E²Ç
»Y·³²°
>*·*¸µ± !"$%s*´+¶&N-,¹*±"	#$*0123	4562789
package br.uff.commerce.domain.customer;

public class Customer
{
	private long id;
	private String name;
	
	public Customer(long id, String name)
	{
		this.id = id;
		this.setName(name);
	}
	
	public long getId()
	{
		return this.id;
	}
	
	public String getName()
	{
		return this.name;
	}
	
	public final void setName(String name)
	{
		if (name == null || name.trim().isEmpty())
			throw new IllegalArgumentException("Name is mandatory.");
		
		this.name = name;
	}	

}
package br.uff.commerce.domain.customer;

/**
 * Repositório (persistência) de Cliente
 * 
 *
 */
public class CustomerRepository
{
	private static CustomerRepository soleInstance = null;
	
	public static CustomerRepository getSoleInstance()
	{
		if (CustomerRepository.soleInstance == null)
			CustomerRepository.soleInstance = new CustomerRepository();
		
		return CustomerRepository.soleInstance;
	}
	
	private CustomerRepository()
	{
		
	}
	
	public void insert(Customer product)
	{
		//insere novo cliente
	}
	
	public void remove(Customer product)
	{
		//remove cliente
	}
	
	public void update(Customer product)
	{
	   //atualiza cliente	
	}
	
	public Customer findById(long id)
	{
		//busca e retorna cliente pelo respectivo id
		return null;
	}
}
package br.uff.commerce.domain.customer;

import java.util.Map;

import br.uff.commerce.request.Request;
import br.uff.commerce.request.RequestException;

/**
 * Singleton
 *
 */
public class EditCustomerRequest implements Request
{
	public final static String NAME = "editCustomer";
	
	private static EditCustomerRequest soleInstance = null;
	
	public static EditCustomerRequest  getSoleInstance()
	{
		if (EditCustomerRequest.soleInstance == null)
			EditCustomerRequest.soleInstance = new EditCustomerRequest();
		
		return EditCustomerRequest.soleInstance;
	}
	
	private EditCustomerRequest() 
	{ 
		
	}

	@Override
	public void execute(Map parameters) throws RequestException
	{
		
		Long customerId = (Long) parameters.get("customerId");
		
		Customer subject = 
				CustomerRepository.getSoleInstance().findById(customerId);
		
		if (subject == null)
			throw new RequestException("Customer with id "+ 
		                                customerId +" not found.");
			
		subject.setName((String) parameters.get("name"));
		
		CustomerRepository.getSoleInstance().update(subject);
		
	}

}
package br.uff.commerce.domain.product;

import java.util.Map;

import br.uff.commerce.request.Request;
import br.uff.commerce.request.RequestException;

/**
 * Singleton.
 *
 */
public class EditProductRequest implements Request
{
	public final static String NAME = "editProduct";
	
	private static EditProductRequest soleInstance = null;
	
	public static EditProductRequest  getSoleInstance()
	{
		if (EditProductRequest.soleInstance == null)
			EditProductRequest.soleInstance = new EditProductRequest();
		
		return EditProductRequest.soleInstance;
	}
	
	private EditProductRequest() 
	{ 
		
	}

	@Override
	public void execute(Map parameters) throws RequestException
	{
		
		Long productId = (Long) parameters.get("productId");
		
		Product subject = 
				ProductRepository.getSoleInstance().findById(productId);
		
		if (subject == null)
			throw new RequestException("Product with id "+ 
		                                productId +" not found.");
			
		subject.setName((String) parameters.get("name"));
		
		ProductRepository.getSoleInstance().update(subject);
		
	}

}
package br.uff.commerce.domain.product;

public class Product
{
	private long id;
	private String name;
	
	public Product(long id, String name)
	{
		this.id = id;
		this.setName(name);
	}
	
	public long getId()
	{
		return this.id;
	}
	
	public String getName()
	{
		return this.name;
	}
	
	public final void setName(String name)
	{
		if (name == null || name.trim().isEmpty())
			throw new IllegalArgumentException("Name is mandatory.");
		
		this.name = name;
	}	

}
package br.uff.commerce.domain.product;

/**
 * Repositório (persistência) de Produto
 * 
 *
 */
public class ProductRepository
{
	private static ProductRepository soleInstance = null;
	
	public static ProductRepository getSoleInstance()
	{
		if (ProductRepository.soleInstance == null)
			ProductRepository.soleInstance = new ProductRepository();
		
		return ProductRepository.soleInstance;
	}
	
	private ProductRepository()
	{
		
	}
	
	public void insert(Product product)
	{
		//insere novo produto
	}
	
	public void remove(Product product)
	{
		//remove produto
	}
	
	public void update(Product product)
	{
	   //atualiza produto	
	}
	
	public Product findById(long id)
	{
		//busca e retorna produto pelo respectivo id
		return null;
	}
}
package br.uff.commerce.request;

import java.util.Map;

/**
 * Singleton
 *
 */
public interface Request
{
	public void execute(Map parameters) throws RequestException;
}
package br.uff.commerce.request;

/**
 * Indica algum problema na execução de uma requisição.
 *
 */
public class RequestException extends Exception
{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	public RequestException()
	{
		super();
		
	}

	public RequestException(String message)
	{
		super(message);
		
	}

}
package br.uff.commerce.requestprocessing;

import br.uff.commerce.domain.customer.EditCustomerRequest;
import br.uff.commerce.domain.product.EditProductRequest;
import br.uff.commerce.request.Request;

/**
 * Factory parametrizado e Singleton.
 *
 */
public class RequestFactory
{
	private static RequestFactory soleInstance = null;
	
	public static RequestFactory getSoleInstance()
	{
		if (RequestFactory.soleInstance == null)
			RequestFactory.soleInstance = new RequestFactory();
		
		return RequestFactory.soleInstance;
	}
	
	private RequestFactory() {}
	
	public Request createRequest(String requestName)
	{
		if (requestName == null || requestName.trim().isEmpty())
			throw new IllegalArgumentException("Request name is mandatory.");
		
		Request result;
		
		if (requestName.equals(EditProductRequest.NAME))
			result = EditProductRequest.getSoleInstance();
		else if (requestName.equals(EditCustomerRequest.NAME))
			result = EditCustomerRequest.getSoleInstance();
		else 
			throw new IllegalArgumentException(
					"Request object not found for "+ requestName);
			
		return result;
	}
	
}
package br.uff.commerce.requestprocessing;

import java.util.Map;

import br.uff.commerce.request.Request;
import br.uff.commerce.request.RequestException;

/**
 * Singleton
 *
 */
public class RequestProcessor
{
	private static RequestProcessor soleInstance = null;
	
	public static RequestProcessor getSoleInstance()
	{
		if (RequestProcessor.soleInstance == null)
			RequestProcessor.soleInstance = new RequestProcessor();
		
		return RequestProcessor.soleInstance;
	}
	
	private RequestFactory factory;
	
	private RequestProcessor()
	{
		this.factory = RequestFactory.getSoleInstance();
	}
	
	public void process(String requestName, Map parameters) 
			throws RequestException
	{
		Request request = this.factory.createRequest(requestName);
		request.execute(parameters);
	}
	
}
Clique aqui para fazer o download do código completo de implementação deste Design Pattern.

Padrões Relacionados

Este Padrão pode ser usado para resolver os seguintes problemas: