객체 지향 프로그래밍

code-kay·2020년 12월 9일
2

Object Oriented Programming, OOP란?

객체 지향 프로그래밍! 코딩 공부를 하면서 여러번 본 단어다. 이게 무슨 의미인 걸까? 일단은 사전적 정의를 찾아보았다.

  • MDN : 데이터가 객체 내에 캡슐화되고 구성 요소 부분 이 아닌 객체 자체가 운용되는 프로그래밍 방식. JavaScript는 매우 객체 지향적인 언어로, 클래스 기반의 언어들과는 대조적으로 프로토타입 기반의 모델을 따른다.

  • Wikipedia : 컴퓨터 프로그래밍의 패러다임 중 하나로, 컴퓨터 프로그램을 명령어의 목록 으로 보는 시각에서 벗어나 여러 개의 독립된 단위, 즉 객체들의 모임으로 파악하고자 하는 것이며, 각각의 객체는 메시지를 주고받고 데이터를 처리할 수 있다.

    ......

    그 외에도 여러 정의가 있었지만, 그래. 이름 그대로 객체가 중요하다는 거군. 그래서 정확히 뭐라는거야? 라는 생각이 들던 중, 확 와닫는 정의가 있었다.

  • 객체지향 프로그래밍이란 실세계의 사물을 추상화(Abstraction), 캡슐화(Encapsulation), 상속(Inheritance), 다형성(Polymorphism) 하여, 코드 재사용을 증가 시키고 유지보수를 감소시키는 장점을 얻기 위해서 객체들을 연결 시켜 프로그래밍 하는 것 입니다. (출처 : https://vandbt.tistory.com/10)

아하! 객체들을 연결 시킴으로써 효율성을 극대화 시킨 프로그래밍 방법이구나! 그렇다면 저기서 말하는 추상화, 캡슐화, 상속, 다형성은 뭘까?

1. 추상화 (Abstraction)

  • 불필요한 정보는 숨기고, 중요한 정보만 표현하여 프로그램을 간단하게 만드는 것. 안에 있는 복잡한 코드들은 신경 쓸 필요 없이 꼭 필요한 정보만 표시하여 사용하기 용이하게 한다.
  • 전화기를 사용하는 사람이 전화기 안에 어떤 부품들이 있는지, 전화기가 어떤 원리로 작동되는지 1도 모른다고 해도, 번호를 누르고 통화하는 방법만 알면 되는 것과 같다.

2. 캡슐화 (Capsulation)

  • 변수와 함수를 하나의 단위로 묶는 것. 대개의 언어에서 '클래스'를 통해서 구현되며, 클래스의 인스턴스 생성을 통해 클래스 안의 변수와 메소드에 접근할 수 있다.
  • 내부의 데이터를 감추고 외부와는 메소드로 상호 작용 할 수 있다. 자료구조 스프린트에서 Hash Table을 구현할 때 Limited Array의 storage 배열을 직접적으로 볼 순 없지만, .set, .get, .each 메소드를 통해 접근할 수 있었던 것이 그 예시.

3. 상속 (Inheritance)

  • 새로운 클래스(하위, 자식 클래스)가 기존 클래스(상위, 부모 클래스)의 자료와 연산을 이용할 수 있게 하는 것. 특정 부분만 수정하여 업그레이드 하는 식의 활용도 가능. 2개 이상의 클래스로부터 상속받는 다중 상속도 가능하다.
  • HTML & CSS에서 자식 엘리먼트들이 부모 엘리먼트에 적용된 속성을 그대로 물려받는 것과도 상속으로 볼 수 있다.

4. 다형성 (Polymorphism)

  • Poly는 여럿(多), morph-는 형태라는 뜻. 즉 여러 형태를 가진다는 뜻인데, 정확히 말하면 하나의 변수, 또는 함수가 상황에 따라서 다른 형태를 가질 수 있다. 다른 의미로 해석될 수 있다는 뜻이다.
  • 포켓몬스터의 메타몽은 변신을 뜻하는 'metamorphosis'에서 이름을 따왔는데, 이름에서 알 수 있듯 변신하는 기술을 가진 포켓몬이다. 이러한 메타몽의 특성은 어떻게 보면 OOP의 Polymorphism와 유사하다고 볼 수 있다.
    메타몽에게는 '변신'이라는 하나의 기술 밖에 없지만, 이 기술을 어느 대상에 사용하느냐에 따라서 기술을 사용한 결과가 달라진다. '잠만보로 변신', '꼬부기로 변신', '피카츄로 변신' 과 같이 일일이 개별 기술(함수)를 만들 필요 없이 '변신'이라는 하나의 기술(함수) 만으로도 상황에 따라 다른 형태를 가질 수 있는 것이다!

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

객체 지향 프로그래밍의 이러한 특성 덕분에,

  • 복잡도가 줄어들고,
  • 코드의 재사용성을 증가시키고,
  • 같은 코드를 반복해서 적을 필요가 없어지기 때문에,
  • 깔끔하고 예쁜 코드를 작성할 수 있다!!

반면,

  • 절차 지향 프로그램과 비교하면 처리 속도가 다소 느리고,
  • 설계하는데 시간이 많이 든다는 등의 단점도 있다.

JavaScript에서 객체를 생성하는 방법

객체 지향 프로그래밍을 하려면 객체를 만들어야 할 것이다.
그렇다면, 객체를 생성하는 방법에는 어떤 것들이 있을까?

1. 객체 리터럴

let obj = { name : 'Kay', id : 'code-kay' };
  • 객체 이니셜라이저(initializer)라고도 한다.

2. Object() 생성자 함수

let obj = new Object();
obj.name = 'Kay';
obj.id = 'code-kay';
  • 기본 객체인 Object에 new를 이용해 인스턴스를 생성한다.

3. Object.create()

let Obj = { name : 'Kay', id : 'code-kay' };
let obj1 = Object.create(Obj);
let obj2 = Object.create(Obj);
  • 사용할 프로토타입 객체를 직접 선택한다.
  • 객체를 프로토타입으로 설정하여 여러 객체를 생성한다.
  • 그냥 카피하는거라고 생각하면 편하다.

4. 생성자 함수

function Person(name, id){
  this.name = name;
  this.id = id;
}
let obj = new Person('Kay', 'code-kay');
  • 생성자 함수란 객체를 만들기 위한 타입을 정의해 놓는 함수이다.
  • 생성자 함수 이름의 첫 문자는 대문자로 쓴다.
  • 'new' 키워드로 객체 인스턴스를 생성한다.
  • 프로퍼티의 종류는 같지만 값은 다르게 객체를 만들 수 있다.
  • 자신을 생성한 생성자 함수의 prototype 프로퍼티가 가리키는 객체를 자신의 prototype 객체로 설정한다.

Instantiation. 인스턴스 생성하기

1. Functional

var Burger = function(patty) {
  var someInstance = {};
  someInstance.patty = 1;
  someInstance.extraPatty = function() {
    this.patty += 1;
  }
  return someInstance;
};

var burger1 = Burger(1);
var burger2 = Burger(2);
burger1.extraPatty();
console.log(burger1.patty) // 2
console.log(burger2.patty) // 2

2. Functional Shared

var extend = funciton(to, from) {
  for (var key in from) {
    to[key] = from[key];
  }
};

var someMethods = {};
someMethods.extraPatty = function() {
  this.patty += 1;
};

var Burger = function(patty) {
  var someInstance = { patty: patty };
  extend(someInstance, someMethod);
  return someInstance;
};

var burger1 = Burger(1)
  • 메소드와 생성자 함수를 따로 적어주고 extend 함수를 이용해 연결시켜주었다.
  • 이렇게 하면 생성된 객체마다 메소드를 가지고 있을 필요 없이 someMethods 에서 참조만 해오면 되기 때문에 메모리 사용량이 줄어든다는 이점이 있다.

3. Prototypal

var someMethods = {};
someMethods.extraPatty = function() {
  this.patty += 1;
};

var Burger = function(patty) {
  var someInstance = Object.creat(someMethods);
  //someMethods를 prototype으로 하는 객체를 생성
  someInstance.patty = patty;
  return someInstance;
};

var burger1 = Burger(1)

4. Pseudoclassical

var Burger = function(patty) {
  this.patty = patty;
};

Burger.prototype.extraPatty = function() {
  this.patty += 1;
};

var burger1 = new Burger(1);
var burger2 = new Burger(2);
  • 가장 간편하고 많이 쓰는 방법! 새로 생성할 때 new Operator를 붙여줘야 한다.

프로토타입(Prototype)

포스팅을 하다보니 '프로토타입'이라는 용어가 계속해서 등장한다. Javascript는 프로토타입 기반의 언어라던가, 객체의 프로토타입을 설정한다던가... 이게 무슨 뜻일까?

JS는 프로토타입 기반의 객체지향 언어이다.

Java, Python 등등 객체지향언어에서 클래스(Class)는 빠질 수 없는 개념이라고 한다. 그런데 JavaScript는 객체지향언어 인데 클래스라는 개념이 없다...! 대신 프로토타입이라는 것이 있는데, 이를 활용해서 클래스를 흉내낼 수 있다고 한다.

그래서 프로토타입이 뭔데?

  • 일단 기억할 것은, 자바스크립트에서는 함수도 '객체'의 형태로, 모든 함수는 prototype이라는 속성을 갖고, 모든 객체는 __proto__ 라는 속성을 갖는다.
  • prototype은 모델의 청사진을 만들 때 쓰는 원형 객체,
  • constructor은 인스턴스가 초기화될 때 실행하는 생성자 함수이다.
  • 생성자 함수, 프로토타입 객체, 생성된 객체는 다음 그림과 같은 관계로 연결되어 있다.
  • 생성된 객체의 .__proto__ 는 prototype 객체를 가리키며, 이를 통해 생성자 함수의 속성을 상속받을 수 있게 된다.
  • 만약 prototype 객체 안에 찾는 값이 없으면 prototype '객체'의 .__proto__ 를 타고 올라가는 식으로 상속 받은 값을 찾는다.
  • 쭉 타고 올라가다 만나는 가장 상위 객체는 Object 이며, 모든 객체는 Object를 상속 받는다.
  • 이런 식으로 'Prototype Chain' 을 형성한다.

프로토타입으로 상속 구현하기

CheeseBurger라는 생성자 함수로 만든 객체에
Burger이라는 생성자 함수의 속성도 상속시켜주고 싶을 때,

CheeseBurger.prototype = Object.create(Burger.prototype);
  • Burger의 프로토타입을 복제해서 CheeseBurger의 포로토타입에 넣어주고,
CheeseBurger.prototype.constructor = CheeseBurger;
  • 위 과정에서 constuctor가 Burger으로 바뀌었으므로 원상복귀
var CheeseBurger = function(name) {
  Burger.call(this, name); // Burger.apply(this, arguments)
}
  • new CheeseBurger를 했을 때 instance가 Burger까지 올라가지 않으므로 CheeseBurger의 Constructor 안에서 this를 올려주는 작업을 해야함!

클래스 문법 사용법

지금까진 프로토타입으로 클래스와 상속을 흉내내느라 눈물의 응꼬쑈 를 했는데, 최신 문법인 ES6에서는 클래스를 사용할 수 있게 되었다. 정확히 말하면, 작동되는 원리는 똑같지만 클래스 문법을 통해 쉽게 사용할 수 있게 된 것이다.

class Burger {
  constructor(patty) {
    this.patty = patty;
  }
  extraPatty(){}
}

class CheeseBurger extends Burger { //extends로 연결
  constructor(patty) {  //constructor가 똑같아서 생략 가능
    super(patty);  //this 전달은 super로 대체
  }
  extraCheese(){}
}
  • extends 로 프로토타입끼리 연결
  • super로 this를 올려주는 작업 대체
  • constructor가 Burger과 같으므로 생략 가능

0개의 댓글