본문 바로가기
개발팁

code bad smell - 긴 switch 문과 polymorphism을 사용한 refactoring 기법

by devscb 2022. 6. 29.
반응형

code bad smell - 긴 switch 문과 polymorphism을 사용한 refactoring 기법

switch 문을 잘 사용한다면 나쁘지 않습니다.
또한, 어쩔 수 없이 switch 문을 쓸 경우가 생길 수 밖에 없는 경우가 존재합니다.
하지만 switch 의 case가 너무 많은 경우, 코드를 이해하기 어려운 경우가 있을 수도 있습니다.
예를 들어 아래와 같은 코드를 봅시다.

int[] mysort(String type, int[] arr) 
{ 
 switch (type) 
 { 
 case "BubbleSort": 
 //bubble sort 로직 수행.
 return arr;
 case "SelectionSort": 
 //selection sort로직수행
 return arr;
 case "InsertionSort": 
 //insertsion sort 로직수행
 return arr;
 } 

 return arr; 
} 



대략적인 코드는 위와 같고, 겉으로 보기에는 충분이 이해할만 할것 같습니다.
하지만, 실제 짜여진 전체 코드는 아래와 같은 코드를 나타냅니다.


int[] mysort(Sting type, int[] arr) 
{ 
 switch (type) 
 { 
 case "BubbleSort": 
 int i, j, temp;
 for(i=n-1; i>0; i--){
 for(j=0; j<i; j++){<br=""> if(arr[j]<arr[j+1]){
 temp = arr[j];
 arr[j] = arr[j+1];
 arr[j+1] = temp;
 }
 }
 }
 return arr;
 case "SelectionSort": 
 for(int i = 0; i < arr.length; i++){
 int min = i;
 for(int j = i+1; j < arr.length; j++){
 if(arr[j] < arr[min]){
 min = j;
 }
 }
 int temp = arr[i];
 arr[i] = arr[min];
 arr[min] = temp;
 }
 return arr;
 case "InsertionSort": 
 for (int i = 1; i < arr.length; i++) {
 int currentValue = arr[i];
 int j;
 for (j = i - 1; j >= 0 && arr[j] > currentValue; j--) {
 arr[j + 1] = arr[j];
 }
 arr[j + 1] = currentValue;
 }
 return arr;
 } 

 return arr; 
} 
 </arr[j+1]){
</i;>



sort의 type이 늘어날 수록 점점 복잡해보이고, 한눈에 코드의 내용이 안보이게 될 것입니다.
이런식의 case문을 읽기 쉽고 유지보수하기 쉽게 하기 위한 방법 중 polymorphism 으로 해결하는 방법이 있습니다.
각 조건에 맞는 하위클래스를 만들고, 각 클래스가 action 을 수행하도록 만들 수가 있습니다.
이번 예제의 경우, 디자인 패턴의 전략패턴(strategy pattern)을 적용한다고 볼 수 있습니다.

이를 위해 interface를 아래와 같이 만들어 줍니다.
그리고 int[] mysort는 단순히 이 interface의 act() 메소드를 호출해주도록 개선할 수 있습니다.
그러면 mysort 메소드는 너무나도 읽기 쉬운 코드가 됩니다.


interface ISort{
 int[] act(int[] arr);
} 

int[] mysort(Isort sortType, int[] arr){
 return sortType.act(arr);
}



각 구현은 아래와 같이 개별 구현이 필요하지만, 아래와 같이 mysort함수를 아주 간단하게 사용할 수 있습니다.
mysort(new BubbleSort(), arr);
읽기도 굉장히 쉬워지고,
유지보수를 할 때에는 내가 관심이 없는 selection sort나 insertion sort 부분의 코드는 읽지 않아도 되기에
굉장히 빠른 수정이 가능합니다.

class BubbleSort implements ISort{
 int[] act(int[] arr){
 int i, j, temp;
 for(i=n-1; i>0; i--){
 for(j=0; j<i; j++){<br=""> if(arr[j]<arr[j+1]){
 temp = arr[j];
 arr[j] = arr[j+1];
 arr[j+1] = temp;
 }
 }
 }
 return arr;
 }
}

class SelectionSort implements ISort{
 int[] act(int[] arr){
 for(int i = 0; i < arr.length; i++){
 int min = i;
 for(int j = i+1; j < arr.length; j++){
 if(arr[j] < arr[min]){
 min = j;
 }
 }
 int temp = arr[i];
 arr[i] = arr[min];
 arr[min] = temp;
 }
 return arr;
 }
}


class InsertionSort implements ISort{
 int[] act(int[] arr){
 for (int i = 1; i < arr.length; i++) {
 int currentValue = arr[i];
 int j;
 for (j = i - 1; j >= 0 && arr[j] > currentValue; j--) {
 arr[j + 1] = arr[j];
 }
 arr[j + 1] = currentValue;
 }
 return arr;
 }
}
 </arr[j+1]){
</i;>



추가적으로 만일 새로운 case를 대처해야할 경우,
mysort함수를 수정해야하지만, refactoring 된 코드에서는 새로운 class를 만들고, 단순히 그 class를 사용해주면 되지요.
오늘 소개한 예제에서는 모든 코드를 제가 작성했지만, 만일 mysort 코드가 특정 라이브러리라고 가정하고, 사용자가 코드를 함부로 못고치는 코드라면?
나는 코드의 일부분 로직만 바꾸고 싶은데 라이브러리를 전체 뜯어서 다시 재구성해야하는 참사가 일어날 수도 있습니다.
오늘 소개한 방식은 abstract factory 패턴에서도 사용되는 방식이기도 합니다.

총평


오늘 소개한 방식은 switch 문 뿐만이 아니라 당연히 if문에도 적용할 수 있는 방법입니다.
if문과 switch 문은 flow을 다루기 위한 기본적인 문법이지만, 별 생각 없이 짜면 코드가 굉장히 난잡해질 수 있습니다.
이를 명심해서 코드를 잘 작성할 수 있도록 생각하는 자세를 가지면 유지보수가 쉬운 코드가 될 수 있습니다.
내가 짠 코드에 if나 case가 너무 많이 나오는것은 아닌가? 라고 생각이 들 때, 이 방식을 한번 생각해보면 좋을것 같습니다.
만일 잘 모르겠다면 extract method라는 간단한 refactoring 기법을 사용하여 조금씩 개선하면 좀 더 잘 개선할 수도 있지 않을까 싶습니다.
extract method라는 refactoring 기법도 조만간 소개해보도록 하겠습니다.

#리팩토링,#code,#bad,#smell,#badsmell,#refactor,#refactoring,#switch,#case,#스위치,#케이스,#polymorphism

https://devscb.com/post/113

 

code bad smell - refactoring technique using long switch statements and polymorphism

code bad smell - refactoring technique using long switch statements and polymorphism Not bad if you use switch statements well.Additionally, there are cases where it is unavoidable to use a switch sta

devscb.com

 

728x90
반응형

댓글