Medium - Composition Over Inheritance

원문 - Mahmoud Abdelwahab

Trend 파악을 Medium 기고문 요약 포스팅 - 상속을 넘어서는 Composition

Photo by Kevin Ku on Unsplash

객체 기반 프로그래밍은 소프트웨어를 만드는 과정에서 매우 유명한 패러다임 중에 하나입니다. 그러나 제가 자바스크립트에 대해 더욱 많이 배우게 되고 함수형 프로그래밍을 알게되면서 생각이 완전히 바뀌었습니다. 이 포스트에서는 제가 배운 것들을 예제를 통해 설명해 드리겠습니다.

예제: 여러분은 호그와트의 학생이 주인공인 게임을 만들려고 합니다. 그리고 그는 선생님으로부터 마법을 교육받죠. 그래서 당신은 Student 클래스를 만들어서 학생 객체가 테스트를 할 수 있도록 할 것이고 Teacher라는 클래스를 만들어서 선생님 객체가 테스트의 성적을 부여할 수 있도록 할 것입니다.

Student
  .takeTest()
Teacher
  .gradeTest()

여러분이 식사, 수면, 걷기와 같은 기능들을 추가하고 싶다고 가정합시다. 그럼 이런 형태가 될 것입니다.

Student
  .takeTest()
  .sleep()
  .eat()
  .walk()
Teacher
  .gradeTest()
  .sleep()
  .eat()
  .walk()

문제는 위의 코드가 DRY 원칙을 따르지 않는다는 것입니다. Don’t Repeat Yourself, 그래서 솔루션은 부모 클래스를 만들어 공통 기능은 수면, 식사, 걷기 특징을 넣는 것입니다. 그래서 이런 형태가 되겠죠.

Human
  .sleep()
  .eat()
  .walk()
    Student
     .takeTest()
    Teacher
     .takeTest()

짠! 문제가 해결되었습니다! 모두가 행복합니다. 그러나 몇 달후 게임을 더욱 짜릿하게 만들기 위해서 당신은 다른 캐릭터로서 드래곤을 추가하기로 결정했습니다. 그러면 이런 형태가 되겠죠.

EvilDragon
 .breathFire()
 .fly()
Human
  .sleep()
  .eat()
  .walk()
    Student
     .takeTest()
    Teacher
     .takeTest()

뭐 이래도 크게 문제는 없지만 만족스럽지 못합니다. 그래서 당신은 게임의 현실성을 높이기 위해 드래곤 캐릭터에게 수면과 식사 같은 기능을 추가하려고 합니다. 그러면 이런 형태가 됩니다.

GameObject
  .sleep()
  .eat()
    EvilDragon
     .breathFire()
     .fly()
    Human
     .walk()
        Student
        .takeTest()
        Teacher
        .takeTest()

자 이제 작업이 완전히 끝났습니다. 그러나 여러분의 게임에서 선생님이 마법을 연구한 끝에 공중부양이 가능하게 되었습니다. 이제 문제가 발생합니다. 어떻게 프로젝트의 구조를 수정하실 건가요? 당신은 모든 사람이 날 수 있는 것이 아니기 때문에 사람 클래스에 flying 기능을 추가할 수 없거니와 선생님 클래스를 EvilDragon 클래스로부터 상속받을 수도 없습니다. 왜냐하면 선생님은 브레스파이어를 못쓰거든요,

이런 해답이 있습니다. 그냥 선생님 클래스에 fly 기능을 추가하기 위해 브레스 파이어 같은 기능을 추가하는 것입니다. 그러나 이렇게 절대 사용하지 않을 기능을 추가하는 것은 나중에 바나나를 얻기 위해 바나나를 든 고릴라를 얻게되는 문제에 빠질 수 있습니다. 자 여러분은 이제 장벽에 막혀있고 게임의 아이디어를 표현할 수 없습니다. 물론 여러분은 그냥 작업을 반복할 수도 있지만 여러분이 굉장한 개발자고 작성한 코드를 꼼꼼히 분석하는 사람이라면 절대 그렇게 하지 않겠죠.

걱정마세요 여기에 해답이 있습니다. Composition 입니다.

상속에서는 여러분은 클래스가 어떤 것인지에 따라 구성을 하게 됩니다. 함수형 프로그래밍과 composition에서는 클래스가 무엇을 하는지에 따라 구성하게 됩니다.

상속을 넘어서는 composition을 선호하고 객체가 무엇인지 생각하는 것 대신에 무엇을 하는지 생각하다보면 여러분은 깨지기 쉽고 꽉 끼는 상속 구조로부터 자유로워 집니다.

아래의 코드는 객체의 관점에서 우리의 게임을 생각할 때의 모습입니다.

class Teacher{
    constructor(name){
        this.name = name;
    }
    sleep(){
        setTimeout(console.log('Damn, that was a great nap'),8000);
    }
}
const teacher = new Teacher(Dumbledore);
teacher.sleep();
//outputs 'Damn, that was a great nap' after 8 seconds

우리가 프로젝트에 함수형 접근을 사용한다면 우리는 독립된 함수들의 컬렉션을 갖게될 것이며 각각은 하나의 목적을 수행할 것이고 함수의 유지보수가 쉬움은 물론 어떤 결과가 발생할 지 더욱 예측하기 쉽게 될 겁니다. 우리가 객체를 만들 때 우리는 단지 우리가 필요한 함수들을 포함하기만 하면 됩니다.

//same code rewritten using factory functions
//factory functions are functions that create objects
//we will use
const sleep = () => {
     return setTimeout(console.log('Damn, that was a great nap'),8000);
}
//here we create the object, but for simplicity let's leave it that way
Object.assign(
)

어느 쪽이 이길까요? 글쎄, 아마 당신은 객체간의 관계가 있을 때 보통 상속을 사용하겠죠. 저와 무하마드 알리가 사람이기 때문에 우리는 사람이 가진 모든 것을 상속 받을 것입니다. 반면에 composition을 사용한다면 우리는 특정한 기능을 가진 객체를 가지게 될 것입니다. 예를 들면 자동차는 엔진 컴포넌트를 가지게 됩니다.

그러나 이것도 완벽한 것은 아닙니다. 왜냐하면 자동차는 운송수단이고 저와 무하마드 알리는 팔이 있고 물건을 들어올릴 수 있죠. 그래서 실생활에서 여러분은 이런 패턴을 모두 따를 수도 있습니다.

많은 엔지니어링과 마찬가지로 상황에 따라 다릅니다. Composition이냐 상속이냐, 여러분은 자신의 코드베이스와 팀, 그리고 작업의 특성과 미래의 목표를 고려해야 합니다. 직업에 적합한 툴을 고르도록 하세요.

Summary

  • 객체를 표현하는 방식에 있어서 Composition과 Inheritance의 차이.
  • 각 패턴마다 장단점이 있기 때문에 함수형 프로그래밍에서 객체를 정의하는 방법을 알되 상황에 맞게 사용하는 것이 중요하다.
  • 함수형 언어에서 객체는 그것이 무엇인지, 다른 객체와의 관계가 어떤지 보다는 어떤 기능을 수행하는지의 관점에서 생각해야 한다.
  • 함수형 언어에서는 하나의 기능만 수행하는 단일 함수들의 집합의 이용하여 객체에서 사용하는 기능들만 import하여 객체를 구성하기 떄문에 유지보수와 객체의 결과를 예측하기 쉽다는 장점이 있다.

© 2019. All rights reserved.

Powered by Hydejack v8.1.1