티스토리 뷰

개발공부/🟦 TypeScript

[TS] 객체 지향 프로그래밍 (OOP)

2022. 10. 26. 21:00
 

JavaScript With Syntax For Types.

TypeScript extends JavaScript by adding types to the language. TypeScript speeds up your development experience by catching errors and providing fixes before you even run your code.

www.typescriptlang.org

 

 

1️⃣  객체 지향 프로그래밍 (OOP)이란?

:  컴퓨터 프로그램을 객체(Object)의 모임으로 파악하려는 프로그래밍 패러다임

 

-  객체(Object)들은 서로 메시지를 주고 받을 수 있으며 데이터를 처리할 수 있다.

 

이미지출처 : 엘리스 타입스크립트 I 강의자료

 

 

 

객체지향 프로그래밍의 장점

1)  프로그램을 유연하고 변경이 용이하게 만든다.

2)  프로그램의 개발과 보수를 간편하게 만든다.

3)  직관적인 코드 분석을 가능하게 한다.

 

 

 

객체 지향 프로그래밍의 중요한 특성

:  강한 응집력(Strong Cohesion)과 약한 결합력(Weak Coupling)을 지향한다.

 

 

💡 용어 정리

-  응집력 (cohesion)  :  하나의 모듈 내부 요소들이 하나의 목적을 위해 연결되어있는 정도

-  결합력 (coupling)  :  서로 다른 모듈 간에 상호 의존하는 정도 or 연관된 정도

 

응집도가 좋은 예시

 

응집도가 안좋은 예시

 

 

 

 

2️⃣  Class (클래스)

 

클래스의 요소

-  필드 (field)

-  생성자(constructor)

-  메소드 (method)

 

→  이 3개를 통칭해서 멤버 (member)라고 한다.

 

 

인스턴스(instance)   :  new 연산자에 의해서 생성된 객체

 

 

 

Class 생성하기

class Person {
    name: string;
    constructor(name: string) {
        this.name = name;    // 클래스 안에서 this.를 앞에 붙이면 클래스의 멤버를 의미
    }
    say() {
        return "Hello, My name is " + this.name;
    }
}

let person = new Person("june");    // new를 사용하여 Person 클래스의 인스턴스를 생성
person.say();    // Hello, My name is june 출력

-  위의 코드에서 Person class의 멤버는 name, constructor, say()

 

 

 

 

 

▶  상속

OOP는 상속을 이용하여 존재하는 클래스를 확장해 새로운 클래스를 생성할 수 있다.

class Animal {
    move(distanceInMeters: number) {
        console.log(`Animal moved ${distanceInMeters}m.`);
    }
}

class Dog extends Animal {
    makeSound() {
        console.log("멍멍!");
    }
}

class Cat extends Animal {
    makeSound() {
        console.log("야옹!");
    }
}

const dog = new Dog();
dog.move(10);
dog.makeSound();

const cat = new Cog();
cat.move(5);
cat.makeSound();

-  extends 키워드로 Animal이라는 기초 클래스에서 Dog 클래스가 파생

-  파생된 클래스는 하위클래스(subclass), 기초 클래스는 상위클래스(superclass)

-  Dog, Cat은 Animal의 기능(move 메소드)을 확장하기 때문에 move()와 makeSound()를 가진 인스턴스를 생성

 

 

 

 

 

 

▶  접근 제어자

속성 또는 메소드로의 접근을 제한하기 위해 사용한다.

 

-  TypeScript에는 public > protected > private 접근 제어자가 존재

-  Java와 다르게 package 개념이 없어 default 접근 제어자는 존재하지 않음

 

이미지출처 : 엘리스 타입스크립트 I 강의자료

 

 

 

 

public 접근 제어자

:  프로그램 내에서 선언된 멤버들이 자유롭게 접근 가능하다.

class Animal {
    public name: string
    constructor(theName: string) {
        this.name = theName;
    }
}

new Animal("Cat").name ;

-  TypeScript에서 멤버는 기본적으로 public으로 선언

-  명시적으로 멤버를 public으로 표시할 수 있음

 

 

 

 

private 접근 제어자

:  멤버가 포함된 클래스 외부에서의 접근을 막는다.

class Animal {
    private name: string
    constructor(theName: string) {
        this.name = theName;
    }
}

new Animal("Cat").name    // Error: Property 'name' is private and only accessible within class 'Animal'

 

 

 

 

protected 접근 제어자

:  멤버가 포함된 클래스와 그 하위 클래스를 제외한 외부에서의 접근을 막는다.

class Person {
    protected name: string
    constructor(name: string) {
        this.name = name;
    }
}

class Employee extends Person {
    private department: string
    constructor(name: string, department: string) {
        super(name);
        this.department = department;
    }
    public getElevatorPitch() {
        return 'Hello, my name is ${this.name} and I work in ${this.department}.';
    }
}

let howard = new Employee("Howard", "Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name);    // Error: Property 'name' is protected and only accessible within class 'Person' and its subclasses.

-  Person에서 파생된 Employee의 인스턴스 메소드에서는 name을 사용 가능

 

 

 

 

 

 

▶  Getters & Setters

비공개로 설정하려는 속성은 private로 설정하고, 속성값을 읽고 수정하는 getter/setter 함수를 사용한다.

class Person {
    private _name: string
    
    get name() {
        return this._name;
    }
    set name(name: string) {
        if (name.length > 10) {
            throw new Error("name too long")
        }
    this._name = name;
    }
}

let person = new Person();

console.log(person.name);    // undefined
person.name = "june";

console.log(person.name);    // june
person.name = "junejunejunejunejune";    // throw Error

-  class의 속성에 직접 접근하는 것을 막고 getter, setter 함수를 사용해 값을 받아오거나 수정

→  속성에 직접 접근해 수정하면 데이터 무결성이 깨질 수 있음 (캡슐화 권장)

 

-  각 객체의 멤버에 접근하는 방법을 세밀하게 제어 가능

 

 

 

 

 

 

▶  readonly

읽기만 가능한 속성을 선언하기 위해 사용한다.

class Person
    readonly age: number = 20    // 선언 초기화
    constructor(age: number) {
        this.age = age;
    }
}

let person = new Person(10);    // 생성자 초기화
person.age = 30;    // Error: Cannot assign to 'age' because it is a read-only property

-  속성을 읽기 전용으로 설정해 변경할 수 없게 만듬

-  선언될 때나 생성자에서 값을 설정하면 이후 수정 불가

 

 

 

 

 

 

▶  static

전역 멤버를 선언할 때 사용한다.

 

💡  전역멤버 : 객체마다 할당되지 않고 클래스의 모든 객체가 공유하는 멤버

class Grid {
    static origin = { x: 0, y: 0 }
    calculateDistanceFromOrigin(point: { x: number; y: number }) {
        let xDist = point.x - Grid.origin.x;
        let yDist = point.y - Grid.origin.y;
        return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
    }
    constructor(public scale: number) {}
}

let grid1 = new Grid(1.0);    // 1x scale
let grid2 = new Grid(5.0);    // 5x scale

console.log(grid1.calculateDistanceFromOrigin({ x: 10, y: 10 }));
console.log(grid2.calculateDistanceFromOrigin({ x: 10, y: 10 }));

-  각 인스턴스가 아닌 클래스 자체에서 보이는 전역 멤버를 생성 (범용적으로 사용되는 값에 설정)

-  "클래스명."을 앞에 붙여 static 멤버에 접근할 수 있음

-  ES6에서는 메소드 전용 속성에는 선언이 안되었으나, TypeScript에서는 사용 가능

 

 

 

 

 

▶  추상 클래스

다른 클래스들이 파생될 수 있는 기초 클래스이다.

abstract class Animal {
    protected name: string
    constructor(name: string) {
        this.name = name;
    }
    
    abstract makeSound(): void
    
    move(): void {
        console.log("move !!");
    }
}

class Dog extends Animal {
    constructor(name: string) {
        super(name); // 파생된 클래스의 생성자는 반드시 super()를 호출
    }
    makeSound(): void { // 반드시 파생된 클래스에서 구현 필수
        console.log(this.name + " 멍멍!!");
    }
}

const animal = new Animal("animal");    // Error: Cannot create an instance of an abstract class
const dog = new Dog("진돗개");

dog.makeSound(); // 진돗개 멍멍!!

-  직접 인스턴스화 할 수 없다.

-  추상 클래스나 추상 메소드를 정의하는데 abstract 키워드를 사용한다.

-  추상 메소드는 클래스에는 구현되어 있지 않고, 파생된 클래스에서 구현해야 한다.

 

 

 

 

추상 클래스를 활용한 디자인 패턴 (Template Method Pattern)

 

프로그램의 일부분을 서브 클래스로 캡슐화해 전체 구조를 바꾸지 않고

특정 단계의 기능을 바꾸는 것을 디자인 패턴이라고 한다.

 

 

-  전체적인 알고리즘은 상위 클래스에서 구현하고 다른 부분은 하위 클래스에서 구현한다.

-  전체 구조는 유사하지만 부분적으로 다른 구문으로 구성된 메소드의 코드 중복을 최소화 할 수 있다.

 

abstract class Parent {
    //템플릿 메소드: 자식에서 공통적으로 사용하는 부분(someMethod)
    public do() {
        console.log("Parent에서 실행 - 상");
        this.hook(); // 훅 메소드: Child에서 구현해야 할 부분
        console.log("Parent에서 실행 - 하");
    }
    abstract hook(): void
}

class Child extends Parent {
    hook(): void {
        console.log("Child");
    }
}

const child = new Child();

child.do();

// 실행 결과
// Parent에서 실행 - 상
// Child
// Parent에서 실행 - 하

 

 

 

 

 


 이 글은 엘리스의 AI트랙 5기 강의를 들으며 정리한 내용입니다.

반응형

'개발공부 > 🟦 TypeScript' 카테고리의 다른 글

[TS] Generic  (0) 2022.11.08
[TS] Interface  (0) 2022.11.07
[TS] TypeScript를 이용해 함수 사용하기  (0) 2022.10.09
[TS] 타입스크립트의 기본 Type  (0) 2022.10.09
[TS] 타입스크립트를 사용하는 이유  (0) 2022.10.09
프로필사진
개발자 삐롱히

프론트엔드 개발자 삐롱히의 개발 & 공부 기록 블로그