728x90

이전 글

[1. 객체지향 프로그램의 의미를 알아보자]

https://honeystorage.tistory.com/287

 

아래 내용 관련 Github :)

https://github.com/jaekwangLee/oop_js_study

 

우리는 이전 글에서

객체지향의 탄생에는

절차지향 프로그래밍의 한계가 있음을

알게되었다.

 

이제는 구체적으로

객체지향의 특징들을 알아보자.

 

흔히 4가지 특징이 있다고 한다.

1. 캡슐화 : 내부의 특성은 안으로 숨기고 사용자에게는 사용법만을 노출시킨다.

2. 상속 : 부모의 특성을 자식이 물려받음

3. 다형성 : 하나의 클래스나 메소드가 다양한 방식으로

4. 추상화 : 중요한 정보만을 표현하여 공통의 속성이나 이름을 묶어 이름 붙임

 

한글로만 봐도 어려운 단어들이지만

하나씩 이해해보자

실제로 써보면

절차지향 프로그래밍에서는 못느꼈던

새로운 희열을 느낄수있다.

 

다시, 이전의 예시코드를 살펴보자.

이 짧은 예시코드안에

객체지향 특성을 모두 볼 수 있다.

{
  interface NormalCar {
    start(): void;
    stop(): void;
    run(): void;
    spec: Specifications;
    ownerName: string;
  }

  interface SuperCar {
    start(): void;
    stop(): void;
    run(): void;
    boost(): void;
    spec: Specifications;
    ownerName: string;
  }

  type Specifications = {
    tires: number;
    fuel: number;
    owner: string;
  };

  type OnOff = "off" | "on";

  class Vehicle {
    private engine: OnOff = "off";
    constructor(tires: number, fuel: number) {
      console.log(tires, fuel);
    }

    start = (): void => {
      console.log("start");
      this.engineToggle("on");
    };

    stop = (): void => {
      console.log("stop");
      this.engineToggle("off");
    };

    run = (): void => {
      console.log("keep going...");
    };

    boost = (): void => {
      console.log("speed up!!!");
    };

    get status() {
      return "car status : " + this.engine;
    }

    private engineToggle = (nextStatus: OnOff): void => {
      this.engine = nextStatus;
    };
  }

  class Car extends Vehicle {
    constructor(private tires: number, private fuel: number, private owner: string) {
      super(tires, fuel);
    }
    
    get spec() {
      return {
        tires: this.tires,
        fuel: this.fuel,
        owner: this.owner,
      };
    }
    
    get ownerName() {
      return this.owner;
    }
    
    set ownerName(newName: string) {
      this.owner = newName;
    }
  }

  // car 인스턴스 생성
  const myCar = new Car(4, 50, "steve");
  myCar.start();
  console.log(myCar.status);
  
  myCar.stop();
  console.log(myCar.status);

  // car 인스턴스 생성할 때 interface를 이용해 추상화
  const myFamilyCar: NormalCar = new Car(4, 60, "steve_family");
  // myFamilyCar.boost() <-- error

  const myHobbyCar: SuperCar = new Car(4, 48, "steve_hobby");
  myHobbyCar.boost();
}

1. 캡슐화

캡슐화는 ownerName을 통해 알수있다.

사용자는 ownerName을 읽거나 쓸수 있지만

class내에서는 owner라는 이름으로 존재한다.

 

사용자는 직접 owner를 수정하거나

읽어들일 수 없다.

 

마찬가지로 engine의 상태 또한

직접 변경할수 없으며,

시동을 걸거나(start) 끔으로써(stop)

상태를 변경할 수 있다.

 

이렇게 변수를 은닉함으로써

불필요하게 class내 멤버변수에 접근하여

값을 변경하는 경우를 방지할 수 있다.

 

2.  상속

Vehicle -> Car

위와 같은 방식으로 상속이 진행되었다.

 

Vehicle이라는 큰 카테고리 내에서 

공통의 속성이나 이름을 묶어주었다.

(모든 탈것에는 공통적으로  tires, fuel, start, stop, run 등의 기능이 존재함)

 

Car에는 Vehicle이 갖는 특성 외에

항상 주인이 정해지게 된다.

 

따라서, vehicle을 상속함과 동시에

생성자함수(constructor)에서 owner를 지정하게 해주었다.

 

다음, 제품별로 고유의 특성들을 또 갖게될텐대

porche 같은 고성능 차에는 boost라는 기능이 추가된다거나

주유구 방향 등을 설정할수도 있겠다.

 

3. 추상화

추상화에는 두가지 방법이 있다.

접근제한자 (private, public, protected)를 통한 추상화와

interface를 통한 추상화가 있다.

 

추상화의 목적은 사용성의 향상에 있다.

예를들어, 추상화처리를 하지 않았다면

Car를 통해 생성한 인스턴스를 사용하고자 할때

start,stop,run,boost,engine,owner,fuel,tires ...

등등 불필요한 properties를 보게될것이다.

 

개발자가 제공하고자하는 항목만을

추상화를 통해 제공할 수 있다.

 

위에서도 private으로 정보 접근을 제한함과 동시에

불필요한 옵션을 줄였고,

interface를 통해 Normal Car에서는 boost라는

property를 제공하지 않게 설정하였다.

 

3. 다형성

부모의 특성을 재정의하는

"오버라이딩"을 통해 이루어지게되는데,

자식들이 부모의 특성을 그대로 물려받기도 하지만

종종 변화를 주는 경우가 생긴다.

 

만약, 절차지향 프로그래밍이었다면

부분적으로 다르게 동작하는 자동차를 만들려면

대부분의 코드를 중복해서 사용하고

해당 부분을 수정해서 새로운 함수를 만들어야 할것이다.

 

객체지향 프로그래밍에서는

오버라이딩을 통해 원하는 부분만 수정한

새로운 객체를 쉽게 생성해낼수있다.

 

이를 다형성이라고 한다.

728x90
반응형