관리 메뉴

Dev Blog

3. Inheritance Patterns - Object Oriented Programming 본문

BootCamp_Codestates/IM Tech Blog

3. Inheritance Patterns - Object Oriented Programming

Yongjae Kim 2020. 12. 9. 16:20

OOP(Object Oriented Programming)

How to make Object in Javascript


프로그래밍 = 소프트웨어의 추상화

현실의 복잡함 => 필요한 관점 추출하여 추상화

1. OOP?

  • 일종의 프로그래밍 패러다임.
  • 로직을 상태(state=>member variable)와 행위(behave)로 이루어진 객체로 만드는 것.
  • 연관된 메소드와 그 메소드가 사용하는 변수들을 분류하고 그룹핑 하는 것. 그룹핑한 것이 객체.

자동차의 공통적인 속성, 기능이 있을 것이다. ⇒ 그러면 자동차라는 class를 하나 만들어두면, 이를 베이스로 여러가지 자동차 object를 뽑아낼 수 있다.

 

class 는 prototype, 즉, 세부사항이 아직 들어가지 않은 청사진으로 볼 수 있고,

⇒ 여기세 우리가 원하는 세부사항을 classconstructor에 기입하면 ⇒ 각 object를 만들 수 있다.

2. 용어정리

1) class:

class라는 하나의 정형화된 모델을 만들어두고, 그 모델을 기반으로 한 instance(복제품)을 만들기 위해 사용한다.

마치 공장에서 같은 규격의 제품을 찍어내는 것과 비슷하다.
ex)  class: Iphone,

class Iphone 의 property: 액정, 스피커, 카메라, ... 등등,

class Iphone의 method: 알람이 울린다, 전화를 건다. 
2) object:

class로부터 만들어진 복제품을 선언한 것

ex) object1: Iphone iphone6,

object2:  Iphone iphoneSE,

object3: Iphone iphone11 ...
3)  instance:

class로 부터 만들어진 복제품인 object에 new와 같은 연산자를 통해 실체화 된 것
ex) instance1: Iphone iphone6 = new Iphone() ,

instance2: Iphone iphoneSE = new Iphone(),

instance3: Iphone iphone11  = new Iphone()...
4)  constructor:

객체를 만드는 역할을 하는 함수

 

3. OOP의 4가지 개념

1. Encapsulation

2. Inheritance

3. Abstraction

4. Polymorphism

 

 

1. Encapsulation

 a concept of bundling of data related variables, properties and method in one class

즉, 여러 속성을 class 내에 모아두는 것을 의미한다. => Reduce complexity + increase reusuability 를 실현.

 

 

지역 변수 등이 은닉화 되어 있는 것.

// 스코프를 이용한 은닉
function Person() {
  // 지역변수
  let name = "park";
  // interface
  this.getName = function() {
    return name;
  };
  this.setName = function(newName) {
    name = newName;
    return name;
  }
}

const human = new Person();
human.name = "kim";

console.log(human.name); //kim
console.log(human.getName());//park 은닉되어 있던 park. 
console.log(human.setName('Seo'));//Seo 은닉되어 있던 name 변경.
console.log(human.getName());//Seo => name 변경 후, Seo 가 출력된다. 

 

2. Inheritance

a mechanism to derive a class from another class to share a set of attributes and methods.

부모(Basic class) 의 속성을 물려받는다.

 

1) extends 키워드

2) 메소드 오버라이딩

: 상속 관계에 있는 부모 클래스에서 이미 정의된 메소드를 자식 클래스에서 같은 시그니쳐를 갖는 메소드로 다시 정의하는 것이라고 할 수 있습니다. 즉,  상속받은 부모 클래스의 메소드를 재정의하여 사용하는 것을 의미합니다.

 

2-1) ES6

Ferrari 에서 run(), stop() 등의 메소드를 자체 정의하면, 상속받은 메소드가 아닌 Ferrari 자체의 메소드를 사용합니다.

그리고 부모의 메소드를 사용하기 위해서는 super.stop() 과 같이 super 키워드를 사용하면 됩니다.

2-2) pseudoclassical

function Phone(model, company, os, lte){
  this.model = model;
  this.company = company;
  this.lte = lte;
  this.os = os;
}
//secretary 함수 추가
Phone.prototype.secretary = function (name) {
  console.log(`${name}`)
}
//inputKeypad 함수 추가 
Phone.prototype.inputKeypad = function (){
  console.log('키패드를 입력했습니다.')
}
//touchScreen 함수 추가 
Phone.prototype.touchScreen = function () {
  console.log('화면을 터치 했어요.')
}
//Phone 속성을 상속 받고, 인자 전달 
function IPhoneMini (model, company, os, lte) {
  Phone.apply(this, [model, company, os, lte])
  this.size = 'small';
}

IPhoneMini.prototype = Object.create(Phone.prototype);
IPhoneMini.prototype.constructor = IPhoneMini;

// 오버라이딩1
IPhoneMini.prototype.secretary = function(){
  Phone.prototype.secretary.call(this, '시리야~')
}

// 오버라이딩2
IPhoneMini.prototype.touchScreen = function () {
  Phone.prototype.touchScreen.call(this);
  console.log('오른쪽으로 스크롤 했어요.');
  return '뒤로가기';
}

const iphoneMini = new IPhoneMini('black', 'apple', 'ios', '4g')

iphoneMini.secretary(); // 시리야~
let backStep = iphoneMini.touchScreen();
console.log(backStep); //화면을 터치 했습니다. 오른쪽으로 스크롤 했습니다. 뒤로가기

 

3) 생성자 오버라이딩

 

상속 class에서 this를 사용하기 전에 super(...)를 호출해야 하는이유?

 

  • 일반 클래스가 new와 함께 실행되면, 빈 객체가 만들어지고 this에 이 객체를 할당합니다.
  • 반면, 상속 클래스의 생성자 함수가 실행되면, 일반 클래스에서 일어난 일이 일어나지 않습니다. 상속 클래스의 생성자 함수는 빈 객체를 만들고 this에 이 객체를 할당하는 일을 부모 클래스의 생성자가 처리해주길 기대합니다.
  • 따라서, 상속 클래스의 생성자에선 super를 호출해 부모 생성자를 실행해 주어야 합니다. 그렇지 않으면 this가 될 객체가 만들어지지 않아 에러가 발생합니다.

 

class Animal {

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

  // ...
}

class Rabbit extends Animal {

  constructor(name, earLength) {
    super(name);
    this.earLength = earLength;
  }

  // ...
}

// 이제 에러 없이 동작합니다.
let rabbit = new Rabbit("흰 토끼", 10);
alert(rabbit.name); // 흰 토끼
alert(rabbit.earLength); // 10

(출처: ko.javascript.info/class-inheritance)

 

4) 메소드 오버로딩

메서드 오버로딩(overloading)은 동일한 이름을 가진 여러 개의 메서드를 만든 후 매개변수 타입과 개수에 맞는 메서드가 자동으로 호출되는 기능을 말합니다.

 

// java, c++, c# 등의 객체지향 언어에서는 함수를 선언할때 파라미터에 대한 타입을 지정함.
// javascript에서는 함수를 선언할때 파라미터에 대한 타입을 지정하지 않음.
// 따라서 같은 이름의 함수를 구분 할 수가 없음.
// 그래도 구현하고자 한다면 하나의 함수 안에서(동일한 이름의 함수를 만들 수 없음.) 파라미터에 대한 구분을 해줘야함.

// 방법1. 파라미터의 길이를 활용 -> switch문을 통한 파라미터 길이의 구분
function inputKeypad(name, type, callback){
  switch(arguments.length){
      case 0:
          break;
      case 1:
          name();
          break;
      case 2:
          type(name);
          break;
      case 3: //
          callback(name, type);
          break;
      default:
          break;
  };
};

//example callback
function callback(a, b){
  if(b){ // b 파라미터가 전달인자로 할당 된다면
    console.log(`이 키패드는 ${a} 키패드 입니다.`)
    // 이 경우 b는 타입으로 전화용 키패드(or 다른 종류의 키패드)에 맞는 숫자 키패드(or 다른 종류의 키패드)의 배열을 받음.
  } else if(a) {
    console.log('이 키패드는 문자용 키패드 입니다.');
  } else{
    console.log('이 키패드는 Default 키패드 입니다.')
  }
}

//example callKeypad
const callKeypad = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9],
  ['*', 0, '#'],
]

//example messageKeypad
const messageKeypad = [
  ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'],
  ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', '이하 생략'],
]

inputKeypad('전화용', callKeypad, callback); //case3 => callback('전화용', callKeypad) =>  console.log(`이 키패드는 ${'전화용'} 키패드 입니다.`)
inputKeypad(messageKeypad, callback); //case2 => callback(messageKeypad) => console.log('이 키패드는 문자용 키패드 입니다.');
inputKeypad(callback);//case1 => callback() => console.log('이 키패드는 Default 키패드 입니다.')
// 방법2. if-else 조건문으로 파라미터의 순서를 구분하여 파라미터가 들어왔을때 해당 타입을 확인하여 구분.
// 전화용 or Other 키패드 경우1: 하나의 문자열, 하나의 배열, 하나의 콜백을 파라미터로 갖는 함수
// 문자용 키패드 경우2: 하나의 배열과 하나의 콜백을 파라미터로 갖는 함수
// Default 키패드 경우3: 콜백만 파라미터로 갖는 함수
function inputKeypad (name, type, callback) { 
  if(typeof callback === 'function'){ // 3개의 파라미터가 전달인자로 부터 할당 받을 때
    callback(name, type);
  } else if(typeof type === 'function'){ //2개의 파라미터가 전달인자로 부터 할당 받을 때
    type(name);
  } else{ //1개의 파라미터가 전달인자로 부터 할당 받을 때
    name();
  }
}

 

3. Abstraction

a concept to handle complexity by hiding unnecessary details from the user.

ex) 페라리 생산자는 시스템 안에 있는 복잡한 것들을 최종 사용자에게 노출시켜 걱정을 끼치고 싶지 않다. 특히 몰래 만든 페라리의 부스터 기능을 숨기고 싶다. 노출시키고 싶지 않은 객체 멤버의 경우엔 this 키워드가 아닌 var, const, let 키워드를 사용하여 클래스의 메소드 내에서 해당 키워드에 접근시킬 수 있다.

 

booster 기능과 gear를 노출시키고 싶지않다... 그리고 편-안

추상화?

인터페이스: 상호작용을 위해 필요한 입력창, 출력창, 마우스 포인트를 이용한 매개체

ex) 눈, 코, 혀, 귀, 손가락 등....

내부 구현을 알지 못해도 노출되어 있는 메소드( = 인터페이스.)로 알 수 있는 것.

 

 

4. Polymorphism

a language’s ability to process objects of various types and classes through a single, uniform interface.

다형성은 특정한 기능을 선언(설계)부분과 구현(동작)부분으로 분리한 후 구현부분을 다양한 방법으로 만들어 선택 사용할 수 있게 하는 개념입니다.

 

ex) 아이폰이라는 class는 전화라는 class의 특징을 상속 받은 후

아이폰만의 고유한 특징을 수정 및 추가 한 것이라고 볼 수 있다. 

class 전화 ----상속--아이폰 특징 추가/수정--> class 아이폰

class 전화 ----상속--갤럭시 특징 추가/수정--> class 갤럭시

 

예시)

학생인 john 은 쉽게 잘 수 없다.

steve와 john 동일한 방법으로 sleep() 메소드를 실행시키지만,

동작 방법이 다르기 때문에 결과 값도 다르다.

 

1) Pseudoclassical Inheritance

 

  • Human.prototype.apply(this) (or Human.prototype.call(this)) 
    => Super(_)로 해결
  • Object.create(proto), Student.prototype.constructor = Student; 등으로 연결 
    => extends 키워드로 해결

2) ES6 Inheritance

클래스?

함수의 한 종류.

객체 지향 프로그래밍에서 특정 객체를 생성하기 위해 변수와 메소드를 정의한 프로토타입(틀)이다.

객체를 정의하기 위한 상태(멤버 변수)와 행위(메소드)로 구성된다.

User라는 이름을 가진 함수를 만든다.

상태 : 생성자 메소드(constructor)에 적혀 있는 멤버 변수

행위: sayHi 같은 클래스 내에서 정의한 메소드

 

new User 를 호출하여 새 객체(object)를 만들면 프로토타입의 멤버변수와 메소드를 활용할 수  있게 된다.

 


자바스크립트에서 객체를 만드는 4가지 방법

 

1. Functional

2. Functional Shared

extend 함수를 Car 객체 안에서 실행시키는데

someInstance 객체 안에

someMethods의 move function을 넣어주어

car1 또는 car2 의 move() 메소드 실행을 가능케 만든다.

car1.move() => 6

car2.move() => 21

 

Functional  방식은 인스턴스를 생성할 때마다 모든 메소드를 someInstance에 할당하는데, 이 경우 각각 인스턴스들이 메소드의 수만큼 메모리를 더 차지하게 된다.

 

Functional Shared 방식을 사용하면

someMethods의 메소드들의 메모리 주소만 참조하므로 효율이 좋아진다.

 

 

 

명확한 이해를 위해........

 

3. prototypal

 

Object.create 는 특정 객체를 프토로 타입으로 하는 객체를 생성해주는 함수입니다.

즉, someInstance는 someMethods 객체의 move 메소드를 사용할 수 있게 된다.

 

4. pseudoclassical

객체 Car 의 prototype 이라는 객체 안에 move 메소드를 추가하여 사용할 수 있게 만든다.

 

 

출처: libertegrace.tistory.com

Comments