[Type Script] OOP, 클래스 상속, 접근 제어자, static 키워드
객체 지향 프로그래밍
Object Oriented Programming, OOP
객체를 다루는 프로그래밍 스타일을 말한다.
객체 지향 언어를 사용하는 개발자는 클래스에 특정 API를 적용하는 방법으로 인터페이스를 사용한다.
클래스 복습
- 멤버 변수로 클래스를 선언할 수 있다.
- 클래스 내 생성자를 선언할 수 있고, 인스턴스 생성 중 한 번만 호출된다.
- Ts 클래스를 ES5에선 Js 생성자 함수로, ES6에선 Js 클래스로 컴파일된다.
- 클래스 생성자의 파라미터를 readonly, public, protected, private 키워드로 정의하면 Ts는 각 파라미터에 대한 클래스 프로퍼티를 만든다.
클래스 상속
상속이란 어떤 클래스가 다른 클래스로부터 기능을 상속받는 것을 말한다.
extends 키워드는 한 클래스가 다른 클래스를 상속함을 선언할 때 사용된다.
위 코드에서 Employee 클래스는 Person 클래스를 상속받고, department라는 새로운 프로퍼티를 만들었다.
그 다음, new 키워드를 사용해 Employee 클래스의 생성자를 만든다.
13번째 줄에서 empl. 를 입력하고 ctrl + space를 누르면 age, firstName, lastName, department 프로퍼티가 뜨는 것을 확인할 수 있다.
Employee 클래스는 Person 클래스의 자식 혹은 서브 클래스라고 말할 수 있다.
* 자바스크립트는 프로토타입 기반 객체 지향 언어이다.
js의 모든 객체는 프로토타입이라는 객체를 가지고, 프로토타입으로부터 프로퍼티와 메서드를 상속받는다.
그리고 런타임 도중 프로토타입으로 한 객체를 다른 객체에 할당한다.
클래스의 멤버(프로퍼티, 메서드)를 접근하고 제어하는 방법
: public, private, protected 접근 제어자를 사용한다.
- public : 모든 내부 및 외부 클래스에서 접근 가능, ts에선 기본적으로 접근권한이 public이기 때문에 프로퍼티나 메서드 앞에 public을 붙여도 접근 권한 변화 X
- protected : 동일 패키지에 속하는 클래스와 서브 클래스 관계일 때만 접근 가능
- private : 클래스 내에서만 접근 가능
js에는 private, protected 키워드가 없기 때문에 컴파일 단계에서 이 키워드를 삭제한다.
따라서 접근 제어자는 개발 편의성을 목적으로 사용된다.
위 코드를 작성했을 때, Employee 클래스의 increasePay 메서드에서 this.을 입력하고 ctrl + space를 눌렀을 때
private인 age 프로퍼티는 접근할 수 없다는 것을 알 수 있다.
Employee의 인스턴스에서 Person 클래스에서 protected인 sayHello 메서드를 호출하려 하면 컴파일 에러가 발생한다.
protected 클래스 멤버는 서브 클래스에서는 접근 가능하지만 클래스 인스턴스에선 접근 할 수 없기 때문이다.
위 코드처럼 코드를 좀 더 간결하게 작성할 수 있다.
Person 클래스에서 생성자의 파라미터로 firstName, lastName, age를 선언해서 클래스 프로퍼티를 만든다.
Person 클래스의 인스턴스를 생성할 수 있지만 private인 age 프로퍼티는 접근할 수 없다.
사실 위 코드는 js로 컴파일될 때 private가 없어지기 때문에 출력은 잘 된다.
따라서 실제 개발 프로젝트에선 컴파일러의 noEmitOnError 옵션을 사용해 모든 Ts 오류가 없어야
Js가 생성되도록 설정하는 것이 일반적이다.
* 명시적 선언 방식은 코드 가독성을 높이지만 암시적 선언 방식은 코드를 더 간결하게해줌
대부분의 경우 단순 프로퍼티의 초기화의 경우 암시적 선언을 사용한다.
고정 변수와 싱글톤
ES6부터 클래스의 각 인스턴스가 일부 프로퍼티를 공유해야 하는 경우 정적 프로퍼티로 선언하게 되었다.
이 정적 프로퍼티를 선언하려면 Ts에선 static 키워드를 사용한다.
먼저 static 프로퍼티와 private 생성자로 싱글톤 디자인 패턴을 구현해보기 위해 간단한 사격게임을 만들어보자.
한 번 총을 쏠 때마다 총알의 개수는 1씩 감소하며 전체 총알 개수를 알려줘야 한다.
왼쪽은 static으로 전체 총알 개수를 나타내는 변수를 만들고, 오른쪽은 public으로 만들었다.
static으로 작성한 코드는 99 98 이 출력되고, public으로 작성한 코드는 99 99 가 출력된다.
먼저 static으로 작성한 코드를 보자.
Gangsta 클래스의 인스턴스인 g1, g2는 동일한 변수 totalBullets를 공유한다.
static 클래스 멤버는 앞에 해당 클래스 이름을 붙이면 접근이 가능하다. (Gangsta.totalBullets)
다음, public으로 작성한 코드를 보자.
두 인스턴스를 생성하고 shoot 메서드를 호출한다.
두 인스턴스는 똑같은 클래스를 인스턴스화하긴 했지만 totalBullets가 정적 변수가 아니기 때문에
서로 값을 공유하지 못한다.
* static 멤버는 서브 클래스에 복사되지만 공유는 되지 않아 Gangsta 클래스와 서브 클래스의 totalBullets은 다른 값임
싱글톤이란?
: 단 하나의 인스턴스를 생성하는 디자인 패턴
한 곳에서만 앱 전체 상태를 관리하며, 외부에서 접근 가능한 객체를 만들어보자
-> 전체 데이터를 한 곳에서 관리하는 원칙을 단일 데이터 소스 or 단일 진실 공급원이라함
먼저 new 키워드로 여러 인스턴스를 생성할 수 있기 때문에 new 키워드 사용을 막아야 한다.
즉, new 키워드로 private인 클래스 생성자의 새로운 인스턴스를 만들면 오류가 발생하게끔 만들어야 한다.
단일 인스턴스를 만드는 방법
클래스 생성자가 private인 경우 클래스 내에서만 접근 가능하다는 문제가 있다.
따라서 인스턴스가 불가능한 클래스 내부 메서드를 외부에서 호출하려면 어떻게 해야할까?
: static 키워드로 클래스 메서드를 정적 메서드로 만들면 특정 인스턴스가 아닌 클래스에서만 속하도록 만들 수 있다.
위 코드에서 AppState 클래스의 counter는 외부에서 접근 가능하고, 단일 인스턴스에만 그 값을 저장한다.
AppState 클래스는 private 생성자가 있으므로 new 키워드로 인스턴스 생성이 불가능하다.
getInstance() 메서드로 AppState 클래스의 생성자를 호출할 수 있으며 정적 메서드로 클래스 인스턴스가 없을 때 호출된다.
appState1로 getInstance 메서드를 호출할 때 AppState의 instanceRef는 인스턴스화되지 않아 undefined 값을
가지기 때문에 AppState를 인스턴스화한다.
appState2로 getInstance 메서드를 호출할 땐 이미 instance Ref는 AppState 객체를 인스턴스화했기 때문에 다시 인스턴스화 하지 않는다.
appState1, appState2는 같이 AppState 객체를 참조한다.
따라서 4 4 가 출력된다.
담소
자바 쪼금 했었지만 객체 지향은 많이 안 해봤다... 그래서 이해하는 데 조금 어려움은 있었지만
오늘도 글로 정리하면서 내 생각도 정리된 듯!!
홧팅홧팅