Notice
Recent Posts
Recent Comments
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Archives
Today
Total
관리 메뉴

DeFacto-Standard IT

[Java] 접근제어자를 사용하여 캡슐화를 하는 이유 본문

Java/Basic

[Java] 접근제어자를 사용하여 캡슐화를 하는 이유

defacto standard 2017. 10. 1. 04:41

자바에서는 private, protected 접근제어자를 사용하여 클래스 내의 필드를 직접적으로 접근하는 것을 제한하는 방법을 주로 사용한다.


이 경우, public으로 지정된 setter/getter 메서드를 사용하여 주로 연산을 한다.


이러한 것을 캡슐화라고 하며, 내부적으로만 필요한 연산들이나 필드를 외부에 노출시키지 않는 것으로 데이터 감추기라고도 한다.


이러한 캡슐화는 여러가지 장점이 존재한다.


1. 외부에 불필요한 정보를 제공하지 않는다.

 외부에 불필요한 정보를 제공하지 않는다는 것은, 프로그래머 입장에서 생산성을 향상시킨다.


 예를 들면, 개발자가 어떤 프로그램을 개발하고 있다고 가정하자.

이 개발자는 기존에 구현되어있는 Collection 중 ArrayList라는 것을 활용하면 빠르게 개발이 가능하다.


만약, 캡슐화를 하지 않은 ArrayList를 사용한다는 사실도 가정을 해보자. 다음과 같은 코드를 작성할 것이다.

ArrayList<Type> list = new ArrayList<Type>();

list.


문제는 여기서 발생한다. ArrayList 클래스는 여러가지 메서드와 필드가 정의되어있다. 그리고 모든 필드와 메서드가 public 접근지정자로 지정되어 현재 캡슐화가 되어있지 않기 때문에, 개발자는 아주 자유롭게 이를 제어할 수 있다.


자유롭다고 해서 좋은 것이 아니다.


프로그래머가 '객체.'을 누르는 순간 IDE는 개발자의 의도를 파악하여 Suggestion을 죽 늘어놓게 된다.

그리고 이 Suggestion에는 현재 클래스가 사용할 수 있는 필드와 메서드를 보여준다.

public으로 지정되어 있기 때문에 모든 필드와 모든 메서드를 보여줄 것이다.


그렇다면 개발자는 각 변수에 대한 의미, 서로 다른 메서드들 끼리 공용으로 사용되는 변수에 대한 의미를 전부 알고 코딩을 해야한다.


이것은 아주 피곤하다. 간단한 경우나 자신이 아주 잘 알고 있는 클래스라면, 사실 이렇게 해도 큰 문제가 발생하지 않는다.

하지만 처음 쓰는 클래스에 대해서 다룬다면 그 변수명을 어떻게 해석을 해야할 것인지에 대해 고민을 해야한다.


'메서드만 호출해도 되지 않냐'고 반문할 수 있다.


그렇다면 필드는 왜 IDE에서 Suggestion으로 띄우는 것인지를 대답할 수 있어야 한다. 어차피 메서드에서 처리할 것이면 필드는 안보여줘도 된다는 말이다.


만약 모든 필드를 private으로 지정하고, 이에 대한 연산을 제공하는 메서드만 제공된다면, 개발자 입장에서는 불필요한 suggestion을 받지 않아 보다 쾌적한 프로그래밍 환경을 제공받는 것이다.


그리고 이는 꼭 다른 클래스를 사용하여 위와 같은 위임(Delegation)을 사용하지 않고 스스로 만든 클래스라고 하더라도 동일하게 적용되는 부분이다.



2. 내부 데이터의 보호와, 이를 위한 소스코드 중복의 감소 및 유지보수성 향상

만약, public으로 설정되어 직접적인 접근을 할 수 있는 경우의 문제점을 알아본다.


예를 들어 Car라는 클래스는 velocity라는 필드가 있고, 이는 속도를 의미한다.

이 차는 최대 속도가 200km/h라고 가정하자.


그렇다면

Car car = new Car();

car.velocity = 200;

과 같이 코딩을 할 것이다.


여기서 200이라는 하드 코딩을 제거하고 변수를 사용한다면 다음과 같이 할 수도 있다.

int velocity = 200;

car.velocity = velocity;


변수를 둠으로써 조금 더 유지보수성이 향상되었다. 이제 200이라는 속도가 바뀔 때 마다 200이라는 숫자만 고치면 변수값을 참조하여 알아서 나머지 코드의 결과도 변하게 된다.


현재 Car 객체의 자동차 속도는 최대가 200km/h이다. 따라서 201 이상의 숫자가 들어오면 안된다. 최대 속도를 생각하니 최소 속도 역시 생각난다. 빠꾸하는 경우를 음수로 생각할 수도 있으나 ('속도'는 속력+방향 이므로) 간단하게 하기 위해 0이라고 가정하겠다.

 

따라서 프로그래머는 다음과 같이 코딩을 한다.

int velocity = 200;

if( velocity >=0 && velocity <= 200 )

car.velocity = velocity; 


... // 코드 작성중

여기서 문제가 발생한다. 아마 car의 velocity 변수는 수도 없이 조작될 것이다.

여러 군데에서 velocity라는 변수에 대해 참조한다는 의미이다.


그렇다면 최저와 최고 속도를 보장하여야 하는 if문을 일일이 작성할 것인가?

만약 차가 업그레이드 되어 210까지 최대속도를 낼 수 있다면 Ctrl+F로 모든 if문의 조건을 변경할 것인가?


물론 0와 200이라는 숫자 역시도 변수를 두어 위와같은 단적인 상황을 해결할 수는 있다.

그렇다면 Exception을 처리해야 하는 상황이거나 if문 안에서 여러 로직을 수행하는 경우는 어떻게 할 것인가?


예를 들어 if문이 false라면 A, B, C라는 로직을 수행한다고 가정하자.

그런데 개발을 하다보니 이런 저런 요구사항의 변경에 따라 A B C 간의 순서가 바뀌고 D E F 라는 로직이 추가된다고 가정하자.

여기서 A ~ F는 각종 Exception처리라던가 로깅이라던가 보안로직이라던가 DB저장이라던가 정보 출력 등의 여러가지의 로직이 될 수 있다.


이러한 것들은 변수를 둠으로써 해결할 수 있는 문제가 아니다.

따라서 모든 소스코드를 뒤져가며 일일이 변경해야한다.


만약 이 velocity라는 값을 세팅하는 부분이 적게 잡아 10000라인이라면? 

그러다가 나중에 저 1만라인 중 제대로 작성된 몇 라인을 모르고 수정해버렸다면? 근데 컴파일 에러는 안난다면?

G라는 로직이 추가된다면? 

또 로직의 순서가 바뀐다면?  

제대로 돌아가던 위의 코드가, 위에껄 밤새가며 1만라인에 해당하는 if else전부 건드렸는데 실수 한 번으로 제대로 작동 안한다면?

한 라인 변경을 안했다면?


나같으면 자살했다.


만약 캡슐화를 하여 메서드를 통해 접근한다면 어떻게 바뀌는지 알아보자.

Car car = new Car();

car.setVelocity(velocity);


와 같은 문장이 될 것이다.

Car 클래스의 setVeocity()메서드는 다음과 같다.


Car {

...

public void setVelocity(int velocity){

if(velocity >=lowestSpeedLimit && velocity <= highestSpeedLimit ))

this.velocity = velocity;

else{

// 로직 A ~ F 수행

}

}

}


끝이다.

1만 라인을 전부 볼 필요가 없다.

로직이 추가된다면 1라인만 추가하면 된다.

로직의 순서가 바뀐다면 바뀐것 끼리만 1번 씩 바꾸면 된다.

실수는 1초안에 해결이 가능하다.


이렇게 setter를 사용한다면 여러가지 조건을 걸러내거나 조건에 맞지 않는 경우에 대한 로직을 한 군데만 바꿔서 해결하는 것이 가능하다.


이러한 경우를 SW공학에서 '산탄총 수술'이라고 한다. 산탄총에 맞으면 몸 곳곳에 파편이 박히고, 이를 전부 해결하기 위해(총알을 빼내기 위해) 엄청난 수고를 들여야 한다는 것이다. 만약, 1개의 파편이라도 빼내지 못한다면 총상환자의 생명은 보장할 수 없다.


사실 '산탄총 수술'은 이러한 캡슐화의 개념보다는, 디자인패턴에서 클래스의 관계 또는 역할을 적절하게 설정하지 못했을 때 쓰이는 단어이지만 비슷한 상황이라서 말해봤다.


하지만 잘 생각해보면 이 역시, 클래스 각각의 역할을 잘못 설정했기 때문에 발생한 것이다.

SOLID 개념 중 SRP라는 것을 위반한 경우에 해당한다.


Main 클래스에서 Car의 Velocity에 대한 처리를 하는 것이 SRP를 위반한 것이다.

Car에서 스스로 Velocity에 대한 처리를 하도록 변경해야 하는 것이다.


SRP는 '단일책임원칙'이다.

Main 클래스는 car에 대해 속도를 세팅하는 부분만 명시하고,

Car 클래스는 실제로 속도에 대한 여러가지 Validation을 처리를 해야하는 책임이 있어야 하는 것인데,


Main에서 velocity값에 따른 if else문이나 A~G로직 등을 처리하는 등 너무 많은 책임을 지고 있기 때문에 발생하는 현상이다.


이러한 여러 가지 이유가 캡슐화를 이용하는 이유가 되겠다.

Comments