Day +22

비트·2023년 5월 11일
0

CodeStates

목록 보기
22/54
post-thumbnail
post-custom-banner

객체 지향

클로저 모듈 패턴

  • 메서드 호출은 객체.메서드()와 같이 객체 내에 메서드를 호출하는 방법을 의미

  • 단순 객체를 사용한 이러한 예제를 흔히 볼 수 있습니다. 다음은 카운터를 구현한 예제

    • ⚠️ 메서드 호출 방식을 이용할 때에는 화살표 함수를 쓰지 않습니다.
    • // counter 예시
       let counter1 = {
         value: 0,
         increase: function() {
           this.value++ // 메서드 호출을 할 경우, this는 counter1을 가리킵니다
         },
         decrease: function() {
           this.value--
         },
         getValue: function() {
           return this.value
         }
       }
      
       counter1.increase()
       counter1.increase()
       counter1.increase()
       counter1.decrease()
       counter1.getValue() // 2

클로저를 이용해 매번 새로운 객체 생성하기

  • 위의 함수 counter1은 단 하나의 객체만 만들 수 있습니다.
  • 만약 똑같은 기능을 하는 카운터가 여러 개가 필요하다면, 이 코드를 여러 번 만들어야 할까요?
  • 같은 코드를 그대로 복사/붙여넣기 해야 하므로, 재사용성이 떨어집니다.
  • 똑같은 기능을 하는 카운터를 여러 개 만드는 방법 중 하나는, 아래 예제 코드와 같이 클로저 모듈 패턴을 이용할 수 있습니다.

    • // 클로저 모듈 패턴으로 구현한 counter 예시
      function makeCounter() {
        let value = 0;
        return {
          increase: function() {
            value++;
          },
          decrease: function() {
            value--;
          },
          getValue: function() {
            return value;
          }
        }
      }
      
      let counter1 = makeCounter()
      counter1.increase()
      counter1.getValue() // 1
      
      let counter2 = makeCounter()
      counter2.decrease()
      counter2.decrease()
      counter2.getValue() // -2



클래스와 인스턴스

클래스와 생성자 함수의 차이

  • 클래스new 연산자 없이 호출하면 에러가 발생한다.
    • 하지만 생성자 함수new 연산자 없이 호출하면 일반 함수로서 호출된다.
  • 클래스 상속을 지원하는 extendssuper 키워드를 제공한다.
    • 하지만 생성자 함수extendssuper 키워드를 지원하지 않는다.
  • 클래스는 호이스팅이 발생하지 않는 것처럼 동작한다.
    • 하지만 함수 선언문으로 정의된 생성자 함수는 함수 호이스팅이, 함수 표현식으로 정의한 생성자 함수는 변수 호이스팅이 발생한다.
  • 클래스 내의 모든 코드에는 암묵적으로 strict mode가 지정되어 실행되며 strict mode를 해제할 수 없다.
    • 하지만 생성자 함수는 암묵적으로 strict mode가 지정되지 않는다.
  • 클래스의 constructor, 프로토타입메서드, 정적메서드는 모두 프로퍼티 어트리뷰트 [[Enumerable]]의 값이 false다. 다시 말해, 열거되지 않는다.


new 키워드

  • 인스턴스를 만들 때에는 new 키워드를 사용합니다.
    • 즉시 생성자 함수가 실행되며, 변수에 클래스의 설계를 가진 새로운 객체, 즉 인스턴스가 할당됩니다.
    • let avante = new Car ('hyndai', 'avante', 'black');
       let mini = new Car ('bmw', 'mini', 'white');
       let beetle = new Car ('volkswagen', 'beetles', 'red');
    • 각각의 인스턴스는 Car라는 클래스의 고유한 속상과, 메소드를 갖습니다.

ES5 클래스 작성 문법

  • function Car (brand, name, color) {
    	// 인스턴스가 만들어질 때 실행되는 코드
    }
  • 여기서 보이는 함수는, 객체지향 프로그래밍에서 생성자(constructor) 함수라고 부릅니다.
    • 인스턴스가 만들어질 때 실행되는 코드.
  • 참고로 생성자 함수는 return 값을 만들지 않습니다.

ES6 클래스 작성 문법

  • 그리고 클래스를 만드는 새로운 문법이 ES6(ECMAScript 6, 비교적 최근 자바스크립트 문법입니다)에 도입.
    • 바로 class 키워드입니다.
    • 최근에는 ES6 방법을 주로 사용합니다.
      • class Car {
        		  constructor (brand, name, color) {
          	  // 인스턴스가 만들어질 때 실행되는 코드
            }
        }



객체 지향 프로그래밍 (Object Oriented Programming)


캡슐화 (Encapsulation)

  • 캡슐화는 외부에서 앞서 말했던 데이터(속성)와 기능(메서드)을 따로 정의하는 것이 아닌, 하나의 객체 안에 넣어서 묶는 것.
  • 데이터(속성)와 기능(메서드)들이 느슨하게 결합되는 것
    • 느슨한 결합 : 코드 실행 순서에 따라 절차적으로 코드를 작성하는 것이 아니라, 코드가 상징하는 실제 모습과 닮게 코드를 모아 결합하는 것을 의미.

은닉화

  • 내부 데이터나 내부 구현이 외부로 노출되지 않도록 만드는 것.
    • 디테일한 구현이나 데이터는 숨기고, 객체 외부에서 필요한 동작(메서드)만 노출시켜야 합니다.


추상화 (Abstraction)

  • 추상화는 내부 구현은 아주 복잡한데, 실제로 노출되는 부분은 단순하게 만든다는 개념.
  • 추상화를 통해 인터페이스가 단순해진다.
    • 너무 많은 기능들이 노출되지 않은 덕분에 예기치 못한 사용상의 변화가 일어나지 않도록 만들 수 있다.

캡슐화 vs 추상화

캡슐화가 코드나 데이터의 은닉에 포커스가 맞춰져있다면,
추상화는 클래스를 사용하는 사람이 필요하지 않은 메서드 등을 노출시키지 않고, 단순한 이름으로 정의하는 것에 포커스가 맞춰져 있다.


✅ 클래스 정의 시, 메서드와 속성만 정의한 것을 인터페이스라고 부릅니다. 이것이 추상화의 본질



상속 (Inheritance)

  • 부모 클래스의 특징을 자식 클래스가 물려받는 것
    • 기본 클래스(base class)의 특징을 파생 클래스(derived class)가 상속받는다
  • 예를 들어 위 그림처럼, 사람(Human)이라는 클래스가 있다고 가정해 봅시다.
  • 사람은 기본적으로 이름과 성별, 나이와 같은 속성, 그리고 먹다, 자다 등과 같은 메서드가 있다고 볼 수 있다.
  • 추가적으로 학생(Student)이라는 클래스를 작성한다고 생각해 봅시다.
    • 그런데 이때 앞서 구현했던 사람(Human) 클래스의 속성과 메서드를 다시 구현한다면 비효율적일 것이다.
    • 학생의 본질은 결국 사람이므로, 상속을 이용하여 학생(Student) 클래스는 사람(Human) 클래스를 상속받을 수 있다.
    • 학생은 추가적으로 학습 내용, 공부하다 와 같은 속성/메서드를 추가한다.


다형성 (Polymorphism)

  • Polymorphism이라는 단어의 poly는 "많은", 그리고 morph는 "형태"라는 뜻을 가지고 있다.
    • 즉 "다양한 형태"를 가질 수 있다.
  • "말하다"라는 동작의 본질은 "입으로 소리를 내다"를 의미
    • 그러나, 각기 다른 동물들이 "말할 때" 제각각의 소리를 내는 것처럼, 객체 역시 똑같은 메서드라 하더라도, 다른 방식으로 구현될 수 있다.
  • 다형성을 HTML 엘리먼트를 예로 들어 설명해보자.
    • Textarea(TextBox), Select, 그리고 Checkbox 등 HTML에서는 이와 같이 모든 요소를 전부 Element라고 부른다.
  • 이 엘리먼트를 여러분들이 직접 구현한다고 생각해보자.
    • 모든 엘리먼트들은 전부 객체이므로, 내부적으로 모양을 그리고 화면에 뿌리는 메서드가 존재할 것이다.
  • 이 메서드가 render라는 이름을 갖고 있다고 가정해보자.
    • 이 경우에는, TextBox, Select, Checkbox의 공통의 부모인 HTML Element라는 클래스에 render라는 메서드를 만들고 상속을 받게 만들 수 있습니다.
  • TextBox는 가로로 긴 네모 상자와 커서가 있는 형태일 것이고, Select 박스는 눌렀을 때 선택지가 나오도록 화면에 그려야 할 것이다.
    • 이처럼 같은 이름을 가진 메서드라도 조금씩 다르게 작동한다. 이것이 바로 다형성이다.

다형성 (Polymorphism)이 없다면?

  • 기본(부모) 클래스에 종류별로 분기를 시켜서 하나하나 다르게 만들어야 할 것이다.

  • 또는 각각의 자식 클래스에 별도의 각기 다른 render 함수를 만들 수도 있겠지만, 엘리먼트라는 클래스의 본질상 "화면에 뿌린다"(render)는 개념은 부모가 갖고 있는 것이 합리적이다.

주요 개념에 대한 장점

주요 개념장점
캡슐화코드가 복잡하지 않게 만들고, 재사용성을 높인다.
추상화코드가 복잡하지 않게 만들고, 단순화된 사용으로 변화에 대한 영향을 최소화한다.
상속불필요한 코드를 줄여 재사용성을 높인다.
다형성동일한 메서드에 대해 if/else if와 같은 조건문 대신 객체의 특성에 맞게 달리 작성하는 것이 가능해진다.



객체 지향 차이점

은닉화(private 키워드)의 한계

  • Java나 TypeScript라는 프로그래밍 언어는 클래스 내부에서만 쓰이는 속성 및 메서드를 구분시키기 위해 private이라는 키워드를 제공.
    • 은닉화를 도와주는 기능이 JavaScript에서는 널리 쓰이지 않는다. 정확히는 지원하는 브라우저가 적다.
  • TypeScript의 예제로, name이라는 속성이 존재한다. 그러나 private 키워드가 붙어 있어서, 클래스 내부에서만 사용 가능하다.

    • // TypeScript 문법입니다.
      
       class Animal {
         private name: string;
      
         constructor(theName: string) {
           this.name = theName;
          }
        }
        
        new Animal("Cat").name; // 사용 불가
        // Property 'name' is private and only accessible within class 'Animal'. 
  • JavaScript에서는 은닉화를 돕기 위해서 일반적으로 클로저 모듈 패턴을 사용.
  • 클래스/인스턴스 형태로 만들 때에는 ES2019부터 #이라는 키워드가 도입


추상화(interface 키워드) 기능의 부재

  • 객체 지향 프로그래밍의 주요 키워드 중 하나인 추상화는, 속성과 메서드의 이름만 노출시켜서 사용을 단순화한다는 의미를 갖고 있다.

  • 즉, 인터페이스(interface)의 단순화를 의미합니다. Java나 TypeScript 언어는 언어의 주요 기능으로 interface를 구현해 놓았다.

    •  // TypeScript 문법입니다.
      
       interface ClockInterface {
         currentTime: Date;
         setTime(d: Date): void;
       }
      
       class Clock implements ClockInterface {
        currentTime: Date = new Date();
        setTime(d: Date) {
          this.currentTime = d;
         }
         constructor(h: number, m: number) {}
      }
      
    • 인터페이스의 이점은, 인터페이스가 일종의 규약처럼 간주되어, 인터페이스를 클래스로 구현하는 사람들이 이에 맞게 작성할 수 있게 돕는다.

    • 이는 클래스를 이용하는 입장에서 노출된 인터페이스를 통해 "이 클래스는 메서드 이름이 의도한 바대로 작동할 것이다" 라는 것을 명백히 드러나게 해 줍니다.

    • 또한 실질적인 구현 방법을 공개하지 않고, 사용법을 노출시키기에도 유리합니다.

profile
Drop the Bit!
post-custom-banner

0개의 댓글