Develop/java

[Java] if, else 에서 탈출해보자! interface활용

에디개발자 2020. 12. 3. 07:00
반응형

프로젝트를 진행하다보면 프로세스 로직에서 수많은 분기를 처리하기 위해 if, else가 무수히 달리는 것을 확인할 수 있습니다. 이번 글에서는 if, else를 최대한 줄이고 가독성을 높이는 작업을 진행해보겠습니다.

모든 예제 소스는 github에 올려두었습니다.

이번에 작성하는 글은 주관적인 생각입니다. 전 if, else가 많으면 확장성, 가독성이 좋치 않다고 생각하는 개발자1이기 때문에 이글을 작성합니다. 

나를 닮았다고 한다....

늘 그랬듯이 개발자는 소스를 보면 가장 이해가 빠르기 때문에 예제소스를 작성해보겠습니다.

 

ProcessService.java

@Slf4j
@Service
@RequiredArgsConstructor
public class ProcessService {

    private final StoreRepository storeRepository;
    private final StaffRepository staffRepository;

    public String if문_탈출못했네(String branch) {
        if(STORE_BRANCH.equals(branch)) {
            Optional<Store> storeOptional = storeRepository.findById(1L);
            if(!storeOptional.isPresent()) {
                return "값이 없다";
            }
            Store store = storeOptional.get();
            return store.getName();
        } else if(STAFF_BRANCH.equals(branch)) {
            Optional<Staff> staffOptional = staffRepository.findById(1L);
            if(!staffOptional.isPresent()) {
                return "값이 없다";
            }
            Staff staff = staffOptional.get();
            return staff.getName();
        }
        return "분기 못탔다";
    }
}

분기 별로 다른 로직을 실행하는 코드를 작성하였습니다. 

모바일로 보면 로직보기 힘들더라.. ㅠ 그래서 간단하게 로직을 설명드리자면 
branch 변수가 "store" 이면 store table에서 조회
branch 변수가 "staff" 이면 staff table에서 조회

이런 코드를 많이 보셨다고 생각합니다. 지금은 분기처리가 2개뿐이지만 서비스 확장으로 분기가 10개 이상이 되었다하면 else if를 8개를 추가해야합니다. 그럼 소스를 찾아가는데에 힘들 수 있을 것입니다. ( 유지보수성 하락 ) 

if, else가 더이상 추가되는 것을 막기 위해 if 단위로 클래스 or 메서드로 객체화시켜보겠습니다.

 

 

수정 시작

일단 그룹화를 위한 interface를 생성해줍니다.

ProcessBranch.java

public interface ProcessBranch {
    String process();
}

 

StaffProcessBranch.java

@Slf4j
@Service
@RequiredArgsConstructor
public class StaffProcessBranch implements ProcessBranch {
    private final StaffRepository staffRepository;

    @Override
    public String process() {
        Optional<Staff> staffOptional = staffRepository.findById(1L);
        if(!staffOptional.isPresent()) {
            return "값이 없다";
        }
        Staff staff = staffOptional.get();
        return staff.getName();
    }
}

ProcessBranch를 implements한 Staff관련 소스를 작성합니다.

로직은 위 소스의 if의 Staff 부분을 작성하였습니다.

 

StoreProcessBranch.java

@Slf4j
@Service
@RequiredArgsConstructor
public class StoreProcessBranch implements ProcessBranch {

    private final StoreRepository storeRepository;

    @Override
    public String process() {
        Optional<Store> storeOptional = storeRepository.findById(1L);
        if(!storeOptional.isPresent()) {
            return "값이 없다";
        }
        Store store = storeOptional.get();
        return store.getName();
    }
}

ProcessBranch를 implements한 Store관련 소스를 작성합니다.

로직은 위 소스의 if의 Store 부분을 작성하였습니다.

 

 

ProcessBranchFactory.java

@Slf4j
@Service
@RequiredArgsConstructor
public class ProcessBranchFactory {

    private final StoreProcessBranch storeProcessBranch;
    private final StaffProcessBranch staffProcessBranch;

    public ProcessBranch getProcessBranch(String branch) {
        ProcessBranch processBranch = null;
        if(STORE_BRANCH.equals(branch)) {
            processBranch = storeProcessBranch;
        } else if(STAFF_BRANCH.equals(branch)) {
            processBranch = staffProcessBranch;
        }
        return processBranch;
    }
}

interface를 implements한 Store, Staff 클래스를 가져올 수 있는 클래스를 생성합니다.

분기 별로 객체를 가져오는 로직을 넣어줍니다.

 

ProcessService.java

@Slf4j
@Service
@RequiredArgsConstructor
public class ProcessService {
    private final ProcessBranchFactory processBranchFactory;

    public String if문_탈출성공(String branch) {
        ProcessBranch processBranch = processBranchFactory.getProcessBranch(branch);
        return processBranch.process();
    }
}

ProcessService 로직을 수정합니다. 

ProcessBranchFactory에 분기를 판단하는 파라미터를 넘겨 관련 객체를 가져옵니다.
조회된 객체를 실행합니다.

 

검증!

테스트 코드를 작성하여 값을 확인해보겠습니다.

@ActiveProfiles("local")
@ExtendWith(SpringExtension.class)
@SpringBootTest
public class ProcessServiceTest {
    @Autowired
    private ProcessService processService;

    @Test
    void if문_탈출못했네_Test() {
        //given
        String storeBranch = "store";
        String staffBranch = "staff";

        //when
        String storeResult = processService.if문_탈출못했네(storeBranch);
        String staffResult = processService.if문_탈출못했네(staffBranch);

        //then
        assertThat(storeResult).isEqualTo("용태스토어");
        assertThat(staffResult).isEqualTo("임용태");
    }

    @Test
    void if문_탈출성공_Test() {
        //given
        String storeBranch = "store";
        String staffBranch = "staff";

        //when
        String storeResult = processService.if문_탈출성공(storeBranch);
        String staffResult = processService.if문_탈출성공(staffBranch);

        //then
        assertThat(storeResult).isEqualTo("용태스토어");
        assertThat(staffResult).isEqualTo("임용태");
    }
}

두 케이스 모두 정상작동을 확인하였습니다.

 

결론

interface를 사용하여 if, else문을 줄일 수 있습니다!! 

 

 

 

p.s 주석 없이 소스가 책처럼 술술 읽히는 그날까지!!

 

반응형