const obj = new Object;
라는 코드가 있다면 변수에 할당된 오브젝트가 바로 인스턴스이다.
이 말은 new Object가 obj에 할당되었고 , 동시에 obj변수의 instance가 된다.
인스턴스를 생성하는 이유는 간단하다. 각 인스턴스마다 다른 값을 용이하게 유지보수하기 위함이다.
가령 인스턴스를 생성하지 않았다고 하면, 클래스 고유의 값을 매번 변경해 주어야 한다. 다형성이 현저히 낮아진다.
객체지향 프로그래밍 OOP란 즉, 인스턴스를 이용한 다형성을 부여하는 획기적인 일이다. 그로 인해 유지보수가 월등히 쉬워진 것이다.
class Shape {
constructor(width, height, color) {
this.width = width;
this.height = height;
this.color = color;
}
draw() {
console.log(`drawing ${this.color} color!`);
}
getArea() {
return this.width * this.height;
}
}
class Triangle extends Shape {
draw() {
super.draw(); // 부모 함수 호출 super.method()
console.log('🔺');
}
위의 코드에서 Triangle 이란 클래스에 Shape class를 확장시켰다.
이렇게 함으로써, 공통적으로 가진 특성을 확장시켜 다형성을 늘린 것이다.
여기서 shape class의 this 키워드를 보면, 해당 클래스에 직접적인 property를 저장한다.
이제 Shape class를 extends한 Triangle class를 보자.
shape에도 있는 draw method를 다시 정의한다. 이렇게 하면 해당 메소드를 현 클래스 입맛에 맞게 바꿀 수 있다.
입맛에 맞게 바꿈과 동시에 기존 메소드를 배껴오고 싶을 수도 있다.
그럴 때는 super.method()
를 사용하여 상속하는 클래스의 메소드를 그대로 가져올 수 있다.
class Rectangle extends Shape {} // Shape을 상속하는 Rectangle
class Triangle extends Shape {}
const rectangle = new Rectangle; // new object 생성
const triangle = new Triangle;
console.log(rectangle instanceof Rectangle); // 왼쪽의 객체가 우측 클래스의 instance인지 확인 // true
console.log(triangle instanceof Rectangle); // false
console.log(triangle instanceof Triangle); // true
console.log(triangle instanceof Shape); // true
console.log(triangle instanceof Object); // true , JS의 모든 object는 class Object를 상속한다. , 그말은 즉슨 Object의 메소드를 쓸 수 있다.
객체 지향 5대 원칙이라고도 불리운다.
SOLID를 풀어 쓰면,
SRP(단일 책임 원칙)
OCP(개방-폐쇄 원칙)
LSP(리스코프 치환 원칙)
ISP(인터페이스 분리 원칙)
DIP(의존 역전 원칙)
의 앞글자를 따서 SOLID 원칙이라 통칭한다.
여기서 책임이란, 기능 정도의 의미로 해석한다.
한 클래스,함수에서 수행하는 기능이 많아질수록 유지보수에 불리하다.
SRP가 안지켜진 사례에는 아래의 경우들이 있다.
기능 확장에 개방적이고, 기능 수정에 패쇄적이어야 한다.
이는 연관되는 기능을 묶는 추상화를 통해 이루어진다.
상점을 예로 들자면, 상점 직원이 아무리 바뀌어도 손님은 상품을 구매하는데 지장이 없다.
이는 직원은 판매인터페이스를 구현하고, 손님은 판매인터페이스와만 소통하기 때문이다.
부모 클래스와 자식 클래스 사이의 행위에는 일관성이 있어야 한다는 원칙이며, 이는 객체 지향 프로그래밍에서 부모 클래스의 인스턴스 대신 자식 클래스의 인스터스를 사용해도 문제가 없어야 한다는 것을 의미한다.
이해를 돕기위해 도형을 예시를 들어보자. 도형 클래스와 사각형 클래스가 있고, 사각형 클래스는 도형 클래스의 상속을 받는다고 가정하자.
(1) 도형은 둘레를 가지고 있다.
(2) 도형은 넓이를 가지고 있다.
(3) 도형은 각을 가지고 있다.
일반화 관계(일관성인지 확인하는 방법은 단어를 교체해 보면 알 수 있다. (1) ~ (3)의 도형이란 단어 대신 사각형을 넣어보자.
(1) 사각형은 둘레를 가지고 있다.
(2) 사각형은 넓이를 가지고 있다.
(3) 사각형은 각을 가지고 있다.
(1) ~ (3) 모두 딱히 이상한 부분이 보이지 않는다. 따라서 도형과 사각형 사이에는 일관성이 있다고 할 수 있다.
여기서 원(Circle) 이라는 도형에 대해 생각해보자. 원 클래스 역시 도형 클래스의 상속을 받는다고 가정하자. 앞에서 언급한 (1) ~ (3)의 도형 단어 대신 원을 대입해보자.
(1) 원은 둘레를 가지고 있다.
(2) 원은 넓이를 가지고 있다.
(3) 원은 각을 가지고 있다.
문장을 읽어보면 (3)번 문장이 어색하다는 것을 알 수 있다. 따라서 도형 클래스는 LSP을 만족하지 않은 설계라 할 수 있다. 따라서 (3)문장에 대해서는 일반화 관계가 성립하도록 수정되어야 한다.
한가지 예를 들어보자. 우리는 스마트폰으로 전화, 웹서핑, 사진 촬영 등 다양한 기능을 사용할 수 있다.
그런데 전화를 할 때에는 웹서핑, 사진촬영 등 다른 기능은 사용하지 않는다.
따라서 전화기능과 웹서핑 기능 사진 촬영 기능은 각각 독립된 인터페이스로 구현하여, 서로에게 영향을 받지 않도록 설계해야 한다.
이렇게 설계된 소프트웨어는 인터페이스 분리 원칙을 통해 시스템의 내부 의존성을 약화시켜 리팩토링, 수정, 재배포를 쉽게 할 수 있다.
변화하기 쉬운 것은, 구체화 된 클래스를 의미하고, 어려운 것은 추상화된 클래스로 이해한다.