하나의 class(클래스)가 하나의 instance(인스턴스) 만 갖는 것을 보장하는 형식이다. 이를 통해 어떤 클래스의 인스턴스가 여러 곳에서 생성되지 않고 하나의 고유한 인스턴스만 사용되도록 보장할 수 있다. ts의 class도 싱글톤 패턴이 있다.
class GameManager {
private constructor() {}
public game = 'Legend Of League';
private static Instance: GameManager | null = null; // 초기값은 null
public static getInstance(): GameManager {
if (this.Instance === null) { // 최초 생성 시에만 인스턴스를 생성한다
this.Instance = new GameManager();
}
return this.Instance; // 생성된 것이 있다면 그것을 반환한다
}
}
const gameManager = GameManager.getInstance(); // class 내부에 매게체를 만들어 생성해하도록 한다.
gameManager.game; // 접근 가능
const gameManager2 = GameManager.getInstance();
console.log(gameManager === gameManager2); // true, 같은 오브젝트이기 때문이다
class가 다른 특정 class의 기능을 이어받을 수 있게 하는 기능이다.
ts 클래스의 상속은 객체 지향 프로그래밍(OOP)에서 기존 클래스의 속성과 메서드를 재사용하고 확장하기 위한 개념과 동일하다. 상속을 사용하면 부모 클래스(또는 슈퍼 클래스)의 특성을 자식 클래스(또는 서브 클래스)가 상속받아 사용할 수 있다. ts의 상속에는 다음과 같은 특징이 있다.
상속 구문
extends
키워드를 사용하여 상속
메서드 오버라이딩
자식 클래스에서 부모 클래스의 메서드를 다시 정의하여 메서드의 동작을 변경하거나 확장할 수 있다. 이를 오버라이딩이라고 한다.
super
자식 클래스에서 부모 클래스의 생성자나 메서드를 호출할 때 super
키워드를 사용한다.
다중 상속 불가능
ts에서는 단일 상속만 지원하므로 하나의 자식 클래스는 여러 부모 클래스로부터 상속받을 수 없다.
접근 제한자
상속된 속성과 메서드의 접근 범위는 부모 클래스에서의 접근 제한자에 따라 결정된다. private
멤버는 자식 클래스에서 접근할 수 없으며 protected
와 public
제한자만 접근이 가능하다.
상속받는 자식 class가 constructor
가 없다면 부모의 constructor
을 이어 받음
class Person {
constructor(protected _name: string, private _age: number) {
}
public print(): void{
console.log(`${this._name}님 안녕하세요! 나이는 ${this._age}세 이군요!`);
}
protected printName(): void {
console.log(this._name);
}
}
// extends 키워드로 Person을 상속
class Student extends Person {
constructor(name: string, age: number) {
super(name, age);
this.printName(); // Person을 상속 받았으므로 Person 클래스의 멤버를 사용 가능
}
}
const person: Person = new Person('Grack', 46);
person.printName(); // protected로 선언된 멤버는 일반 인스턴스에서 호출 불가능
person.print();
const studnet: Student = new Student('Plex', 53);
studnet.print();
/*
Grack님 안녕하세요! 나이는 46세 이군요!
Plex
Plex님 안녕하세요! 나이는 53세 이군요!
*/
클래스 상속은 코드의 재사용성과 유지보수성을 높여주는 중요한 개념이다. 부모 클래스의 공통된 특성을 한 번 정의하고, 이를 여러 자식 클래스에서 사용하며, 필요한 경우 자식 클래스에서 특화된 동작을 구현할 수 있다. class의 상속은, 각각의 class나 오브젝트가 복잡해지며 공통된 특징이 집합되어있을 때 구분하여 사용하면 좋다.
abstract
키워드를 활용하면 완전하지 않은 class를 만들 수 있다. 완전하지 않은 class는 new
를 통해 인스턴스를 만들 수 없다. 대신 다른 class에서 상속을 받아 기능을 구현하고 확장하는 용도로 사용된다.
abstract class는 class의 앞부분에 abstract
를 쓰고, 추상화할 멤버에 abstract
를 쓴다.
또한 abstract
키워드를 통해 만들어진 멤버는 자식에서 반드시 구현해야한다.
abstract class Shape {
abstract calculateArea(): number; // 추상 메서드, 하위 클래스에서 반드시 구현해야 함
constructor(protected color: string) {}
public displayInfo(): void {
console.log(`This color is ${this.color}.`);
}
}
// 추상클래스를 상속받아 만든 자식 클래스 1
class Circle extends Shape {
constructor(color: string, public radius: number) {
super(color);
}
calculateArea(): number { // 추상 메서드를 자식 클래스에서 구현
return Math.PI * this.radius * this.radius;
}
}
// 추상클래스를 상속받아 만든 자식 클래스 2
class Rectangle extends Shape {
constructor(color: string, public width: number, public height: number) {
super(color);
}
calculateArea(): number { // 추상 메서드를 자식 클래스에서 구현
return this.width * this.height;
}
}
const circle = new Circle("red", 5);
const rectangle = new Rectangle("blue", 4, 6);
console.log("Circle area:", circle.calculateArea());
console.log("Rectangle area:", rectangle.calculateArea());
circle.displayInfo(); // 추상 클래스의 public 메서드이므로 호출 가능
rectangle.displayInfo(); // 추상 클래스의 public 메서드이므로 호출 가능
/*
Circle area: 78.53981633974483
Rectangle area: 24
This shape is red.
This shape is blue.
*/
일반 클래스와 추상 클래스의 차이점을 정리하자면 다음과 같다.
인스턴스 가능 여부
일반 클래스는 인스턴스가 가능하지만 추상클래스는 불가능하다.
사용 용도와 목적
공통된 특정 요소를 모아 이를 통합하여 자식에게 정보를 제공하는 템플릿이란 목적은 동일하지만, 추상클래스가 더욱 일반 클래스보다 미완성적이다.
추상 멤버 구현 강
추상 클래스에서 구현한 추상 멤버는 자식에서 강제로 구현해야지만 일반 클래스는 강제적이지 않다.