본문 바로가기

백엔드 개발 노트

[디자인 패턴] 팩토리 패턴을 이용한 OCP 지키는 코드 짜기

https://ghcodenote.tistory.com/29

 

[JAVA] JAVA Enum field를 이용한 Enum 그룹화하기

https://ghcodenote.tistory.com/28 [디자인 패턴] 확장성에 용이한 코드 짜기졸업 프로젝트에서 문장의 형태소를 분석해 특정 형태소가 들어가는지 checkList를 만드는 API를 구현해야 했다. 외부 API를 통

ghcodenote.tistory.com

이전 포스팅


여기까지 해서 형태소를 그룹화했습니다!!

 

이제 Noun, MA 등의 형태소 그룹을 가지고 형태소 그룹에 해당하는 checkList를 채워 나가는게 목표인데요

 

문제가 발생했습니다.

 

for (MorphemeVO morpheme : morphemeVOS) {
            String lemma = morpheme.lemma();
            String type = morpheme.type();

            MorphemeTypeEnum typeEnum = MorphemeTypeEnum.findByMorpheme(type);
            if (MorphemeTypeEnum.EMPTY == typeEnum) {
                continue;
            }
            
            if(typeEnum == Enum.Noun){
	            명사 체크리스트 메소드 실행
            } 
            if(typeEnum == Enum.VO){
	            동사 체크리스트 메소드 실행
            } 
            if(typeEnum == Enum.VV){
	            부사 체크리스트 메소드 실행
	           } 
	           .
	           .
	           .
	          
	          10개 더 있어야함..ㅎㅎ
      
           
        }

- 뒷구르기 3번하면서 보면서 봐도 뭔가 잘못됐음을 직감…

 

이러면 아무리 Enum을 그룹화해봤자 if문이 enum 그룹 갯수만큼 있어야 합니다… 또한 Enum이 하나 늘어나면 if문이 늘어나게 되므로 OCP에 위배됩니다(제 생각..)

 

따라서 좀 더 효과적인 방법을 찾고 있었어요..

적어도 Service 코드에서는 한줄로 끝날 수 있게 하는게 목표입니다.

 

찾아보면서 적용하기로 한게 Factory Method 패턴입니다.

 

팩토리 패턴이란!

하나의 커맨드 센터가 있다고 생각합시다. 커맨드 센터에는 주문표가 있어요.

 

티비→ A기관에서 수리, 테블릿→B기관에서 수리

 

근데 소비자 입장에서는 이게 어디서 수리하는지도 몰라요. 그래서 커맨드 센터에 그냥 보내면 거기서 수리하는 곳을 알려주게 커맨드 센터를 구축하는 거에요

 

이해 안되신다고요? 죄송합니다. 제 필력이 이정도밖에 안돼요ㅜㅜ

 

아무튼 이 때 커맨드 센터(Factory), 주문표(HashMap), 티비(형태소 그룹 타입), A기관(checkList를 실행하는 구현체) 로 생각하시면 됩니다.

 

@Service
public class MorphemeCheckerFactory {
    private final Map<MorphemeTypeEnum, MorphemeChecker> checkerMap = new ConcurrentHashMap<>();

    public MorphemeCheckerFactory() {
        checkerMap.put(MorphemeTypeEnum.NOUN, new NounChecker());
        checkerMap.put(MorphemeTypeEnum.NP, new NPChecker());
        checkerMap.put(MorphemeTypeEnum.JKS, new JKSChecker());
        checkerMap.put(MorphemeTypeEnum.VV, new VVChecker());
        checkerMap.put(MorphemeTypeEnum.VA, new VAChecker());
        checkerMap.put(MorphemeTypeEnum.EC, new ECChecker());
        checkerMap.put(MorphemeTypeEnum.JX_JC, new JXJCChecker());
        checkerMap.put(MorphemeTypeEnum.MA, new MAChecker());
        checkerMap.put(MorphemeTypeEnum.EF, new EndingChecker());
    }

    public MorphemeChecker getChecker(MorphemeTypeEnum type) {
        return checkerMap.get(type);
    }
}

이렇게 checkerMap(주문표)에 Enum이 어떤 Check Method로 가는지 다 적어놉니다.

 

 

 

그러면 service 입장에서는 getChecker를 통해 사용할 checker를 가져옵니다.

- Service

MorphemeChecker morphemeChecker = morphemeCheckerFactory.getChecker(typeEnum);

MorphemeChecker는 인터페이스입니다. 그 안에 여러 구현체들이 있어요

 

public interface MorphemeChecker {
    void analyzeMorpheme(Map<MorphemeCheckEnum, Boolean> checkList, String type,
                         String lemma);
    default void updateCheckListTrue(Map<MorphemeCheckEnum, Boolean> checkList, MorphemeCheckEnum checkEnum) {
        checkList.put(checkEnum, Boolean.TRUE);
    }
}

interface 안에는 형태소 분석하는 기능(analyzeMorpheme)과 updateCheckListTrue-디폴트 메소드가 있습니다→ 체크리스트 업데이트 메소드

 

 

MorphemeChecker를 implements하는 구현체들

@Service
@Transactional
class NounChecker implements MorphemeChecker {
    @Override
    public void analyzeMorpheme(Map<MorphemeCheckEnum, Boolean> checkList, String type,
                                String lemma) {
                   //명사일때 해야하는 checkList 작업
    }
}

@Service
@Transactional
class NPChecker implements MorphemeChecker {
    @Override
    public void analyzeMorpheme(Map<MorphemeCheckEnum, Boolean> checkList, String type,
                                String lemma) {
                  //대명사일때 해야하는 checkList 작업
    }
}

.... 나머지도 구현

 

이런식으로 구현하면 서비스 입장에서는 어떤 checker인지 몰라도 메소드를 호출할 수 있게 됩니다!

        for (MorphemeVO morpheme : morphemeVOS) {
            String lemma = morpheme.lemma();
            String type = morpheme.type();

            MorphemeTypeEnum typeEnum = MorphemeTypeEnum.findByMorpheme(type);
            if (MorphemeTypeEnum.EMPTY == typeEnum) {
                continue;
            }

			//서비스 입장에서는 typeEnum이 뭔지 몰라도 구현체(analyzeMorpheme)를 호출할 수 있다
            MorphemeChecker morphemeChecker = morphemeCheckerFactory.getChecker(typeEnum);
            morphemeChecker.analyzeMorpheme(checkList, type, lemma); 
        }