
공부한 내용을 정리한 글입니다.
비판적으로 읽으시고, 부족한 부분이 있다면 댓글로 알려주세요.
위키백과에서는 캡슐화를 다음과 같이 설명한다.
하지만 위 설명 만으로는 왜 객체의 속성과 행위를 묶어야 하는지, 왜 일부 내용을 은닉해야 하는지, 이를 통해 무엇을 달성하고자 하는지 이해하기 어렵다.
객체지향에서 캡슐화를 따름으로 어떤 이점을 가져갈 수 있을지 알아보자.
Human이라는 data fields가 있고, 이를 인자로 받는 함수들이 있다고 하자
interface Human {
name: string;
age: number;
hunger: number;
}
function work(human: Human){
human.age++;
human.hunger++;
}
function eat(human: Human) {
human.hunger = 0;
}
지금은 Human을 사용하는 함수가 work()와 eat() 두 개뿐이다.
Human의 interface를 수정하면 여러 파일을 돌아다니며 기존에 작성한 함수에 문제가 없는지 확인해야 한다.
이러한 문제를 해결하기 위해 정의된 interface와 함수들을 하나의 파일에서 관리할 수 있다.
예를 들면 human.ts 라는 파일을 만들고 interface와 함수들을 이 안에서 관리한다면, inteface를 수정하고 바로 같은 파일에서 다른 함수들을 확인하며 수정할 수 있다.
하지만, class를 이용하면 이를 data field와 함수들을 자연스럽게 함께 두며 관리할 수 있다.
class Human {
name: string = 'yun seong';
age: number = 28;
hunger: number = 100;
work(){
this.age++;
this.hunger++;
}
eat(){
this.hunger = 0;
}
...
}
class를 정의하여 data field와 함수들을 함께 둠으로써 코드의 유지보수성을 향상시켰다.
하지만, 이 정도로 충분할까?
상상력을 발휘해보자, 갑자기 화면에 출력되는 Human의 age가 -10이 되었다면 우리는 어떻게 대처해야 할까?
위처럼 단순히 모아 놓기만 한 class를 사용했다면, Human 가져가 구현한 모든 코드를 확인해야 한다. 프로젝트가 커질수록 확인해야 할 코드의 양도 많아질 거다.
이를 어떻게 해결할 수 있을까?
class Human {
private _name: string = 'yun seong';
private _age: number = 28;
private _hunger: number = 100;
get name(){
return this._name;
}
get age(){
return this._age;
}
get hunger(){
return this._hunger;
}
work(){
this._age++;
this._hunger++
}
eat(){
this._hunger = 0;
}
...
}
private 접근 제어자를 사용해 내부 data field를 직접적으로 수정할 수 없게 만들고 class 외부에서는 getter 함수를 통해 data field를 사용할 수 있다.
이를 통해 Human class에 관련된 문제가 발생했을 때, class를 정의한 코드만 확인해도 문제를 추적할 수 있도록 완전한 책임을 진다.
여기서 재밌는 부분은 완전한 책임은 구현체 단위로 적용되는 것이 아니라, class 단위로 적용된다는 것이다.
class Human{
...
private _hunger = 100;
feed(human: Human){
human._hunger = 0
}
...
}
위에서 feed()가 다른 Human 구현체를 받아 _hunger를 수정한 것처럼, 같은 class의 구현체라면 서로 private한 data field에 접근하고 수정할 수 있다.
나는 이런 동작이 허용되는 이유가 feed() 는 Human class 내부에서 정의되어있고, feed()로 인해 다른 구현체에 버그를 유발하더라도 Human class 내부에서 확인 가능하기 때문이라고 느꼈다.
캡슐화는 data field와 이에 관련된 함수들을 함께 두고, 완전한 책임을 가짐으로, 버그 발생 시 해당 class를 정의한 code만 확인해도 버그를 추적할 수 있도록 해준다.