Service Factory - opinião

Olá pessoal. [desculpem pelos problemas de acentuacao no texto, mas ainda nao configurei meu teclado…]

Gostaria de discutir com os colegas uma solucao que adotei para meu pequeno projeto.
Neste projeto estou usando apenas JSF2 para camada de apresentacao, Hibernate - JPA, e Apache Shiro para a seguranca.

Bom, mesmo com poucos frameworks, desenhei a aplicacao em camadas separando a camada de apresentacao (junto com controllers no caso do JSF) e a camada de services.
Para manter uma boa pratica de desenho, os services estao orientados a interface (nao sei se posso escrever assim mesmo - orientado a interfaces).
Possuo a seguinte estrutura:

pacote.services.[nome referente ao negocio].api -> possui as interfaces
pacote.services.[nome referente ao negocio].impl -> possui as implementacoes

Como eu nao quis usar o spring, criei uma classe que me devolve uma instancia de um service dada uma interface enviada como parametro.
Este “ServiceFactory” entao possui (por enquanto) um metodo estático que retorna a classe concreta para a interface.

Desta forma, em meus backing beans, utilizo apenas a interface gerando um menor acoplamento.
Não utilizei DI pois é realmente um projeto pequeno e não acredito que há necessidade.
Outro fato é que tentei buscar um pouco de “convention over configuration”.

Observando o código abaixo e o cenário descrito gostaria de saber se acham muito ruim esta abordagem, o que poderia melhorar e enfim, no final das contas me ajudar a saber se estou tomando o caminho certo. :lol:

package br.com.xxx.service.support;

import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import br.com.xxx.service.support.exception.ServiceFactoryException;

/**
 * Provides a factory to obtain service implementations by convention over configuration.
 * @author adolfo
 */
public class ServiceFactory {

	/**
	 * Retrieves an implementation instance of the interface sent by type parameter.
	 * @param <T>	- Interface definition
	 * @param type	- Interface class definition
	 * @return		- implementation of the type
	 * @throws 		ServiceFactoryException
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getService(Class<? extends T> type) throws ServiceFactoryException {
		
		// retrieving the implementation service name.
		String[] qualifiedName = type.getName().split("\\.");
		List<String> qualifiedNameList = Arrays.asList(qualifiedName);
		
		int classNameIndex = qualifiedNameList.size() - 1;
		int switchIndex    = qualifiedNameList.size() - 2;
		
		String className = qualifiedNameList.get(classNameIndex);
		String newName   = className.substring(1).concat("Impl");
		
		qualifiedNameList.set(switchIndex, "impl");
		qualifiedNameList.set(classNameIndex, newName);
		
		String qualifiedNameString = StringUtils.join(qualifiedName, ".");
		
		// building the implementation class qualified name.
		try {
			return (T) Class.forName(qualifiedNameString).newInstance();
		} catch (Exception e) {
			throw new ServiceFactoryException(e);
		} 
		
	}
	
}

Através da classe acima posso “chamar” um service conforme o código abaixo:

...
		IProductsService service = ServiceFactory.getService(IProductsService.class);
		System.out.println(service.getProductName());
...

Obrigado.

Acho que está bem desenvolvido, você utilizou bem os conceitos de Abstract Factory com reflection. No entanto, de qualquer forma você está buscando as implementações do seu serviço em um pacote no mesmo projeto (mesma JVM), seria mais ou menos a mesma coisa que fazer :

IProductsService service = new ProductsServiceImpl();

Como não há nenhum recurso de injeção pelo container (EJB3 ou Spring), na prática não faz muito sentido usar isso, você apenas sofisticou a factory dos seus services. Tente criar algo mais declarativo (parecido com o Spring antigo), com um XML ou um properties onde você linka a interface com a implementação desejada. Acho que vai ser algo mais útil pra utiliza na prática.

Outra Recomendação: Use um servidor com suporte a EJB3 e CDI e anote suas implementações com @Stateless. No código onde você chama o serviço ficaria:

@EJB
IProductsService service;
System.out.println(service.getProductName());  

Você tira a responsabilidade do seu código de buscar a implementação, deixando isso pro container. Acho que isso é o verdadeiro espírito da coisa :slight_smile: