1. 개요
객체 지향 프로그래밍은 OOP(Object-Oriented Programming)라고도 하며,
프로그램을 설계하는 방법 이론 중 하나로 명령형 프로그래밍 패러다임에 속한다.
명령형 프로그래밍이란?
프로그래밍 패러다임 중 하나로 여러 작업들이 순차적으로 처리되는
과정과 방식을 결정하여 프로그램을 설계하는 관점이다.
한 예시로 명령형 프로그래밍에 속하는 순차적 프로그래밍은
명령어를 순차적으로 처리하는 방식으로 프로그램을 설계한다.
객체 지향 프로그래밍 이전에는 절차적 프로그래밍 방식이 지배적이었는데,
절차적 프로그래밍은 프로시저(루틴, 서브루틴, 함수, 메서드)를 순차적으로 호출하여 프로그램을 설계했다.
절차적 프로그래밍은 처리 과정을 하나의 묶음으로 구조화할 수 있어서 모듈화가 쉽고 속도가 빨랐지만
프로그램이 복잡해질 수록 코드를 이해하고 흐름을 파악하기 어려워져 유지, 보수에 어려움이 있었다.
이런 문제를 해결하고자 객체 지향 프로그래밍이 등장하게 됐다.
객체 지향 프로그래밍은 기존에 프로그램을 단순히 데이터와 처리 과정으로 나누었던 것을 넘어서
객체(Object)라는 개념적인 단위로 프로그램을 나누고, 객체들의 상호작용으로 프로그램을 만드는 방법이다.
객체 지향 프로그램이 복잡해질 수록 협업 환경에서의 의사소통이 중요해졌고
프로그래밍 형식에 대한 약속인 디자인 패턴이 자연스럽게 등장하게 됐다.
2. 요소
객체 지향 프로그래밍은 그것을 이루는 3가지 요소로 인해 프로그램의 유지, 보수가 용이하다는 장점이 있다.
1. 캡슐화
변수와 함수를 하나의 단위로 묶는 것으로, 클래스를 통해 구현된다.
클래스의 인스턴스를 생성해서 멤버에 접근할 수 있다.
또한 프로그램의 세부 구현을 숨기는 정보 은닉을 통해 내부적으로 모듈의 응집도를 높이고
외부로 노출을 최소화하여 모듈 간 결합도를 낮춰 모듈을 유연하게 사용할 수 있다.
정보 은닉은 접근 제어자로 구현할 수 있다.
2. 상속
부모 클래스의 기능과 역할을 자식 클래스가 물려받는 것.
부모 클래스의 기능을 일부 변경 또는 추가해야할 때 상속을 통해 자식 클래스를 만들어서
코드의 재사용성을 높일 수 있다.
3. 다형성
상황에 따라 변수 또는 함수가 다르게 해석될 수 있음을 의미한다.
부모 클래스를 상속한 자식 클래스는 상황에 따라 메서드 오버라이딩을 통해 부모 클래스의 메서드를
자식 클래스에서 재정의하여 이름은 같지만 동작이 다른 메서드를 만들 수 있다.
메서드 오버로딩과 제네릭을 통해서도 구현할 수 있으며,
새로운 메서드를 만들 필요가 없어서 중복 코드를 최소화할 수 있다.
3. 장단점
앞서 얘기한 특징으로 인해 객체 지향 프로그래밍은 중복 코드를 최소화하고 코드의 재사용성을 높여서
프로그램의 유지, 보수에 용이하다는 장점이 있다.
하지만 프로그램이 복잡해질 수록 상속이라는 특징 때문에 클래스 간의 관계를 설정하기 어려워지고
프로그래밍의 난이도가 높아진다는 단점이 있다.
또한 캡슐화로 변수와 함수를 묶으면 멤버에 접근하기 위한 포인터 연산이 필요하고,
다형성으로 인해 상속 관계에서 호출할 메서드를 찾는 메서드 디스패치가 발생하여 성능 하락이 발생한다.
4. 설계 5원칙 (SOLID)
객체 지향 프로그래밍을 위해 준수해야하는 5가지 원칙을 의미한다.
시간이 지나도 유지, 보수 및 기능 확장에 용이한 프로그램을 설계하는데 도움이 된다.
1. 단일 책임 원칙 (Single Responsibility Principle)
모듈은 하나의 기능에 대한 책임만을 가져야 한다는 뜻으로 모듈의 변경 원인이 유일해야 한다는 의미이다.
반대로 모듈이 여러 기능에 책임을 가진다면 각기 다른 기능에 수정이 발생할 때
해당 모듈이 변경되어 모듈의 용도가 불명확해질 수 있다.
따라서 단일 책임 원칙을 지키면 모듈의 용도가 명확해지고 기능에 수정이 발생할 때
그 기능에 책임이 있는 모듈을 바로 파악할 수 있다.
2. 개방 폐쇄 원칙 (Open-Closed Principle)
프로그램의 기능 추가와 확장에는 열려있고(개방적이고) 수정에는 닫혀있다(폐쇠적이다)는 뜻으로
기존의 코드를 수정하지 않고 기능을 변경 및 확장할 수 있어야 한다는 의미이다.
만약 특정 기능을 하는 클래스가 다른 기능으로 변경된다면, 기존 기능을 사용하던 모든 클래스를
수정하거나 새로운 기능을 만들기 위해 기존 클래스를 변경해야하는 소요가 발생한다.
그런데 프로토콜(인터페이스)을 사용하여 특정 기능에 대한 추상체를 만들어 참조하도록 하면
기존 코드를 수정하지 않고 단지 프로토콜의 구현체(클래스)를 새로 만들어서 기능을 추가 및 변경할 수 있다.
추상화란?
추상체를 만드는 작업으로 객체의 가장 특징적인 것들을 파악하고 불필요한 것을 덜어내는 과정
변경되는 세부 구현은 숨기고 변경되지 않는 인터페이스만 노출시킬 수 있다.
3. 리스코브 치환 원칙 (Liskov Substitution Principle)
부모 클래스 대신 자식 클래스를 사용했을 때 문제없이 부모 클래스처럼 동작해야한다는 것을 의미하며,
올바른 상속 관계를 설정하기 위한 원칙이다.
부모 클래스가 사용되는 곳에 자식 클래스도 사용될 수 있음을 유의하여
의도치 않은 오류가 발생하지 않도록 코드를 작성해야 한다.
4. 인터페이스 분리 원칙 (Interface Segregation Principle)
클라이언트의 목적과 용도에 따라 모듈을 적절한 인터페이스로 분리해야 한다는 뜻으로
각 클라이언트는 자신의 목적과 용도에 맞는 인터페이스만을 제공받아야 한다는 의미이다.
이 원칙을 준수하면 모듈에 대한 클라이언트들의 불필요한 접근과 간섭을 막고 오류를 방지할 수 있다.
5. 의존성 역전 원칙 (Dependency Inversion Principle)
고수준 모듈(추상체)은 저수준 모듈(구현체)에 의존하면 안되고
저수준 모듈(구현체)가 고수준 모듈(추상체)에 의존해야 한다는 뜻으로,
객체들 간의 올바른 의존 관계를 설정하기 위한 원칙이다.
만약 구현체가 추상체에 의존하지 않으면 기능이 변경될 때 구현체 또는 구현체를 의존하는
모든 클래스의 코드를 수정해야 하지만
추상체에 의존할 경우 수정 소요가 발생하지 않는 유연한 확장이 가능해진다.
여기서 알 수 있듯이 의존성 역전 원칙은 개방 폐쇄 원칙과 밀접한 관련이 있다.
"의존성 역전"은 컴파일 또는 런타임 시점에 의존성이 역전(추상체 -> 구현체) 되기 때문에 붙혀진 이름이다.
참고 문서
'CS' 카테고리의 다른 글
아키텍처 패턴에 대하여 (MVC, MVP, MVVM) (0) | 2023.02.10 |
---|---|
함수형 프로그래밍에 대하여 (1) | 2023.02.06 |
Git과 Github에 대하여 (feat. Merge VS Rebase) (0) | 2023.02.04 |
애자일(Agile)에 대하여 (0) | 2023.01.27 |
RESTful API에 대하여 (0) | 2023.01.24 |