product 에 해당하는 ship도, creator 에 해당하는 factory 도 모두 팩토리 매소드 패턴을 적용하여 변경 가능성은 줄이고, 확장가능성은 높인다.
자세히보기
팩토리 매소드 패턴을 적용했을 때의 장점과 단점
기준점이 되는 ship 인터페이스나 shipFactory 클래스는 전혀 변경하지 않고도, 쉽게 새로운 유형인 BlueShip, BlackShip 등 을 추가할 수 있다. 즉, 확장에는 열려있고, 변경에는 닫혀있다.
팩토리 매소드 패턴을 사용하면 확장성이 좋지만, 동시에 해당 패턴을 사용하지 않았을 때보다 클래스 구조도는 훨씬 복잡해지게 된다.
확장에 열려있고, 변경에 닫혀있는 객체지향의 원칙이란
새로운 유형을 추가할 때, 기준이 되는 인터페이스나 클래스의 코드는 전혀 변경하지 않고, 기존의 유형에 상관없이 새로운 유형을 쉽게 추가할수 있는 구조를 말한다.
기존 코드를 변경하지 않고, 기존 것을 확장하여 새로운 것을 만드는 것이 쉽다.
자바 8에 추가된 default 매소드에 대해서 설명
자바 8부터는 interface 에서 default 키워드를 붙인 매소드를 미리 구현해놓아 하위 클래스들에서 새롭게 구현하지 않을 경우, 호출되는 디폴트 매소드로 이용할 수 있다.
참고로 자바 9부터는 interface에서 private 키워드를 붙여 매소드를 구현해놓을 수 있다.
따라서 자바 9 이후로는 사실 추상매소드 쓸 일이 별로 없다.
실제 예시
자바
실제 예시에서는 위의 예시 1에서처럼 단순한 팩토리 패턴이 많이 쓰인다. 매개변수의 값에 따라서 내부에서 분기처리하여 서로 다른 인스턴스를 반환하는 구조.
예를 들면, 자바의 java.lang.Calendar, java.lang.NumberFormat 등이 그러하다.
public class SimpleFactory {
public Object createProduct(String name) {
if (name.equals("whiteship")) {
return new Whiteship();
} else if (name.equals("blackship")) {
return new Blackship();
}
throw new IllegalArgumentException();
}
}
public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {
...
private static Calendar createCalendar(TimeZone zone, Locale aLocale) {
CalendarProvider provider = LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale).getCalendarProvider();
if (provider != null) {
try {
return provider.getInstance(zone, aLocale);
} catch (IllegalArgumentException var7) {
}
}
Calendar cal = null;
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
byte var6 = -1;
switch(caltype.hashCode()) {
case -1581060683:
if (caltype.equals("buddhist")) {
var6 = 0;
}
break;
case -752730191:
if (caltype.equals("japanese")) {
var6 = 1;
}
break;
case 283776265:
if (caltype.equals("gregory")) {
var6 = 2;
}
}
switch(var6) {
case 0:
cal = new BuddhistCalendar(zone, aLocale);
break;
case 1:
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case 2:
cal = new GregorianCalendar(zone, aLocale);
}
}
}
if (cal == null) {
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja" && aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return (Calendar)cal;
}
}
스프링
spring bean factory
object 타입의 product 를 만드는 BeanFactory라는 Creator
public class SpringBeanFactoryExample {
public static void main(String[] args) {
BeanFactory xmlFactory = new ClassPathXmlApplicationContext("config.xml");
String hello = xmlFactory.getBean("hello", String.class);
System.out.println(hello);
BeanFactory javaFactory = new AnnotationConfigApplicationContext(Config.class);
String hi = javaFactory.getBean("hello", String.class);
System.out.println(hi);
}
}