이 예에서 AnimalFactory와 ColorFactory의 두 가지 Factory를 구현하겠습니다. 그런 다음 AbstractFactory를 사용하여 접근 권한을 관리합니다.
먼저 Animal 인터페이스를 생성합니다.
public interface Animal {
String getAnimal();
String makeSound();
}
그리고 Animal인터페이스의 구현체인 Duck클래스를 생성합니다.
public class Duck implements Animal {
@Override
public String getAnimal() {
return "Duck";
}
@Override
public String makeSound() {
return "Squeks";
}
}
또한 이러한 방식으로 동물 인터페이스(Dog, Bear 등)를 보다 구체적으로 구현할 수 있습니다.
이제 여러 제품군을 준비했으므로 이들을 위한 AbstractFactory 인터페이스를 만들 수 있습니다.
public interface AbstractFactory<T> {
T create(String animalType);
}
그다음으로 팩토리 메서드 패턴을 활용하여 AnimalFactory를 생성하겠습니다.
public class AnimalFactory implements AbstractFactory<Animal> {
@Override
public Animal create(String animalType) {
if ("Dog".equalsIgnoreCase(animalType)) {
return new Dog();
} else if ("Duck".equalsIgnoreCase(animalType)) {
return new Duck();
}
return null;
}
}
이 모든 설정이 완료되면 GetFactory()이 제공하는 인수에 따라 AnimalFactory를 제공하는 FactoryProvider 클래스를 만들 것입니다.
public class FactoryProvider {
public static AbstractFactory getFactory(String choice){
if("Animal".equalsIgnoreCase(choice)){
return new AnimalFactory();
}
else if("Color".equalsIgnoreCase(choice)){
return new ColorFactory();
}
return null;
}
}
JDK에서 추상 팩토리 메서드 패턴의 예로 DocumentBuilderFactory클래스의 newInstance 메소드가 해당됩니다. DocumentBuilderFactory는 xml파일을 Document로 파싱해오는 예제코드 입니다.
import java.io.ByteArrayInputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
public class DocumentBuilderExample {
public static void main(String[] args) throws Exception {
String testXml = "<note><to>Receiver</to><from>Sender</from><heading>SubjectHeading</heading><body>Body text under the body tag.</body></note>";
ByteArrayInputStream ba = new ByteArrayInputStream(testXml.getBytes());
DocumentBuilderFactory absFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder factory = absFactory.newDocumentBuilder();
Document doc = factory.parse(ba);
doc.getDocumentElement().normalize();
System.out.println("Root Node Name is ::: " + doc.getDocumentElement().getNodeName());
System.out.println("get abstract factory class name is ::: " + absFactory.getClass());
System.out.println("get abstract factory class name is ::: " + factory.getClass());
}
}
DocumentBuilderFactory가 시스템 속성을 읽어 인스턴스를 생성하기때문에 Client입장에서는 내부적인 구현을 알 필요가 없이 newInstance() 메서드를 통해 팩터리를 생성하면 됩니다.
public abstract class DocumentBuilderFactory {
//.....
public static DocumentBuilderFactory newInstance() {
return FactoryFinder.find(
/* The default property name according to the JAXP spec */
DocumentBuilderFactory.class, // "javax.xml.parsers.DocumentBuilderFactory"
/* The fallback implementation class name */
DEFAULT_IMPL);
//.....
}
}
FactoryFinder의 find메서드를 따라가보면 3가지 분기를 통해 인스턴스를 생성하고 있습니다.
static <T> T find(Class<T> type, String fallbackClassName)
throws FactoryConfigurationError
{
final String factoryId = type.getName();
dPrint(()->"find factoryId =" + factoryId);
// Use the system property first
try {
String systemProp = SecuritySupport.getSystemProperty(factoryId);
if (systemProp != null) {
dPrint(()->"found system property, value=" + systemProp);
return newInstance(type, systemProp, null, true);
}
}
}
처음에 시스템 속성이 null이 아닐경우 시스템 속성을 통해서 인스턴스를 생성하고 있습니다.
try {
if (firstTime) {
synchronized (cacheProps) {
if (firstTime) {
String configFile = SecuritySupport.getSystemProperty("java.home") + File.separator +
"conf" + File.separator + "jaxp.properties";
File f = new File(configFile);
firstTime = false;
if (SecuritySupport.doesFileExist(f)) {
dPrint(()->"Read properties file "+f);
cacheProps.load(SecuritySupport.getFileInputStream(f));
}
}
}
}
final String factoryClassName = cacheProps.getProperty(factoryId);
if (factoryClassName != null) {
dPrint(()->"found in ${java.home}/conf/jaxp.properties, value=" + factoryClassName);
return newInstance(type, factoryClassName, null, true);
}
}
다음으로 시스템 속성이 null일 경우 $java.home/conf/jaxp.properties이 경로에 있는 설정들을 읽어서 인스턴스를 생성하려고 합니다.
T provider = findServiceProvider(type);
if (provider != null) {
return provider;
}
if (fallbackClassName == null) {
throw new FactoryConfigurationError(
"Provider for " + factoryId + " cannot be found");
}
dPrint(()->"loaded from fallback value: " + fallbackClassName);
return newInstance(type, fallbackClassName, null, true);
마지막으로 Jar 서비스 Provider를 찾아서 Provider를 리턴하거나 Provider가 없다면 newInstance()를 호출할때 기본설정이 되어있었던 DEFAULT_IMPL인
private static final String DEFAULT_IMPL =
"com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl";
DocumentBuilderFactoryImpl을 생성하여 리턴합니다.
객체 생성을 캡슐화해서 코드와 구상 형식을 분리시킬 수 있게 해 줍니다. 따라서 애플리케이션의 결합을 느슨하게 만들고, 특정 구현에 덜 의존하도록 만들 수 있습니다.