🫠메소드가 파라미터를 많이 가질수록 코드를 이해하고 유지하기가 더 어렵습니다. 예를 들어, 다음과 같은 메소드가 있다고 가정해보겠습니다.
print(1, 2);
위 메소드는 두 개의 파라미터를 가지고 있습니다. 하지만 이 파라미터들이 어떤 의미인지, 어떻게 사용되는지 한눈에 알기 힘듭니다. 이러한 문제를 해결하기 위해 파라미터의 개수를 줄이는 것이 좋습니다.
print(Point);
위 코드처럼 Point
객체 하나만을 파라미터로 받으면, 해당 객체 내부에 필요한 모든 정보가 담겨 있으므로 메소드를 이해하기가 더 쉬워집니다.
🥳파라미터를 클래스로 변화함으로써 파라미터의 개수를 줄일 수 있으며
파라미터의 순서를 생각하지 않고 클래스 자체를 넘기면 되서 더욱 가독성이 높고 편리해집니다.
// 파라미터가 두 개인 메소드
public void print(int x, int y) {
System.out.println("X: " + x + ", Y: " + y);
}
// Point 클래스
public class Point {
int x;
int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
// 파라미터가 하나인 메소드
public void print(Point point) {
System.out.println("X: " + point.x + ", Y: " + point.y);
}
log.print("good name",false);
😰위의 경우 false가 무엇을 의미하는지 한 눈에 파악하기 어렵기 때문에 boolean 파라미터는 사용하지 않는게 좋습니다. 대신 아래와 같이 분리하여 사용하는 것을 추천합니다. (보통 boolean 파라미터를 사용하는 경우 flag 용도로 사용하기 위해 넣는데 좋지 않은 선택이라고 하네요...)
log.print("good name");
log.error();
🤓메소드의 이름은 해당 메소드의 기능을 추상화한 것이기 때문에, 그 내부의 구현은 해당 메소드의 이름보다 더 구체적인 단계(추상화가 더 낮은)로 이루어져야 합니다.
public void processOrder() {
// 1. 주문 확인
// 2. 재고 확인
// 3. 결제 진행
// 4. 고객에게 메일 전송
sendComplexQueryToDatabase();
compileGeneratedJavaFiles();
}
위의 예시에서 processOrder 메소드는 주문 처리를 추상화한 것인데, sendComplexQueryToDatabase()와 compileGeneratedJavaFiles() 메소드는 주문 처리와 직접적으로 관련이 없거나, 더 낮은 수준의 작업을 수행합니다.
public void processOrder() {
// 1. 주문 확인
verifyOrderDetails();
// 2. 재고 확인
checkInventory();
// 3. 결제 진행
processPayment();
// 4. 고객에게 메일 전송
sendEmailToCustomer();
}
🧐이 경우, processOrder의 내부 로직이 메소드 이름보다 추상화 수준이 낮은 여러 메소드로 구성되어 있습니다. 이렇게 하면 메소드의 의도가 명확해지고, 다른 개발자들이 코드를 더 쉽게 이해할 수 있습니다.
메소드의 이름과 내부 로직의 추상화 수준이 일치하도록 하면, 코드가 일관성 있고 가독성이 높아집니다. 이는 유지보수를 쉽게 하고 버그 발생 가능성을 줄여줍니다.
🤗클린 코드를 작성하려면 로직을 잘게 분할하여 각각의 메소드로 만드는 것이 일반적으로 추천됩니다. 이는 코드의 재사용성을 높이고, 유닛 테스트를 쉽게 만들며, 다른 사람이 코드를 이해하기 쉽게 만듭니다. 그러나 모든 경우에 메소드로 로직을 추출하는 것이 좋은 것은 아닙니다.
가독성을 높이려는 목적으로 메소드를 추출할 때, 단순히 메소드의 이름만으로 로직을 설명할 수 있는 경우는 잘 선택된 경우입니다.
예를 들어, 아래의 Java 코드를 살펴보겠습니다.(극단적인 예입니다...)
java
public void process(int a, int b) {
int c = a+b
return c;
}
java
public void process(int a, int b) {
return add(a,b);
}
public int add(int a, int b) {
return a + b;
}
1번의 메소드와 2번의 메소드를 보시면 a+b를 추출하여 새로운 메소드로 만들어서 사용하고 있는데 잘 생각해 보시면 이것은 그냥 단순 이름 변경에 불과한 정도의 메소드 추출이라고 생각이 될 수 있습니다.
이 처럼 메소드를 추출할 경우 기존 코드에서 단순 이름 변경에 불과한 정도의 리팩토링이라면 오히려 리팩토링을 안 하는 것이 더 좋을 수 있습니다.🤔
🧐메소드 추출의 또 다른 단점은, 과도하게 많은 메소드로 코드가 분할되면, 해당 메소드의 로직을 찾기 위해 파일 전체를 뒤져야 할 수도 있다는 것입니다. 이러한 상황은 코드의 가독성을 떨어뜨리고, 유지보수를 어렵게 만듭니다.
예를 들어
public void complexLogic() {
partOne();
partTwo();
partThree();
partFour();
...
}
위와 같이 너무 많은 작은 메소드로 로직이 나뉘어져 있다면, 각 메소드의 역할을 이해하기 위해서는 각 메소드의 내부 로직을 모두 찾아봐야 합니다. 이럴 경우, 메소드 추출은 반드시 필요한 경우에만 신중하게 적용하는 것이 좋습니다.
이렇게 메소드 추출을 적절하게 사용하면 코드의 가독성과 유지보수성을 높일 수 있습니다. 반면, 잘못 사용하면 코드의 복잡성을 높일 수 있으므로 신중한 판단이 필요합니다.😊