객체 지향 프로그래밍(이하 OOP)는 컴퓨터 프로그램을 “객체(Object)"들의 모임으로 파악하고자 하는 프로그래밍의 패러다임 중에 하나이다. 각 “객체(Object)" 들은 서로 메시지를 주고 받을 수 있으며 데이터를 처리할 수 있다.
위의 장점들을 관통하는 객체 지향 프로그래밍의 중요한 특성은 강한 응집력(Strong Cohesion)과 약한 결합력(Weak Coupling)을 지향한다는 점이다.
소프트웨어 공학에서 말하는,
응집력(cohesion) : 프로그램의 한 요소가 해당 기능을 수행하기 위해 얼마만큼의 연관된 책임과 아이디어가 뭉쳐있는지를 나타내는 정도. 프로그램의 한 요소가 특정 목적을 위해 밀접하게 연관된 기능들이 모여서 구현되어 있고, 지나치게 많은 일을 하지 않으면 그것을 응집력이 높다고 표현한다.
결합력(coupling) : 프로그램 코드의 한 요소가 다른 것과 얼마나 강력하게 연결되어 있는지, 얼마나 의존적인지를 나타내는 정도. 결합력이 낮다는 것은 한 요소가 다른 요소들과 관계를 크게 맺고 있지 않은 상태를 의미한다.
OOP의 경우 클래스에 하나의 문제 해결을 위해 데이터를 모아 놓은 객체를 활용한 프로그래밍을 지향하므로 응집력을 강화하며, 클래스 간에 독립적으로 디자인함으로써 결합력을 약하게 할 수 있다.
필드에 몬스터들을 배치하고 싶다.
몬스터의 종류는 드래곤, 좀비, 해골이 있다.
각 몬스터들은 시간이 흐르고 레벨업을 할 수 있다.
위의 상황에서 객체들을 어떻게 디자인해야 할까?
몬스터를 만들어 봅시다.
몬스터는 체력(healthPoint)과 레벨(level), 서식지(habitat) 속성을 가지고 있으며,
레벨을 1단계 올릴 수 있는 메소드(levelUp)을 가지고 있다.
이 특성들은 모든 몬스터들이 가지는 특성이므로 몬스터라는
클래스의 속성으로 부여해 줄 수 있다.
아래는 몬스터 객체를 만드는 JavaScript 코드이다.
var monster = {
level : 0,
habitat : 'forest',
levelUp: function () {
this.level++;
}
};
자 이제 몬스터를 만들 수 있다. 그런데 이 몬스터에 다양한 다른 작업들을 하고 싶다. OOP의 컨셉을 설명하면서 다양하게 조작해보고자 한다.
캡슐화는 객체의 데이터를 외부에서 직접 접근하지 못하게 막고, 함수를 통해서만 조작이 가능하게 하는 작업이다.
몬스터의 레벨(level)과 같은 경우는 이용자가 임의로 조작하게 하면 문제가 생길 가능성이 있다. 그래서 레벨업(levelUp)이라는 함수를 통해서만 레벨이 조작되었으면 좋겠다.
아래의 코드는 외부에서 level변수에 직접 접근하지 못하도록 막는 캡슐화를 나타내었다.
var monster = createMonster();
function createMonster () {
var level = 0;
var openInfo = {
habitat : 'forest',
levelUp : function () {
level++;
}
};
return openInfo;
}
이전의 몬스터 객체처럼 monster.level로 몬스터의 level데이터에 접근할 수 없게 되었다. level은 오직 monster.levelUp이라는 메소드(method)로만 조작할 수 있다.
추상화는 객체들이 가진 공통의 특성들을 파악하고 불필요한 특성들을 제거하는 과정을 말한다. 객체들이 가진 동작들을 기준으로 이용자들이 동작만 쉽게 구동할 수 있도록 한다. 몬스터의 예제에서 레벨업(levelUp) 메소드를 실행만 하면 level이라는 속성을 컨트롤 할 수 있었던 것처럼.
이러한 추상화 과정을 통해 이용자들은 프로그래머가 만든 객체를 더 쉽게 사용할 수 있게 된다.
추상화를 할 때 주의할 점은 속성 위주가 아닌 동작 위주로 정의하는 작업을 하는 것이다. 객체의 동작에 연관이 되지 않는 속성들은 결국은 불필요하다. 따라서 불필요한 속성들을 걸러내기 위해 동작을 먼저 정의하고 동작에 필요한 속성들을 정리하는 것이 좋다.
몬스터들이 공격을 받아 healthPoint가 0이 되면 필드에서
죽은 것(death)으로 처리하고 싶다.
이용자들은 몬스터들을 공격하고 죽는 모습을 보기만 하면 된다.
어떠한 과정을 거쳐서 몬스터가 죽었는지에 대해서는 알 필요가 없다.
위의 케이스에서는 객체에 2가지 속성이 추가되어야 한다. 몬스터가 죽은 상태인지 살아있는 상태인지를 확인하는 속성, 그리고 healtpoint라는 속성이다. 위의 속성을 추가하고 공격을 받는 상황을 메소드(method)로 만들어보겠다.
var monster = {
level : 0,
habitat : 'forest',
healthPoint = 100,
isLive = true,
levelUp: function () {
this.level++;
}
damaged: function (damage) {
this.healthPoint -= damage;
if (this.healthPoint === 0){
this.isLive = false;
}
}
};
이젠 몬스터는 체력(healthPoint)를 가지고 있고 데미지를 받아 체력이 깍이다가 체력이 0이 되면 죽는다. 이렇게 어떠한 동작과 속성을 정의하고 불필요한 정의들을 삭제하여 이용자가 편리하게 객체를 이용할 수 있도록 구성한 것이 추상화이다.
몬스터를 여러 마리 만들어야 한다. 각 몬스터들은 모두 같은 속성을 가지고 있다. 몬스터 객체를 이용하여 모든 몬스터들이 같은 속성과 메소드를 가질 수 있도록 만들 수 있다.
OOP의 가장 큰 특성 중 하나가 바로 코드의 재사용성과 상속의 개념이다. 같은 객체를 여러 개 만들어야 하는 경우, 한 번 작성된 코드를 활용하여 동일한 객체를 만들 수 있다.
JavaScript 에서는 생성자 함수(Constructor)와 팩토리 함수(Factory)를 통해 재사용성을 유지하면서 여러 객체를 생성할 수 있다.
function Monster () {
this.healthPoint = 100;
this.habitat = 'forest';
this.level = 1;
}
Monster.prototype.levelUp = function () {
this.level++;
}
var broMonster= new Monster();
var childMonster = new Monster();
var parentMonster= new Monster();
위의 생성자 함수를 이용하여 각각의 몬스터 종류를 생성할 수 있다.
위의 코드처럼 생성자 함수를 이용하는 방법 이외에도 Factory Function을 이용하는 방법도 있다. Factory Function은 간단히 객체를 리턴하는 함수라고 생각하면 된다.
Factory Function 으로 구성한 몬스터 객체,
var monsterPrototype = {
levelUp : function () {
this.level++;
}
}
function createMonster () {
var monster = Object.create(monsterPrototype);
monster.level = 1;
monster.habitat = 'forest';
return monster;
}
var broMonster= new createMonster();
var childMonster = new createMonster();
var parentMonster= new createMonster();
여기서 추가로 상속(inheritance) 개념이 있다. 이 부분은 Prototype Chain과 함께 포스팅하도록 하겠다.
By Cyrano on July 27, 2019.
안녕하세요. 게시글 잘 봤습니다.
이젠 몬스터는 체력(healthPoint)를 가지고 있고 데미지를 받아 체력이 깍이다가 체력이 0이 되면 죽는다. 이렇게 어떠한 동작과 속성을 정의하고 불필요한 정의들을 삭제하여 이용자가 편리하게 객체를 이용할 수 있도록 구성한 것이 추상화이다.
이 부분에서 이해가 잘 되지 않는 내용이 있는데요.
이용자가 편리하게 객체를 이용할 수 있도록 구성한 것이 추상화라고 적어주셨는데 몬스터가 죽는 것도 이용자가 객체를 이용했다고 볼 수 있는 건가요? 몬스터가 죽는 건 서버 측의 행위인 것 같은데...
제가 본 oop 설명글 중에 제일 이해가 잘되는거 같아요! 좋은글 감사합니다🙏🏻