JavaScript

객체지향(Object-Oriented)

nacjji 2022. 12. 10. 04:20
반응형
객체지향

소프트웨어의 핵심을 기능이 아닌 객체로 삼으며, 누가 어떤 일을 할 것인가에 초점을 맞춘다. 즉 객체를 도출하고 각각의 역할을 정의하는 것에 초점을 맞춘다. 

책임과 권한을 가진 객체들이 서로 메시지를 주고받으며 협력해서 필요한 기능을 수행하도록 시스템을 개발하는 것을 객체 지향이라고 한다. 

 

객체지향 vs 절차지향

객체지향적인 소프트웨어가 절차지향적인 소프트웨어와 다른 점은 캡슐화, 다형성, 클래스 상속을 지원하는지의 여부와 데이터 접근 제한을 걸 수 있는지이다.

 

캡슐화

캡슐화란 객체 내부의 세부적인 사항을 감추는 것을 말한다. 

아이들에게 가루약을 먹일 때 아이들은 가루를 보면 쓸 것이라고 생각하고 잘 먹지 않을 것이다. 하지만 캡슐 안에 가루를 숨겨서 삼키게 하면 아이들 도 잘 먹을 수 있는 것처럼 객체 또한 내부의 정보를 외부로 노출시키지 않는 정보 은닉을 하기 위한 기법이라고 보면 된다. 

캡슐화의 목적은 변경하기 쉬운 객체를 만드는 것이다. 캡슐화를 통해 객체 내부의 접근을 제한하면 객체와 객체 사이의 결합도를 낮출 수 있기 때문에 설계를 좀 더 쉽게 변경할 수 있게 된다.

 

클래스 상속

상속이란 이미 정의된 상위 클래스의 특징을 하위 클래스에서 물려받아 코드의 중복을 제거하고 코드의 재사용성을 증대시키는 것을 말한다. 

즉, 하나의 클래스가 가진 특징(함수, 변수 및 데이터)을 다른 클래스가 그대로 물려 받는 것을 말한다. 

개별 클래스를 상속 관계로 묶음으로써 클래스 간의 체계화된 구조를 파악하기 쉬워진다. 

데이터와 메소드를 변경할 때 상위에 있는 것만 수정하여 전체적으로 일관성을 유지할 수 있다. 

기존에 작성된 클래스를 물려 받아 재활용하여 사용하므로 객체지향 프로그래밍의 중요한 기능 중 하나이다.

// 부모클래스
class Mother {
  // 부모클래스 생성자
  constructor(name, age, gender) {
    ;(this.name = name), (this.age = age), (this.gender = gender)
  }
  // 부모 클래스 getGender 메소드
  getGender() {
    return this.gender
  }
}

// 부모 클래스를 상속받은 자식 클래스
class Child extends Mother {
  // 자식 클래스 생성자
  constructor(name, age, gender) {
    super(name, age, gender)
  }
}
const child = new Child("Jiwon", "27", "Male")
console.log(child.name)
console.log(child.age)
console.log(child.gender)

 

추상화

객체에서 불필요한 부분을 생략하고 객체 속성 중 공통된 부분을 모아 상위 개념으로 새롭게 선언하는 것을 추상화라고 한다. 

객체 속성 중 공통적이고 중요한 것에만 중점을 두어 모델화 하는 것이다. 

추상화는 객체들의 공통적인 특성을 파악하여 필요 없는 특성을 제거하는 과정이다. 

시스템을 구축하기 전에 시스템 구조 및 구성을 가시적으로 볼 수 있고 해당 시스템과 유사한 모델을 만들어 여러가지 테스트를 할 수 있다. 

복잡한 내부 구현에 신경쓰지 않고, 외부에 노출되어 있는 인터페이스만을 이용하여 코드를 작성할 수 있다. 

클래스를 설계할 때 공통적으로 묶일 수 있는 기능을 추상화 -> 추상 클래스 -> 인터페이스로 모델링해서 향후 다형성으로 확장할 수 있도록 설계한다. 

인터페이스란 클래스를 정의할 때 메소드와 속성만 정의하여 인터페이스에 선언된 프로퍼티 또는 메소드의 구현을 강제하여 코드의 일관성을 유지할 수 있게 만든다. 

 

다형성

객체가 연산을 수행하게 될 때 하나의 행위에 대해 각 객체가 가지고 있는 고유한 특성으로 다른 여러 형태로 재구성 되는 것을 말한다. 

즉, 동일한 메소드의 이름을 사용하지만 메소드에 대해 클래스마다 다르게 구현되는 개념이 다형성이다. 

다형성을 통해 역할과 구현을 분리해서 오버라이딩(Overriding)을 통해 서비스의 구현기능을 유연하게 변경, 확장이 가능하다. 

오버라이딩이란 부모 클래스에서 선언한 메소드들을 자식 클래스에서 새롭게 선언하는 것을 말하고, 오버로딩은 부모 클래스에서 생성한 메소드들을 자식클래스가 가지고 왔을 때 현재 가지고 있는 매개변수를 변경해서 생성한 메소드를 오버로딩이라고 한다. 

/** Polymorphism **/

class Employee {
  constructor(name) {
    // name 멤버변수 할당
    this.name = name
  }

  // Employ 클래스의 name 출력
  buy() {
    console.log(`${this.constructor.name} 클래스의 ${this.name}님이 물건을 구매하였습니다.`)
  }
}

class User {
  constructor(name) {
    // name 멤버변수 할당
    this.name = name
  }

  buy() {
    // User 클래스의 name 출력
    console.log(`${this.constructor.name} 클래스의 ${this.name}님이 물건을 구매하였습니다.`)
  }
}

const employee1 = new Employee("나지원")
const employee2 = new Employee("나지투")
const user1 = new User("나지쓰리")
const user2 = new User("나지포")

const polymorphismArray = [employee1, employee2, user1, user2]
// polymorphismArray에 저장되어 있는 Employee, User 인스턴스들의 buy 메소드를 호출한다.
polymorphismArray.forEach((polymorphism) => polymorphism.buy())

// Employee 클래스의 나지원님이 물건을 구매하였습니다.
// Employee 클래스의 나지투님이 물건을 구매하였습니다.
// User 클래스의 나지쓰리님이 물건을 구매하였습니다.
// User 클래스의 나지포님이 물건을 구매하였습니다.

 

의존성(Dependency)

의존성이란 객체들이 협력하는 과정 속에서 해당 객체들이 다른 객체를 의존하게 되는 정도를 나타낸다. 

어떤 객체가 변경될 때 그 객체에 의존하는 다른 객체도 함께 변경된다. 

만약 하나의 객체가 변경될 경우 의존하고 있는 다른 객체 또한 변경이 이루어지게 된다. 

 

결합도(Coupling)

의존성의 정도를 나타내며 다른 모듈에 대해 얼마나 많은 의존성을 가지고 있는지를 나타낸다. 

객체 사이의 의존성이 과한 경우를 가리켜 결합도가 높다고 말한다. 

두 객체 사이의 결합도가 높을 수록 함께 변경될 확률도 높아지기 때문에 변경하기 어려워진다. 

다라서 설계의 목표는 객체 사이의 결합도를 낮춰 변경이 용이한 설계를 만드는 것이어야 한다. 

 

응집도(Cohesion)

모듈에 포함된 내부 요소들이 각각 연관되어 있는 관계의 정도를 나타낸다. 

밀접하게 연관된 작업만을 수행하고 연관성 없는 작업은 다른 객체에 위임하는 객체를 가리켜 응집도가 높다고 말한다. 

1개의 메소드가 내부에서 변수를 많이 사용할 수록 해당 메소드와 클래스는 응집도가 높아지게 된다. 

자신의 데이터를 스스로 처리하는 자율적인 객체를 만들면 결합도를 낮출 수 있을 뿐더러 응집도를 높일 수 있다. 

객체의 응집도를 높이기 위해서는 객체는 스스로 자신의 데이터를 책임져야 한다.