객체지향 프로그래밍(Object-Oriented Programming, OOP)은 컴퓨터 프로그래밍의 패러다임 중 하나로, 필요한 데이터를 추상화하여 객체 간의 상호작용으로서 데이터를 처리하는 방식의 프로그래밍 설계 방법이다.
코드의 재사용을 통해 반복적인 코드를 최소화하고, 보다 유지보수가 수월한 프로그램을 만들 수 있다.
추상화는 불필요한 정보는 숨기고 중요한 정보만을 표현함으로써 프로그램을 간단하게 만드는 것
복잡한 개념을 관리 가능한 단순한 형태로 표현하는 것
단적인 예로 자동차를 추상화한다고 했을 때를 예로 들어보자면, 운전자는 단순히 시동걸기, 액셀, 브레이크 등의 조작법만 알고 있다면 운전을 하는데에 문제가 없다.
실제로 엔진 구동 메커니즘이나 가속이 어떤식으로 이루어지는지 까지는 몰라도 되는것이다.
class Car {
엔진시작() {
// 시동거는 복잡한 로직
}
가속() {
// 가속되는 복잡한 로직
}
감속() {
// 정지하는 복잡한 로직
}
}
class Driver {
constructor(name, car) {
this.name = name;
this.car = car;
}
시동걸기() {
if(this.car){
this.car.엔진시작();
}
}
감속하기() {
if(this.car){
this.car.감속();
}
}
가속하기() {
if(this.car){
this.car.가속();
}
}
}
const 차 = new Car();
const 운전자 = new Driver('BK', 차);
운전자.시동걸기();
운전자.가속하기();
운전자.감속하기();
이 코드에서 운전자(Driver)는 자동차(Car)의 내부 구조나 동작 원리를 알 필요 없이 시동 걸기, 가속하기, 감속하기 등의 인터페이스만 사용하여 차량을 제어할 수 있다.
클래스를 사용하는 사용자는 추상적으로 메서드 동작을 가늠해 사용할 수 있게 되는것이다.
'시동걸기? 시동을 거는거니까, 차를 운전할 수 있게 준비하는 거겠구나'와 같이 말이다.
이것이 추상화가 주는 큰 장점이다.
객체들의 공통적인 특징(속성, 기능)을 뽑아 이름을 붙이는 것
공통의 속성과 행위를 추출해 타입을 정의하는 것
자동차, 버스, 비행기, 지하철, 자전거의 공통적인 특징을 뽑아 하나로 묶어 표현하자면 '이동수단'으로 묶어 부를 수 있다.
이와 같이 공통적인 특징을 뽑아 이름을 붙히는것을 추상화라고 한다.
class 이동수단 {
전진();
후진();
}
class 사륜이동수단 extends 이동수단 {
방향전환();
}
class 자동차 extends 사륜이동수단 {
사이드브레이크();
}
프로젝트의 규모와 필요에 따라 추상화의 depth를 설계하는 것이 좋다.
필요 이상으로 복잡하지 않으면서 동시에 문제를 정확하게 해결하는 데 필요한 충분한 정보를 포함하는 설계가 바람직하다.
부모 클래스의 속성과 메서드를 자식 클래스에게 건네주는 것, 이를 통해 코드의 재사용성을 높이고, 중복을 줄일 수 있다.
위 추상화의 예시에서 extends
키워드를 통해 추상화된 클래스들을 이어주었는데 이것이 바로 상속이다.
하나의 변수 또는 함수가 상황에 따라 다른 의미로 해석 될 수 있는 것
다형성을 표현하기 위해서 오버라이딩과 오버로딩을 사용할 수 있다.
동물이라는 부모 클래스에서 강아지, 고양이, 새 등의 자식 클래스들을 생성하고 각 동물들에게 '소리내기'라는 메서드를 정의한다고 했을 때, 각각의 동물은 이 메서드를 자신이 내는 소리에 맞게 재정의 할 수 있다.
각 동물이 자신이 내는 소리에 맞게 재정의 하는 것을 오버라이딩이라고 한다.
오버라이딩
상위 클래스의 메서드를 하위 클래스에서 재정의하는 것
class Animal {
constructor(name) {
this.name = name;
}
소리내기() {
console.log(`${this.name}가(이) 소리를 냅니다.`);
}
}
class Dog extends Animal {
소리내기() {
console.log(`${this.name}가(이) 멍멍 소리를 냅니다.`);
}
}
class Cat extends Animal {
소리내기() {
console.log(`${this.name}가(이) 야옹 소리를 냅니다.`);
}
}
class Bird extends Animal {
소리내기() {
console.log(`${this.name}가(이) 짹짹 소리를 냅니다.`);
}
}
const 베리 = new Dog('베리');
const 나비 = new Cat('나비');
const 앵무 = new Bird('앵무');
베리.소리내기();
나비.소리내기();
앵무.소리내기();
오버라이딩을 통해 객체가 올바른 동작을 할 수 있도록 하게 해주며 이는 다형성을 띄게 만들어준다.
오버로딩
같은 이름의 메서드가 인자의 개수나 자료형에 따라 다른 기능을 하는 것
자바스크립트는 진정한 의미의 오버로딩을 지원하지는 않고, 나머지 매개변수 혹은 typeof로 매개변수의 갯수나 인자의 타입을 통해 오버로딩과 비슷한 효과를 낼 수 있다.
서로 연관되는 데이터와 그 데이터를 다루는 함수를 하나의 객체라는 캡슐에 묶는 것이다.
이를 통해 객체의 내부 상태를 외부에서 직접 접근하는 것을 제한하며 오직 객체가 제공하는 메서드를 통해서만 상태 변화가 가능하도록 해 낮은 결합성, 즉 클래스 간의 낮은 의존성을 가질 수 있다.
정보은닉을 통해 외부에서의 접근을 제한할 수 있다.
은닉화는 ES6 문법인 private field(#)를 통해 가능하다.
하나의 클래스/모듈은 하나의 책임(역할)을 가져야 한다.
하나의 책임이라는 의미는 하나의 기능만 도맡아 수행해야한다는 뜻이다.
즉, 변경이 필요한 이유가 하나여야 한다.
하나의 클래스는 하나의 기능을 담당하여 하나의 책임을 수행하는 데 집중할 수 있도록 해야한다.
소프트웨어 구성요소(클래스, 모듈, 함수 등)는 확장에는 열려있고, 변경에는 닫혀있어야 한다.
즉, 코드를 변경하지 않고 확장할 수 있어야 한다.
서브타입은 슈퍼타입으로 치환될 수 있어야 한다.
불필요하거나 사용하지 않는 프로퍼티를 가지고 있지 않아야 한다.
하나의 일반적 인터페이스보다는, 여러 개의 구체적인 인터페이스가 낫다는 것이다.
상위 모듈은 하위 모듈에 의존하면 안되며 둘 다 추상화에 의존해야 한다.