본문 바로가기
Today I learned

2021 08 10 - Factory Method

by soheemon 2021. 8. 10.

언제 사용하면 좋을까?

 

작업해야 하는 객체의 정확한 유형과 종속성을 알 수 없는 경우

라이브러리 또는 프레임워크의 사용자에게 내부 구성요소를 확장하는 방법을 제공하려는 경우

 

하나의 상황을 가정해보자.

(말도안되는 극단적인 가정이지만 이해를 위해서 은근슬쩍 넘어가자.)

 

가정: 새로 시작한 프로젝트에 로거를 도입해야 한다.

'파일로 Exception정보를 기록한다'가 요구사항으로 들어왔다.

황소희는 룰루랄라 코드를 작성했다.

 

//황소희가 작성한 로거 클래스.
public class FileLogger {

	public FileLogger(String path) {
		/* 파일 로거 셋팅..*/
	}

	public void write(String msg) {
		System.out.println(msg);
	}

}

// client Code. 다른 개발자들에 의해 실제 프로젝트 안에서 사용될 코드들.
public class Client {
	private static FileLogger filelogger;
	public static void main(String[] args) {  
		loggerConfigure();
		
		try{
			throw new NumberFormatException();
		}catch(Exception e){
			StringBuilder sb = new StringBuilder();
			sb.append(e.toString());
			for(StackTraceElement se : e.getStackTrace()){
				sb.append(se.toString());
			}
			writeLog(sb.toString());			
		}
		
	}  
	
	static void loggerConfigure(){
		filelogger = new FileLogger("경로");
	}
	
	static void writeLog(String msg){
		filelogger.write(msg);
	}
}

 

 

그런데! 반나절만에 기획자가 연락이 와서

환경에 따라서 소켓으로 로그를 전송하는 기능도 넣어달라고 한다.

 

정신이 아득해진다... 어떻게 고쳐야 할까?

 

FileLogger와 SocketLogger 처럼 각각의 클래스를 갖되,

어떤 클래스가 와도 다 참조가 가능한 super abstract class를 만들자.

* 두개 로더의 부모클래스 Logger 생성

 

API

//superClass
public abstract class Logger {
	//두가지는 Logger를 상속받으면 꼭 있어야 하는 메서드이다. 각각의 Custom을 위해 abstarct로 구현하게끔 했다.
	public abstract void initLogger();
	public abstract void write(String msg);
	
}

// 소켓통신시 이용하는 class
public class SocketLogger extends Logger {

	@Override
	public void initLogger() {
		System.out.println("socket Connect..");
	}

	@Override
	public void write(String msg) {
		System.out.println("socket:: " + msg);
		
	}

}

// 파일에 작성할때 사용하는 class
public class FileLogger extends Logger{

	@Override
	public void initLogger() {
		System.out.println("File open..");
	}

	@Override
	public void write(String msg) {
		System.out.println("file :: " + msg);
	}

}

 

Client Code

public class Client {
	private static Logger logger;
	public static void main(String[] args) {  
		loggerConfigure();
		
		try{
			throw new NumberFormatException();
		}catch(Exception e){
			StringBuilder sb = new StringBuilder();
			sb.append(e.toString());
			for(StackTraceElement se : e.getStackTrace()){
				sb.append(se.toString());
			}
			writeLog(sb.toString());			
		}
		
	}  
	
	static void loggerConfigure(){
		boolean isNetworkConntected = true;
		logger = isNetworkConntected ? new SocketLogger() : new FileLogger();
	}
	static void writeLog(String msg){
		logger.write(msg);
	}
}

+ abstract method를 사용하는 이유

- 반드시 있어야 하는 메서드이지만, 서브 클래스간 구현이 달라져서. (file open, socket connection 등...)

 

뭔가 동작이 달라질것 같은경우. abstract super class를 하나 만든다음에

그것을 상속하는 subclass를 사용해야 겠다.

나중에 새로운 로깅방식이 생겨도 확장이 가능하니까.

 

유지보수가 용이한 코드라고 할수있겠다 (급마무리)

 

jdk에서 살펴보기

대표적인 FactoryMethod의 예를 하나 살펴보자.

TimeZone 및 locale을 사용하여 각각 다른 Calendar(를 상속하는) 객체를 생성 후 반환해준다.

 

java.util.Calendar#getInstance()가 한번 감싸고 있고 createCalendar가 FactoryMethod라고 할수있게따.

    /**
     * Gets a calendar using the default time zone and locale. The
     * <code>Calendar</code> returned is based on the current time
     * in the default time zone with the default locale.
     *
     * @return a Calendar.
     */
    public static Calendar getInstance()
    {
        Calendar cal = createCalendar(TimeZone.getDefaultRef(), Locale.getDefault());
	cal.sharedZone = true;
	return cal;
    }
   
   // 조건에 따라서 생성되는 객체가 달라진다.
   private static Calendar createCalendar(TimeZone zone,
					   Locale aLocale)
    {
	// If the specified locale is a Thai locale, returns a BuddhistCalendar
	// instance.
	if ("th".equals(aLocale.getLanguage())
	    && ("TH".equals(aLocale.getCountry()))) {
	    return new sun.util.BuddhistCalendar(zone, aLocale);
	} else if ("JP".equals(aLocale.getVariant())
		   && "JP".equals(aLocale.getCountry())
		   && "ja".equals(aLocale.getLanguage())) {
	    return new JapaneseImperialCalendar(zone, aLocale);
	}	    

	// else create the default calendar
        return new GregorianCalendar(zone, aLocale);	
    }

참고

https://refactoring.guru/design-patterns/factory-method

 

Factory Method

/ Design Patterns / Creational Patterns Factory Method Also known as: Virtual Constructor Intent Factory Method is a creational design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objec

refactoring.guru

 

'Today I learned' 카테고리의 다른 글

2021 08 13 - Builder Pattern  (0) 2021.08.13
2021 08 10 - Abstract Factory  (0) 2021.08.10
UML(Unified Modeling Language) 훑어보기  (0) 2021.08.10
Concurrency Patterns  (0) 2021.08.09
Java Synchronization  (0) 2021.08.06

댓글