[JS] OOP - 객체 지향 프로그래밍

심언조·2021년 2월 26일
0

JavaScript

목록 보기
1/2
post-thumbnail

📔 OOP?

Object Oriented Programming - 객체 지향 프로그래밍
컴퓨터 프로그래밍의 패러다임중 하나로 프로그램을 객체(object) 단위로 나누고 이를 상호작용하도록 작성하는 방법이다. 여기서 객체는 어떤 역할을 수행하는 함수와 변수의 묶음의 개념으로 본다.

객체 지향 프로그래밍이라는 패러다임이 등장하기전에는 절차적 프로그래밍 방식을 사용했다. 입력을 받아 명시된 순서대로 처리한 다음 결과는 내는 것. 즉, 프로그램 자체가 가지는 기능을 구현하는데 생각이 그쳤고 데이터를 취급하는 방법에는 관심이 적었다.

그러나 시간이 지남에 따라 기능이 많은 코드를 작성하게 될 때 매우 복잡한 코드를 만들기 일쑤 였고 이를 극복하기 위해 등장한 것이 객체 지향 프로그래밍이다. 객체 지향 프로그램의 원리는 작은 문제를 해결할 수 있는 객체들을 만들고 이 객체들을 조합하여 큰문제를 해결하는법이다.

잘 만들어둔 객체는 수정할 필요없이 재사용할 수 있음으로 개발기간과 비용이 대폭 줄어들게 된다.

📚 객체지향 프로그래밍의 요소

객체지향 프로그래밍은 캡슐화(Encapsulation), 상속(Inheritance), 추상화(Abstraction), 다형성(Polymorphism) 의 요소를 가지고 있고 각각의 의미는 다음과 같다.

캡슐화(Encapsulation)

외부에 따로 속성이나 기능을 하나로 묶어 객체로 정의하고 객체 안에서 정의된 속성은 해당 객체에서만 접근이 가능하다. 이는 클래스(class)라는 개념으로 구현된다. 클래스는 객체지향을 지원하는 언어가 제공한다.

정보의 은닉(Infomation Hiding)

캡슐화의 개념에는 "은닉화"의 특징도 포함하고 있으며 이는 프로그램의 세부적인 구현을 외부로 드러나지 않도록 감추는 것이다. 외부로의 노출을 최소화 하여 모듈간의 결합도를 떨어뜨려(Loose Coupling)유지보수가 용이한 코드를 만들 수 있다.

상속(InHeritance)

상속은 부모객체의 특징을 물려받는 것이다. 다시 말하자면 부모가 가진 속성이나 메서드를 그 자식이 부모의 속성이나 메서드를 상속받아 가질 수 있다. 상속을 사용하여 프로그램의 복잡도를 줄여주고 불필요한 코드를 제거할 수 있다. 또한 상속받은 자식의 기능의 일부분을 변경해야 할 경우 해당하는 기능을 다시 정의하게 되는데 이런 작업을 오버라이딩(Overriding)이라고 한다.

추상화(Abstraction)

추상화는 복잡한것들을 어떤 객체의 이름으로 추상화 시켜서 내부 메서드의 구현이 어떤지 몰라도 사용가자 사용하기 쉽게 만들는 것이다.

예를 들어 자동차의 엔진이 어떻게 돌아가고 연료가 어떻게 사용되는지 이해하지 않아도 우리는 "핸들"과 "브레이크", "엑셀 페달" 같이 추상화 되어있는 도구(객체)를 사용하기만 하면 되는 것이다.

다형성(Polymorphism)

객체의 변수나 메서드가 상황에 따라 다른 의미로 해석될 수 있는 것을 말한다. 상속에서 언급했던 오버라이딩(Overriding)기법을 사용하여 자식 클래스의 메서드가 부모 클래스의 메서드와 다르게 동작하거나 변수가 다른 값으로 지정될 수 있다.

포유류의 범주(부모 클래스)에 있는 강아지(자식 클래스)나 고양이(자식 클래스)가 소리를 낼 때(같은 메서드) '멍멍', '야옹' 하는 것과 같이 다르게 소리를 내는 것과 같다.

📒 Javascript에서 OOP

Javascript는 흔히 프로토타입 기반 언어(prototype-based language)라 불린다. 모든 객체들이 메서드와 속성을 상속받기위해 프로토타입 객체(protype object)를 가진다는 의미이다. 프로토타입 체인(prototype chain)이라 부르며 다른 객체에 정의된 메서드와 속성을 한 객체에서 사용할 수 있는 원리이다.

프로토타입(Prototype)

JS에는 객체의 인스턴스와 프로토타입간에 연결이 구성되며 이 연결을 따라 프로토타입 체인을 타고 올라가며 속성과 메서드를 탐색한다.
보통 객체(Ordinary Object)에는 [[prototype]]으로 표현되는 프로토타입 객체에 대한 링크는 내부 속성으로 정의되어 있다. 그러나 많은 브라우저들이 __proto__ 속성을 통해 특정 객체의 프로토 타입 객체에 접근할 수 있도록 구현되어 있다.

var myArray = new Array(); // === [] <- 객체초기자 또는 리터럴이라고 한다
var myObj = new Object(); // === {}
 
console.log(typeof myArray); // object
console.log(myArray.__proto__.__proto__ === myObj.__proto__); // ture

위 결과에서 배열또한 객체이며 배열의 prototype의 원형은 Object로 부터 파생된 것이다. 한편 constructor 는 원본 생성자 함수 자신(Array())을 가르킨다.

또한 함수(function)객체에는 prototype 속성이 있으며 이 또한 하나의 객체이며 프로토타입 체인을 통해 상속하고자 하는 속성과 메서드를 담아두는 역할로 사용되는 객체이다.

var Human = function(name, age){
  this.name = name;
  this.age = age;
}

// Human 함수의 prototype 에 sleep 이라는 함수를 정의
Human.prototype.sleep = function(){
  console.log('zzz...');
}

var sim = new Human('sim', 25);
var jimmy = new Human('jimmy', 28);

// sim 과 jimmy 의 생성자(constructor)는 Human 함수이다
console.log(sim.constructor.name); //  Human
console.log(jimmy.constructor.name); // Human

// Human 객체에 선언한 sleep 함수 호출
sim.sleep(); // zzz...

// Human 에 toString 메서드를 만들지 않았지만
// 프로토타입 체인을통해 Human의 원형 Object 의 prototype 에 정의된
// toString 메서드를 사용할 수 있다.
sim.toString(); // "[object Object]"

ES6전 객체 지향 패턴

Class 키워드가 없던 ES6 전 JS는 인스턴스(instance)를 만드는 작업은 다음과 같이 할 수 있다.

Functional Instantiation

함수를 사용하여 클래스를 만들고 인스턴스를 생성하는 방법.

var Car = function(name, brand){
  var instance = {};
  instance.name = name;
  instance.brand = brand;
  instance.handle = 1;
  instance.wheel = 4;
  instance.state = 'parking';
  
  instance.acceleration = function(){
   this.state = 'accelerating';
  }
  
  instance.parking = function(){
    this.state = 'parking';
  }
  
  return instance;
}

또는 클로저 모듈 패턴을 사용하여 다음과 같이 사용할 수 있다.

function Car(name, brand){
  let carName = name;
  let carBrand = brand;
  let state = 'parking';
  return {
  	getName:function(){
    	return carName;
    },
    getBrand:function(){
    	return carBrand;
    },
    getState:function(){
  		return state;
    },
    acceleration:function(){
        state = 'accelerating';
    },
    parking:function(){
        state = 'parking';
    }
  }
}

Pseudoclassical

JS의 프로토타입을 이용하여 만드는 방법이다. 이 방법을 사용하여 객체지향의 상속을 구현 할 수 있다.

function Car(name, brand){
  this.name = name;
  this.brand = brand;
  this.handle = 1;
  this.wheel = 4;
  this.state = 'parking';
}

Car.prototype.acceleration = function(){
	this.state = 'accelerating';
}

Car.prototype.parking = function(){
	this.state = 'parking';
}

function Truck(name, brand){
    // apply 메서드를 사용하여 Car 클래스에 있는 모든 요소를 상속받는다.
    // 다만 apply 를 사용하여 모든 요소를 가져왔을 뿐 prototype은 변경되지 않은 상태
    Car.apply(this, arguments);
	this.name = name;
    this.brand = brand;
  	// 이전에 없던 새 요소 추가
    this.size = 'big';
    this.storage = [];
}

// Truck의 prototype을 Object.create() 메서드를 사용하여 
// Car의 prototype을 상속, 그러나 constructor는 아직 Car인 상태
Truck.prototype = Object.create(Car.prototype);

// Truck 의 생성자(constructor)가 Truck임을 명시해 준다.
Truck.prototype.constructor = Truck;

// Truck 의 메서드 추가
Truck.prototype.loadBoxes = function(box){
	this.storage.push(box);
}

// Car 의 acceleration 메서드를 오버라이딩(overriding)
Truck.prototype.acceleration = function(){
	this.state = 'accelerating...!!!';
}

그리고 위 코드 작성 후 실행시킨 결과는 다음과 같다

ES6후 class 키워드

ES6에서는 class키워드를 사용하여 위 코드보다 간단하게 작성할 수 있다.

class Car {
  constructor(name, brand){
    this.name = name;
    this.brand = brand;
    this.handle = 1;
    this.wheel = 4;
  	this.state = 'parking'
  }
  
  acceleration(){
    this.state = 'accelerating';
  }
  
  parking(){
    this.state = 'parking';
  }
}

class Truck extends Car {
  constructor(name, brand){
    // super 메서드를 통해 Car의
    super();
    this.name = name;
    this.brand = brand;
  }
  
  // Truck 의 메서드 추가
  loadBoxes(box){
	this.storage.push(box);
  }

  // Car 의 acceleration 메서드를 오버라이딩(overriding)
  acceleration(){
    // 만약 Car의 acceleration 를 실행시킨 후 다른 동작을 하려면 
    // super.acceleration(); 을 사용한다
    this.state = 'accelerating...!!!';
  }
  
}

참고 자료

Wiki - 객체 지향 프로그래밍

Wiki - SOLID 객체 지향 프로그래밍 설계 원칙

Wiki - 메소드 오버라이딩

MDN - Object prototypes

MDN - 자바스크립트의 상속

[Javascript] 프로토타입 이해하기

profile
웹 개발자

0개의 댓글