본문 바로가기
Swimming/Think

생성자 오버로딩은 왜 사용하면 안되는가.

by JKROH 2023. 2. 28.
반응형

 지난 주차에 생성자 오버로딩에 관련하여 배우면서, 생성자 오버로딩의 사용법과 왜 사용하는지에 대해 공부했다. 그리고 생성자 오버로딩은 왜 사용하면 안 되는지에 대해 생각해 보라는 숙제를 받았다.

 오늘까지 고민해본 결과 생성자 오버로딩을 사용하면 안 되는 이유는 다음과 같다.

생성자 오버로딩은 명시적이지 않은 코드를 만든다.

 

 클린 코드를 공부하면서 내 나름대로 세운 규칙 중에 하나는 '반드시 명시적인 이름을 사용하라' 이다. 우리는 코드를 마치 '글을 읽듯이 읽을 수 있게 작성해야 한다'. 전체적인 코드를 봤을 때, public한 메서드들과 생성한 객체들만 가지고도 어떤 객체들을 활용해서 어떤 기능을 하는 프로그램인지 알 수 있어야 한다. 따라서 메서드의 이름과 객체의 이름, 변수의 이름은 반드시 '그 이름만 가지고도 무슨 일을 하는지 알아야한다.'

 

 내가 생성자뿐만 아니라 내가 제작한 메서드의 오버로딩도 사용하지 않으려는 이유도 여기에 있다. 메서드를 오버로딩하면 같은 이름의 메서드인데 서로 다른 일을 한다. 클린 코드의 기본인 '한 메서드는 하나의 일만 한다를 완벽하게 위반한다'. 만약 전달되는 argument가 다르니 다른 메서드라고 하면 뭐 딱히 해 줄 말이 없다.

 

 아무튼 내가 고민한 끝에 내린 결론은 위의 이유와 같다. 생성자 오버로딩을 사용하면 명시적이지 않은 코드가 반드시 나오게 된다.

 

 예를 들어 보자, 우리는 사운드바 객체를 만드는데 일반적으로는 USB-C타입에 너비 300mm 높이 70mm로 만든다고 한다. 그래서 해당 값을 default로 두고 그렇지 않은 경우에만 생성자에 USBType, width, height을 인자로 넣어준다고 가정해 보자. 그러면 우리는 아래와 같은 생성자들을 만들 수 있다.

class SoundBar {
    USBType usbType = new USBType("C");
    int width = 300;
    int height = 70;
    
    public SoundBar(){
    }
    
    public SoundBar(USBType usbType){
    	this();
        this.usbType = usbType;
    }
    
    public SoundBar(USBType usbType, int width){
    	this();
        this.usbType = usbType;
        this.width = width;
    }
    
    public SoundBar(USBType usbType, int width, int height){
    	this();
        this.usbType = usbType;
        this.width = width;
        this.height = height;
    }
    
    
    ... 더 많은 생성자와 객체 구현

 더 많은 생성자를 만들 수 있지만 벌써 문제가 보이니 더 적지 않았다. 이제 해당 생성자들을 통해서 객체를 생성한다고 해보자.

SoundBar soundBarOne = new SoundBar(usbType, 100);
SoundBar soundBarTwo = new SoundBar(usbType, 120);

 자 그래서 두 사운드바에 적용된 100과 120이 위의 클래스 코드를 보지 않고 어떤 값인지 알 수 있을까. 말 그대로 '알 수가 없다'. 물론 해당 예시는 생성자 오버로딩을 사용했을 때의 문제점을 지적하기 위해 조금 과하게 설정한 감이 있지만, 이런 문제가 발생하지 말라는 법은 없다.

 

  겨우 3개의 변수만을 설정하고 가능한 생성자 오버로딩의 경우를 전부 적지도 않았는데도 벌써부터 치명적인 오류가 발생한다. 만약 변수가 더 많고 생성자 오버로딩의 경우가 더 많았다면, 우리는 클래스의 내부를 뜯어보지 않고서는 '이 객체가 어떤 객체인지조차 알 수가 없다'. 해당 코드를 스스로 리뷰하는데도 엄청나게 오랜 시간이 들 텐데, 만일 이게 내가 작성한 코드가 아니라고 생각하면 벌써부터 아찔하다.

 

  명시적인 코드는 협업의 관점에서 그 어느 것보다 중요하다. 그렇기 때문에 우리는 함수와 변수, 그리고 객체의 이름을 짓는데 오랜 시간을 들이고 고민하는 것이다. 물론 오버로딩은 코드를 작성하는 시점에서는 편리하게 사용할 수 있다. 나는 이 코드가 어떤 의도인지 알고 있으니까 말이다. 그러나 내가 아닌 다른 사람이 내 코드를 본다면, 오버로딩을 왜 했는지, 어떤 인자들이 달라졌는지를 알아봐야 하는 새로운 task가 생기게 된다.

 

  따라서 우리는 생성자 오버로딩을 사용하면 안 된다. '대신 팩토리 메서드를 사용하는 것이 정신건강에 이롭다'. 당장 저 예시에서만 봐도 어떤가. makeModifiedHeightSoundBar(int height); 메서드가 있었다면 너무 편하지 않았을까. 메소드 이름만 봐도 반환될 값이, 생성될 객체 인스턴스가 높이가 수정된 사운드 바라는 게 명시적이다. 코드를 처음 보는 사람도 해당 메서드는 높이를 수정한 사운드 바 객체를 만들고 argument로는 높이가 들어갈 것이라는 것을 직관적으로 알 수 있다. 

 

 사회의 어느 일이 안 그렇겠냐만은, 개발은 나 혼자 할 수 있는 일이 아니다. 물론 자기 혼자서 할 간단한 게임을 개발하거나, 아주 간단한 서류 정리 프로그램 등을 만드는 수준이라면 혼자서도 분명히 가능하다. 그러나 개발을 업으로 삼고, 대중에게 선보일 서비스를 개발하려면 반드시 나 이외의 누군가와 함께 해야 한다. 그렇기에 개발자에게 소프트 스킬이 계속해서 강조되는 것이기도 하다. 개발을 잘 모르는 사람, 나와는 다른 생각을 가진 개발자와 나는 끊임없이 소통하고 상호작용하며 서비스를 발전시켜가야 한다.

 

 그러나 개발자는 동시에 코드로 말하는 사람이다. 우리가 말을 잘하는 사람과의 소통이 더 편하고 더 빈번했으면 하는 것처럼, 개발자는 '잘 짜이고 잘 읽히는 코드로 사람의 마음을 움직일 수 있어야 한다'. 결국 생성자 오버로딩은 왜 사용하면 안 되는 가의 결론은 다음과 같다고 귀결될 수 있다. '생성자 오버로딩은 나는 편할지 몰라도 남은 불편하게 만드는 방법'이다. 그렇기에, 우리는 생성자 오버로딩을 사용하면 안 된다.

반응형

댓글