TIL 객체 지향 프로그래밍

백광호·2021년 1월 14일
0

TIL

목록 보기
35/55

코드스테이츠 39일차

오늘은 어제 예고했던 객체 지향 프로그래밍에 대해
포스팅 해보도록 하겠다.

아직 배우는 단계라 조금은 틀린 지식이 있을 수 있고
이부분은 일단 작성 후 수정을 통해 개선하도록 하겠다.

새로 배운 것들

OOP(객체 지향 프로그래밍)

Object Oriented Programming
객체 지향 프로그래밍은 사람이 세계를 보고 이해하는 방법을 흉내낸 방법론
이라고 할 수 있다.

예를들어 스마트폰이 있다면 기본적인 기능인
전화를 하거나, 문자를 주고받는건 어느 스마트폰이던 가능하지만
특정 제조사마다 다른 기능을 가지고 있다는것을 알고있다.

객체 지향 프로그래밍으로 해석하면
스마트폰이라는 객체에 기본적인 기능인 전화나 문자라는 메소드가 들어있고
제조사마다 다른 기능은 그 제조사의 속성이라고 볼 수 있다.

이를 코드로 표현한다면 이렇게 볼 수 있을 것 같다.

class Smartphone {
  constructor(manufacturer, voice) {
    // attribute(fields)
    this.manufacturer = manufacturer
    this.voice = voice
  }
    // method
  message() {
    console.log(`${this.manufacturer} Smartphone`)
  }
}

    // object
const samsung = new Smartphone('Samsung', 'bixby');
const apple = new Smartphone('Apple', 'siri');

console.log(samsung.manufacturer, samsung.voice); // output 'Samsung', 'bixby'
console.log(apple.manufacturer, apple.voice);  // output 'Apple', 'siri'
samsung.message(); // output 'Samsung Smartphone'
apple.message(); // output 'Apple Smartphone'

이렇게 클래스를 만들 수 있는데 과정을 살펴보면
스마트폰이라는 객체의 기본 기능은 method에 적은 기능이다.
메세지를 보내는 기능으로 어느 스마트폰이나 가능하다.

하지만 attribute에 적은것은 같은 스마트폰이라도
제조사마다 다른 기능이 들어가 있는 것을 적어두었다.

이렇게 클래스를 이용해 템플릿처럼 만들어 두고
이제 객체를 생성해 사용만 하면 된다.

생성할 때에는 constructor의 매개변수가 들어가고
thisconstructor의 인스턴스가 된다.

이렇게 생성된 객체의 모습은 아래와 같다.

스마트폰이라는 객체에 제조사는 samsung이 되었고
음성인식은 bixby가 되었다.

이제 이 객체에 메소드를 실행시켜보면 다음과 같이 나온다.

이런 메소드에는 속성을 담아서 실행시킬 수 있다.
지금은 제조사를 포함해 메세지를 출력시키도록 코드를 짰다.

이렇듯 기본적인 기능은 동시에 사용할수 있지만
그 속성은 서로 다른게 이용할 수 있는것이
객체 지향 프로그래밍의 특징이라고 볼 수 있다.

이런점을 이용하면 학생들의 성적이나
회원 관리등을 쉽게 할 수 있을 것 같다.

객체 지향 프로그래밍의 특징에 대해 더 서술하자면
객체 지향 프로그래밍에는 크게 4가지 특징을 가지고 있다.

Encapsulation (캡슐화)

캡슐화는 코드의 재사용성을 높이는 방법 중 하나로
객체 지향 프로그래밍을 이용했을 때 얻을 수 있는 장점 중 하나이다.

let mathScore = 32
let englishScore = 40
let scienceScore = 24

function getAverage(math, eng, sci) {
  return (math + eng + sci) / 3
}

getAverage(mathScore, englishScore, scienceScore)
// output 32

위 코드는 한 학생의 성적을 평균으로 낸 코드이다
한 학생의 평균을 계산할 때에는 문제가 없는데
여러 학생의 평균을 내야 한다면 변수명을 계속 바꿔줘야 할 것이다.
이런 번거로움을 줄일 수 있는 것이 객체의 특징이다.
위 코드를 캡슐화 하면 아래와 같이 바꿀 수 있다.

let student = {
  mathScore: 32,
  englishScore: 40,
  scienceScore: 24,
  getAverage: function() {
    return (this.mathScore + this.englishScore + this.scienceScore) / 3
  }
};

student.getAverage()
// output 32

이렇게 바꾸면 student에 그냥 학생의 이름을 넣어도 될 것이다.

Inheritance(상속)

객체는 부모의 속성(key)를 상속(계승)받아 가질수 있는 특징이 있다.
가장 쉽게 찾아볼 수 있는 객체가 바로 HTMLElement인데

이런식으로 확인해보면 __proto__Element라고 적혀있는 것을 볼 수 있다.

따라서 HTMLElement의 부모가 Element인데
HTMLElementElement의 속성을 상속받아 모두 가지고 있다.

클래스 예제로 다시 한번 살펴보면

class Shape {
  constructor(width, height, color) {
    this.width = width;
    this.height = height;
    this.color = color;
  }
	
  draw() {
    console.log (`deawing ${this.color} color!`)
  }

  getArea() {
    return this.width * this.height;
  }
}

class Rectangle extends Shape {} // 상속
const rectangle = new Rectangle(20, 20, 'blue');
rectangle.draw(); // output 'deawing blue color!'

하나의 클래스에서 사용한 코드를
다른 클래스에서도 상속시켜 사용할 수 있다.

위 예제에서는 Shape에서 사용한 코드를 Rectangle에 상속시켜
동일한 코드를 사용한 결과이다.
상속시킬때에는 estends를 사용한다.

상속받아 사용했기 때문에 Shape로 실행한 결과와
Rectangle로 실행한 결과는 같은 결과를 얻게 된다.

추가로 상속받을 때 부모의 요소를 남기는 방법이 두가지 있다.
하나는 위의 예제처럼 남길 요소는 적지 않는것이고

두번째 방법은 super를 이용하는 방법이다.

class Rectangle extends Shape {
  constructor(width, height, color) {
    super(width, height, color)
  }
} // super사용
const rectangle = new Rectangle(20, 20, 'blue');
rectangle.draw();

이렇게 적으면 부모의 요소는 남기게된다.

Abstraction(추상화)

사용자 입장에서 보이지 않지만 복잡한 계산이 이루어지는 과정을 말한다.
예를들어 클래스를 이용해 프로필을 만든다고 가정했을 때
나이를 잘못 입력한 경우 0으로 표시되게 바꾸는 과정을 만들어 보자

class Profile{
  constructor(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender
  }
  
  get age() {
    return this._age
  } // 나이 체크 과정
  set age(value) {
    this._age = value < 0 ? 0 : value;
  } // 잘못된 나이를 바로잡는 과정
}
// 이 과정에서 변수의 이름이 같을 경우 무한반복해 콜스택이 쌓이기 때문에 이름은 다르게 만든다

const pangho = new Profile('pangho', -1, 'male')
console.log(pangho.age) // output 0

사람의 나이가 -1이 된다면 말이 되지 않는다.
때문에 음수의 값을 받았을때 해당 값을 바꿔주는 과정이 필요하다

이 과정에서 사용한 gettersetter는 추상화와 가장 가까운데
사용자가 데이터를 잘못 입력해도
get age()에서 잘못 입력된 나이에 접근해
set age(value)에서 잘못된 나이를 바로잡아준다.

이 과정을 사용자가 접근할 수는 없다 왜냐하면
gettersetterprivate값이기 때문에 외부에서
해당 변수 및 메소드에 접근할 수 없고 클래스 내에서만 사용이 가능하다.
이 말은 사용자는 gettersetter에 접근할 수 없다는 말과 같다.

속성값도 private값으로 만들 수 있다.

class Experiment {
  publicField = 2;
  #privateField = 32;
}

const experiment = new Experiment();
console.log(experiment.publicField) // output 2
console.log(experiment.privateField) // output undefined

이렇게 #을 붙여주면 된다.
다만 사용자가 접근할 수 없기 때문에 그냥 이런게 있다 정도만 알아두자.

이처럼 추상화는 클래스의 정보를 숨김으로 안정성을 높이는
객체 지향 프로그래밍의 특성이라고 볼 수 있다.

Polymorphism(다형성)

다형성은 상속과 밀접한 연관이 있다.
아까 위에서 상속을 설명할 때 사용했던 예제를 다시 가져와보자

class Shape {
  constructor(width, height, color) {
    this.width = width;
    this.height = height;
    this.color = color;
  }
	
  draw() {
    console.log (`deawing ${this.color} color!`)
  }

  getArea() {
    return this.width * this.height;
  }
}

만약 이 중 상속받은 자식이
getArea() 함수를 하나 더 만들면 어떻게 될까?

class Triangle extends Shape {
	getArea() {
		return (this.width * this.height) / 2;
	}
}

const triangle = new Triangle(20, 20, 'red');
triangle.getArea()

상속받은 getArea()와 새로 만든 getArea() 두개가 생성될까?

그렇지 않다, 만약 동일한 이름의 함수를 새로 만들게되면
추가되는것이 아닌 덮어씌워지게 되는데 이 과정을
메소드 오버라이딩(Mathod Overriding)이라고 한다.

결과적으론 덮어씌운 getArea()값을 출력하게 된다.
이렇듯 다형성은 클래스의 다양한 형태를 가지는 특성을 가지고 있다.

위 네가지 특성중 상속과 다형성은 자주 사용하게되니 꼭 알아두자

profile
안녕하세요

0개의 댓글