[자바] getter메소드 사용을 지양하자

2020. 4. 25. 15:43TIL/자바

기존의 나는 코딩을할때 상태값이 있는 클래스에는 무조건 getter와 setter를 기본적으로 정의해주곤 했다.

최근 OOP스터디를 하던중, getter메소드 사용을 지양해야한다는 피드백을 들었는데,

그 이유에 대해서 정리해보고자 한다.

먼저, 내가 공부해온 내용을 토대로 추측한 이유를 적어보겠다.

 

나는 '객체지향의 사실과 오해'라는 책을 읽고나서, 객체의 상태가 변경되는 것은 객체 스스로의 행동에 의해서야 한다는 것을 알게 되었다.

이렇게 설계될때, 객체는 자율적인 객체가 되고 외부의 영향을 받지 않음으로써 느슨한 결합과 유연한 협력을 이룰 수 있게 된다.

이러한 관점에서 볼때,  getter와 setter는 자신의 상태정보를 외부에 노출하는 격이 되고, 이것은 외부의 영향으로 상태정보가 변할 수 있는 가능성을 열어두게 된다. 한 객체의 변화가 여러 객체에게 영향을 주는 이러한 코드가 많아질수록, 훗날 유지보수가 어려워 질 것은 당연하다.

나같은 초보자들은 아직 절차지향적인 사고를 완전히 벗어나지 못했기 때문에,

이러한  getter와  setter없이는 처리하기 힘든 로직들을 종종 짜고 있을 것이다. 

하지만, 객체지향의 달인들은 이러한 로직들도 모두 getter와 setter없이 짤 수 있지 않을까?

 

검색을 통해서 getter메소드를 지양할 수 있는 방법에 대해 알 수 있었다.

쉽게 말해, getter를 통해 얻은 상태값으로 하려고 했던 '행동'을 그 상태값을 가진 객체가 하도록 '행동'의 주체를 옮기는 것이다.

내가 직접 짜던 코드를 통해 예를 들어 보겠다.

public static List<String> extractWinners(List<Car> cars, int winnerPosition) {
        List<String> winners = new LinkedList<>();
        for (Car car : cars) {
            if (car.getPosition() == winnerPosition) {
                winners.add(car.getName());
            }
        }
        return winners;
    }

위의 메소드는  getPosition()메소드를 통해 car객체의 position이라는 상태값을 꺼내고, 그 값과 winnerPosition값을 비교한다.

해당 객체의 position상태값이 winnerPosition값과 같다면, winners라는 리스트에 그 객체를 추가해주는 코드이다.

이것을 getPosition()없이 수행하기 위해서는 해당 객체의 position값과 winnerPosition값을 비교하는 로직을

객체에 위치시키는 것이다.

 

public static List<String> extractWinners(List<Car> cars, int winnerPosition) {
        List<String> winners = new LinkedList<>();
        for (Car car : cars) {
            if (car.isWinner(winnerPosition)) {
                winners.add(car.getName());
            }
        }
        return winners;
    }

이런식으로, car객체에게 winnerPosition을 넘겨주고, '너의 position값이 winnerPosition값과 같니?'라고 메시지를 보내는 것이다.

객체는 isWinner라는 메소드에서 넘겨받은 숫자와 자신의 position값을 비교하여, 참/거짓 여부를 반환해준다.

이렇게 로직을 상태값을 가지는 객체에 위치시킴으로서 getter를 사용하지 않고 설계를 할 수 있게 된다. 

 

어떤 느낌인지는 알았지만 , getter를 사용하지 않아야할 이유가 크게 와닿지는 않았다.

getter는 단순히 값을 뱉어주는 역할을 할 뿐이므로,  객체의 상태값을 변경할 수 있는 메소드도 아닌데 

굳이 왜 메시지를 통해 그 로직을 처리하는 것이 좋은 것 일까?

명확하게 이해하지는 못했지만, 내가 생각한 이유는 다음과 같다.

 

첫째, 메시지를 통한 로직처리를 통하여 getter를 남발하는 코딩습관을 막을 수 있다.

getter는 상태정보를 제공해 줌으로서 다양한 로직에 편리하게 사용 될 수 있다. 

하지만, 이와같이 getter를 통한 로직처리를 하는 메소드가 많아 질수록 getter에 대한 의존성은 매우 높아진다고 볼 수 있다.

객체는 독립적이어야 한다. 타 객체에 의존하는 경향이 커질 수록 못난 설계가 된다는 것은 이제 쉽게 받아들일 수 있는 내용이다.

 

둘째, getter메소드만으로 외부에서 객체의 상태를 변경하지는 못하지만, 그 결과값이 객체의 상태를 변경시키는데에 사용될 수 있다.

여러 고민과 토론끝에 이 두번째 이유가 가장 정답에 가깝다고 판단하게 되었다. 

간단한 코드를 통해 이러한 판단을 하게 되었는데, 

public Car {
	private int number;
    
    public Car(int number) {
    	this.number = number;
    }
    
    public void update(int newNumber) {
    	this.number = newNumber;
    }
    
    public int getNumber() {
    	return number;
    }
    
    public void move(int number) {
    	if (this.number >= number) {
        	this.number++;
        }
    }
}
public class Game {

	public void run(Car car) {
		int number = car.getNumber();
        
		if (number >= 4) {
        	car.update(number+1);
		}
	}
    
	public void run2(Car car) {
    	car.move(4);
	}

}

위와 같이 Car클래스와 Car클래스로부터 객체를 만들어 게임을 실행하는 Game클래스가 있다.

Game의 run메소드는 getter를 이용하여 로직을 처리하고 있고,

run2메소드는 객체에 메시지를 보내서 로직을 처리하고 있다.

두 로직 모두 car객체의 number가 4이상이면 number를 1만큼 증가시키는 로직이다.

주목할 점은, 1만큼 증가시키는 판단을 누가 하는가 이다.

run2같은경우, 숫자를 넘겨줄 뿐, 상태값을 변경시키는 판단을 객체에게 맡기고 있다.

하지만 run같은 경우, getter를 통해 값을 받은 후 그 값을 이용하여 객체의 상태값을 변화시키는 판단을 하게되고, update메소드를 호출함으로서 객체의 상태값을 바꾼다.

즉, 객체의 상태값을 바꾼다는 판단을 외부에서 하고 있는 것이다.

getter메소드가 직접적으로 상태값을 바꾸지는 않지만, 이것이 사용되는것은 외부에게 '상태값 변경에 대한 판단권'을 줘버리게 될 수 있다.

이것은 계속해서 되새기고 있는 '독립적인 객체'설계에 위배되는 행위이다.

 

정리하자면, 객체에게 메시지를 보냄으로서 로직을 처리하는것은

상태값을 수정할지에 대한 판단권을 그 상태값을 가진 객체 스스로에게 부여하는 것이다. 

관련된 모든 내용을 이해했다고 할 수는 없지만 , getter의 사용보다는 객체에게 메시지를 보내어 로직을 처리하는것이 더 옳은 설계인것은 내 머리에 잘 박히게 된 것 같다.  

 

'TIL > 자바' 카테고리의 다른 글

[자바] 로또 게임 구현  (0) 2020.05.18
[자바] Java와 JVM  (0) 2020.05.06
[테스트] JUnit 과 AssertJ  (0) 2020.04.23
[자바] 레이싱 게임 구현  (0) 2020.04.22
[자바] 코드 컨벤션  (0) 2020.04.06