JavaScript 공부 6주차

학습할 것


  • 객체지향 프로그래밍이란 무엇인가.
  • 객체지향 프로그래밍에 대한 다양한 논쟁.
    • 예 1) 객체지향 프로그래밍은 모든 프로그램을 제작하는데 적합한가?
    • 예 2) 객체지향 프로그래밍이 가져올 수 있는 문제점.
    • 예 3) 그럼에도 객체지향 프로그래밍 패러다임이 가장 널리 쓰이는 이유.
    • 예 4) 다른 프로그래밍 패러다임 (2개 이상)
  • 객체지향 프로그래밍의 4대 특성
    • 상속
    • 캡슐화
    • 추상화
    • 다형성
  • super 키워드
  • 메소드 오버라이딩
    • 오버라이드와 오버라이딩의 차이점
  • 다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
    • 어떻게 디스패치가 일어나는지?
  • 추상 클래스
    • 인터페이스와 무엇이 다른가?
  • is-a 관계, has-a 관계

객체지향 프로그래밍이란 무엇인가

객체지향 프로그래밍은 컴퓨터 프로그래밍 패러다임 중 하나로, 로직을 상태와 행위로 이루어진 객체로 만들고, 그 객체들의 집합으로 프로그램을 표현하려는 것이다.

const circle = {
radius = 5,   <- 상태 데이터
getDiameter(){
return 2 * this.radius <- 데이터를 조작하는 행위
}
}

원을 예로 들면, 원이라는 객체는 반지름이라는 상태를 가지며 반지름 데이터를 조작하여 지름을 구하는 행위를 할 수 있다.
이처럼 객체지향 프로그래밍은 객체를 상태를 나타내는 데이터와 그 데이터를 조작하는 행위를 하나의 단위로 묶어 생각하는데, 이때 객체의 상태 데이터를 프로퍼티, 동작을 메소드라고 한다.

객체지향 프로그래밍에 대한 다양한 논쟁

객체지향 프로그래밍은 모든 프로그램을 제작하는데 적합한가?

객체 지향 프로그램은 프로그램에서 사용되는 다양한 객체에 대한 정보를 저장해야 하므로 한정적인 자원만을 사용해야 하는 환경(ex.임베디드 시스템)에선 사용시 제약이 있다.

객체지향 프로그래밍이 가져올 수 있는 문제점.

프로그램을 만들기 위해서 해당 프로그램에 사용하려는 객체들에 대한 명확한 이해가 필요하므로 설계가 복잡하다.

그럼에도 객체지향 프로그래밍 패러다임이 가장 널리 쓰이는 이유.

모듈식으로 형성되어 있어 다른 프로그램에서 재사용 및 재활용하기 쉬우며, 이 때문에 생산성이 올라간다.

객체지향 프로그래밍의 4대 특성

상속

어떤 객체의 프로퍼티 또는 메소드를 다른 객체가 상속받아 그대로 사용할 수 있는 것을 말한다. 상속을 하는 클래스는 부모 클래스(또는 수퍼 클래스, 베이스 클래스), 상속을 받는 객체는 자식 클래스(또는 파생 클래스, 서브 클래스)라고 한다.
자식 클래스는 부모 클래스의 프로퍼티와 메소드를 사용할 수 있고, 필요한 기능을 추가해 확장하여 사용할 수 있다.

class Base {
  constructor(name) {
    this.name = name;
  }
  sayhi(){
    return this.name;
  }
}
class Derived extends Base{} //<- Base 상속

const derived = new Derived('Lee');
console.log(derived.sayhi());

캡슐화

캡슐화는 객체의 프로퍼티와 메소드를 하나로 묶는 것을 말한다. 특히 객체의 특정 프로퍼티나 메소드를 감출 목적으로 사용하기도 하는데, 이를 정보 은닉이라 한다. 이로써 외부에 공개할 필요가 없는 구현의 일부를 공개되지 않도록 감추어 적절치 못한 접근으로 객체의 상태가 변경되는 것을 방지하고, 객체 간의 상호 의존성을 낮출 수 있다.
대부분의 객체지향 프로그래밍 언어는 public, private, protected와 같은 접근 제한자를 사용하여 공개 범위를 한정할 수 있다(public으로 선언된 프로퍼티 및 메소드는 외부에서 참조가 가능하지만, private로 선언된 경우는 외부에서 참조할 수 없다).
JavaScript는 접근 제한자를 지원하지 않는다. 즉 JavaScript 객체의 모든 프로퍼티 및 메소드는 기본적으로 외부에 공개되어 있다. 단 아래와 같이 private와 비슷하게 사용할 수 있다.

function Person(name,age) {
  this.name = name; //<-public
  let _age = age //<-private

  this.sayhi = function{
    console.log('my name is $(this.name) and my age is $(_age)');
  };
}

const me = new Person('Lee', 20);
me.sayhi(); //<-'my name is Lee and my age is 20'
console.log(me.name); //<- Lee
console.log(me._age); //<-undefined

추상화

객체는 다양한 속성을 지니고 있는데, 이 중에서 프로그램에 필요한 몇 가지 속성만 간추려 내어 사용하는 것을 말한다.

다형성

특정 기능을 선언과 구현 부분으로 분리한 후 구현 부분을 다양한 방법으로 만들고 선택하여 사용할 수 있게 하는 기능이다.

super 키워드

super 호출

super를 호출하면 수퍼클래스의 constructor를 호출한다.

class Base(){
  constructors(a, b) {
    this.a = a;
    this.b = b
  }
}

class Dervied extends Base(){
  // constructors(...args) {super(...args)}  <-암묵적으로 생성
}

const derived = new Dervied(1, 2);
console.log(derived) // derived{a:1, b:2}

위와 같이 수퍼클래스의 constructor 내부에서 추가한 프로퍼티를 가지는 인스턴스는 서브클래스의 constructor을 생략할 수 있다.

class Base{
  constructors(a, b) {
    this.a = a;
    this.b = b
  }
}

class Dervied extends Base{
  constructor(a, b, c){
    super(a, b);
    this.c = c;
  }
}

const derived = new Dervied(1, 2, 3);
console.log(derived) // derived{a:1, b:2, c:3}

이때 서브클래스를 호출하면서 생성한 인수 중에 수퍼클래스의 constructor에 전달할 필요가 있는 인수는 위와 같이 서브클래스의 constructor에서 호출한 super 키워드를 통해 호출한다.

super 참조

super를 참조하면 수퍼클래스의 메소드를 호출할 수 있다.

class Base {
  constructor(name) {
    this.name = name;
  }
  sayhi(){
    return 'hi! ${this.name}';
  }
}

class Derived extends Base{
  sayhi(){
    return '${super.sayhi(). how are you doing?';
  }
}
const derived = new Derived('Lee');
console.log(derived.sayhi()); //<-Lee, how are you doing?

메소드 오버라이딩

메소드 오버라이드는 자식 클래스에서 부모 클래스의 기능(메소드)를 재정의할 때 사용하는 기능이다.

class Animal{
  constructor(name){
    this.name = name;
  }
  stop() {
    this.speed = 0;
  }
}
class Rabbit extends Animal{
  stop(){
    // rabbit.stop()을 호출할 때
    //Animal의 stop()이 아닌 이 메소드가 사용
  }
}

만약 자체적으로 메소드를 만든 후에도 이 과정 전후에 부모 메소드를 호출하고 싶다면 super 키워드를 사용한다.

class Animal{
  constructor(name){
    this.name = name;
  }
  stop() {
    this.speed = 0;
  }
}

class Rabbit extends Animal{
  hide(){
    console.log('${this.name}이 숨었습니다.');
  }
  stop(){
    super.stop(); //부모 클래스의 stop()을 호출
    this.hide() //숨는다
  }
}

오버로딩와 오버라이딩의 차이점

오버로딩은 함수의 이름은 동일하지만, 매개변수의 타입 또는 개수가 다른 메소드를 구현하고 매개변수에 의해 메소드를 구별하여 호출하는 방식이다.
자바스크립트는 오버로딩을 지원하지 않지만, argument 객체를 이용하여 구현이 가능하다.

다이나믹 메소드 디스패치 (Dynamic Method Dispatch)

어떻게 디스패치가 일어나는지?

추상 클래스

예를 들어 Animal이라는 클래스가 존재하지만, 단순히 동물을 만든다는 것은 조금 이상한 것처럼 추상적인 개념을 가지고 있는 객체를 생성하는 일이 있는 것은 좋지 않다.
JavaScript는 추상 클래스를 지원하지 않는다. 다만 아래와 같이 직접 구현하여 상속시키는 방법을 사용할 수 있다.

class Abstract {
  constructor() {
    if (new.target === Abstract) {
      throw new TypeError("Cannot construct Abstract instances directly");
    }
  }
}

class Derived extends Abstract {
  constructor() {
    super();
    // more Derived-specific stuff here, maybe
  }
}

const a = new Abstract(); // new.target is Abstract, so it throws
const b = new Derived(); // new.target is Derived, so no error

인터페이스와 무엇이 다른가?

인터페이스는 추상 메소드들의 집합으로, 클래스와는 다르며 인스턴스를 가질 수 없다. JavaScript는 인터페이스를 지원하지 않는다.

is-a 관계, has-a 관계

is-a 관계는 notebook is a computer, laptop is a computer 처럼 computer이라는 공통 속성을 상속받는 관계를 말한다.
has-a 관계는 polise has a gun, soldier has a gun처럼 하위 클래스가 상위 클래스를 소유하는 관계를 말한다. 반드시 상속을 받는 것이 아니라 소유하지 않는 경우에는 상속을 하지 않는 것이 좋다.

출처

좋은 웹페이지 즐겨찾기