객체지향 프로그래밍
프로그램의 처리단위가 객체인 프로그래밍 방법
현실 세계를 모델링하는 대표적인 프로그래밍 패러다임
데이터와 프로세스가 동일한 모듈 내부에 위치하도록 프로그래밍하는 방식
코드를 추상화해 직관적으로 사고할 수 있다. 현실 세계의 객체를 유연하게 표현할 수 있고 각각의 특성을 가지고 있어 특정 기능을 수행할 수 있다.
예를 들어 자동차는 객체이고 출발, 정지, 운행 등과 같은 기능을 수행할 수 있다.
객체지향 프로그래밍을 사용해야 하는 이유
만약 API 를 만들 때 복사-붙여넣기 방식으로 동일한 코드를 여러 군데 분산 시켜 놓는다면, 해당 로직을 수행할 때 복사한 코드를 일일히 찾아가며 수정을 해야하는 상황이 발생한다.
객체지향 프로그래밍을 사용한다면 코드 변경점이 발생하더라도 최소한의 수정으로 많은 시간을 절약할 수 있다.
발생한 문제점을 빠르게 인식하고 어떤 코드에서 오류가 났는지 빠르게 찾아 고쳐 개발에 소요되는 시간을 최대한 줄일 수 있게 된다.
객체지향 프로그래밍은 데이터와 프로세스를 하나의 단위로 처리하는 특성을 가지고 있기 때문에 코드를 수정할 때 어떤 코드에서 문제가 발생했는지 직관적으로 인지할 수 있으며 해당 로직을 수행하는 코드만 고치더라도 문제가 해결될 수 있는 장점이 있다.
객체지향 프로그래밍은 의존성을 효율적으로 통제할 수 있는 다양한 방법을 제공함으로써 요구사항 변경에 좀 더 수월하게 대응할 수 있는 가능성을 높여준다.
동작을 기준으로 프로그래밍을 진행하는 것보다 데이터를 중심으로 프로그래밍을 하게 되면 코드의 규모가 커지더라도 일관성을 유지하기 좋다.
객체지향 설계 5원칙(SOLID)
객체 지향 프로그래밍 및 설계의 다섯 가지 기본 원칙의 맨 앞단어를 하나씩 가져와 만든 것
프로그래머가 시간이 지나도 유지보수와 확장이 쉬운 시스템을 만들고자 할 때 사용한다.
- 단일 책임의 원칙(Single Responsibility Principle, SRP)
- 하나의 객체는 하나의 책임을 가져야 한다.
- 클래스나 모듈을 변경할 이유가 단 하나뿐이어야 한다
// 사용자의 인증을 검증하는 책임을 가진 UserAuth 클래스
class UserAuth {
// UserAuth 생성자
// 유저의 정보를 멤버 변수에 할당
constructor(user) {
this.user = user
}
// 유저 인증 메소드, user 멤버변수를 기반으로 인증하기 때문에 아무런 인자를 받지 않는다.
verifyCredentials() {
// ...
}
}
// 사용자의 설정을 변경하는 책임을 가진 UserSettings 클래스
class UserSettings {
constructor(user) {
// user 를 인자로 받지만 userAuth라는 인스턴스를 만들어서 멤버변수에 할당한다..
this.userAuth = new UserAuth(user)
}
changeSettings(userSettings) {
// 생성자에서 선언한 userAuth 객체의 메소드를 사용
if (this.userAuth.verifyCredentials()) {
// ...
}
}
}
- 개방-폐쇄의 원칙(Open-Closed Principle, OCP)
- 소프트웨어 엔티티 또는 개체(클래스, 모듈, 함수)는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.
- 즉, 소프트웨어 개체의 행위는 확장될 수 있어야 하지만 변경되어선 안된다.
- 기존 코드에 영향을 주지않고, 소프트웨어에새로운 기능이나 구성요소를 추가할 수 있어야 한다.
- 어떤 기능을 추가하더라도 calculator 함수 코드를 수정하지 않고 기능하도록 만든 함수
// 전달받은 callBack 함수 인자를 통해 기능을 확장할 수 있게 한다.
function calculator(nums, callBack) {
let result = 0
for (const num of nums) {
result = callBack(result, num)
}
return result
}
const add = (a, b) => a + b
const sub = (a, b) => a - b
const mul = (a, b) => a * b
const div = (a, b) => a / b
// 전달받은 함수를 callBack 함수로 전달
calculator([2, 5], add)
- 리스코프 치환 원칙(Liskov substitution principle, LSP)
- 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
- 부모 클래스와 자식 클래스가 있는 경우 서로 바꾸더라도 해당 프로그램에서 잘못된 결과를 도출하지 않는다.
class Shape {
// Rectangle과 Square의 부모 클래스를 정의한다.
getArea() {
// getArea는 빈 메소드로 정의
}
}
class Rectangle extends Shape {
// Rectangle은 Shape를 상속받는다.
constructor(width = 0, height = 0) {
// 직사각형의 생성자
super()
this.width = width
this.height = height
}
getArea() {
// 직사각형의 높이와 너비의 결과값을 조회하는 메소드
return this.width * this.height
}
}
class Square extends Shape {
// Square는 Shape를 상속받는다.
constructor(length = 0) {
// 정사각형의 생성자
super()
this.length = length // 정사각형은 너비와 높이가 같이 깨문에 width와 height 대신 length를 사용한다.
}
getArea() {
// 정사각형의 높이와 너비의 결과값을 조회하는 메소드
return this.length * this.length
}
}
const rectangleArea = new Rectangle(7, 7) // 49
.getArea() // 7 * 7 = 49
const squareArea = new Square(7) // 49
.getArea() // 7 * 7 = 49
- 인터페이스 분리 원칙(Interface segregation principle, ISP)
- 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
- 클라이언트가 필요로 하지 않는 기능을 가진 인터페이스에 의존해서는 안되고, 최대한 인터페이스를 작게 유지해야 한다.
- 의존성 역전 원칙(Dependency Inversion Principle, DIP)
- 추상화에 의존해야 하고 구체화에 의존해선 안된다.
- 높은 계층의 모듈(도메인)이 저수준의 모듈(하부구조)에 의존해서는 안된다.
- 추상화를 하지 않고 고수준 계층의 모듈이 저수준 계층의 모듈을 의존하고 있다면 사소한 코드 변경에도 고수준 계층의 코드를 변경해야 할 것이고 소모되는 개발 코스트 또한 증가할 것이다.
'JavaScript' 카테고리의 다른 글
Closure(클로저) (0) | 2023.07.31 |
---|---|
도메인(Domain) (0) | 2022.12.10 |
객체지향(Object-Oriented) (0) | 2022.12.10 |
JavaScript, 비동기함수 와 await(async/await) (0) | 2022.11.25 |
JavaScript, 콜백함수와 Promise (1) | 2022.11.25 |