객체 지향 프로그래밍이란?
실세계의 사물 또는 개념들을 객체
로 바라보고, 상태와 행위를 가진 객체를 만들어 그 객체들 간 상호작용을 통해 프로그램을 만들어 나가는 방식이다.
유지보수가 어려웠던 절차지향, 구조적 프로그래밍을 해결하기 위해 등장했으며 객체를 독립성과 신뢰성이 보장되게 만들어 놓으면 재사용성이 높아지므로 유지보수가 용이하다.
객체지향 프로그래밍을 알려면, 먼저 왜 객체지향 프로그래밍이 등장하게 되었는지를 살펴보자.
객체지향 이전의 프로그래밍에는 절차지향 프로그래밍
과 구조적 프로그래밍
이 있었다.
절차지향 프로그래밍이란?
코드를 위에서부터 순차적으로 쭈욱 내려오면서 실행되는 방식.
하지만, 기존 코드를 재사용하는게 비효율적이고 유지보수가 어렵다.
구조적 프로그래밍이란?
프로그램을 작은 함수 단위로 나누고 함수끼리 호출을 하는 방식.
하지만, 한 문서 내에 메소드의 수가 많아질 경우 추후 유지 보수가 어렵다.
때문에 이를 해결하기 위해, 큰 문제를 쪼개는 것이 아니라 먼저 작은 문제들을 해결 할 수 있는 객체들을 만들고, 이 객체들을 조합해서 큰 문제를 해결하는 상향식(Bottom-up) 해결법의 객체지향 프로그래밍
이 등장하게 되었다. 객체를 독립성과 신뢰성이 보장되게 만들어 놓으면 재사용성도 높아지므로 개발기간과 비용 또한 줄어들게 되었다.
현실의 무언가(사물 또는 개념)를 추상적으로 표현한 것이다.
객체 내부에 자료형(필드)와 함수(메소드)가 같이 존재하여 물리적, 논리적 요소를 한번에 묶어서 저장하는 것이 가능하다.
(= 같은 종류의 데이터와 로직이 함께 있는 구성체라고 볼 수 있다.)
(= 클래스에 정의된 내용대로 메모리에 생성 된 것이다.)
각각의 객체는 메시지를 주고받고, 데이터를 처리하고 협력 할 수 있다.
- 추상화
처음부터 객체의 구현체를 구현하는 것이 아니라 객체의 역할과 구현을 분리할때 역할(인터페이스)에 대한 설계이다.- 캡슐화
객체와 관련된 기능,데이터 등을 감싸주는 것이다. 만약 감싸주지 않는다면 객체에 대한 무분별한 접근이 가능하게 되므로 안전에 취약하고 모듈이라 보기가 어렵다.
java의 경우 접근제어자를 통해 객체의 캡슐화를 구현한다.- 상속
기존 클래스를 재사용하여 새로운 클래스를 작성하는 것으로, 상위 클래스의 기능을 하위 클래스가 사용할 수 있다. 이를 통해 코드의 중복을 제거하여 코드의 재사용성을 높일 수 있다.
객체의 역할에 대한 구현체를 설계하게 된다.- 다형성 ⭐️
여러가지 형태를 지닐 수 있게 해주는 것이다.
역할
과구현
으로 구분지어 보았을 때,
역할(인터페이스)과 구현(구현 객체) 으로 구분하게 되면 변경도 유연하고 편리할 수 있다.
(ex. 역할-배우, 자동차-모델(가스,전기), 할인정책 로직 등)
클라이언트(관객,운전자,회원 등)은 내부 구조를 몰라도, 대상의 역할(인터페이스)만 알면 된다.
구현 대상의 내부 구조가 변경되거나, 구현 대상 자체가 변경되어도 클라이언트는 영향을 받지 않는다.
자바 언어를 생각해보면 오버라이딩을 통해 인터페이스를 구현한 객체 인스턴스를 실행 시점에 유연하게 변경 할 수 있다. 결론적으로, 클라이언트가 변경되지 않아도, 서버의 구현 기능을 유연하게 변경할 수 있다는 점이다.
객체 지향 프로그래밍은 프로그램을 유연하고 변경이 용이하게 만들기 때문에 대규모 소프트웨어 개발에서 많이 사용된다.
(확장이 가능하고 유지보수에 용이하다, 컴포넌트를 쉽고 유연하게 변경하며 개발할 수 있다.)
어떻게 유지 보수가 용이한가? 에 대해서는 자바 언어의 다형성을 생각해 볼 수 있다.
객체 설계시 역할과 구현을 분리하여 설계한다. 역할(인터페이스)를 먼저 부여하고 그 역할을 수행하는 구현 객체를 만들면 구현 객체에 대하여 변경이 용이해질 수 있다. (핵심은 구현보다 역할이 더 중요하다는 것이다)
- 단일 책임 원칙 (SRP)
하나의 클래스는 하나의 책임만 가져야 한다.
변경이 있을 때 파급 효과가 적도록 책임을 분배하여 개발한다.- 개방-폐쇄 원칙 (OCP) ⭐️
확장에는 개방되어있고, 변경에는 폐쇄적이어야한다.
확장을 하려면 기존 코드를 변경하는 것이 아니라 다형성을 활용하여 인터페이스의 새로운 구현체를 통해 기능을 구현한다.
이때, 객체를 생성하고 연관관계를 맺어주는 역할을 spring 컨테이너가 해결해주는데 이 원칙을 지키기 위해 IoC, DI 가 필요하게 된다.- 리스코프 치환 원칙 (LSP)
다형성에서 구현 객체는 인터페이스 규약을 다 지켜야한다. (기본 기능 보장)- 인터페이스 분리 원칙 (ISP)
범용 인터페이스 하나로 해결하기 보단, 여러 개의 인터페이스로 분리하는 것이 인터페이스가 명확해지고 대체 가능성이 높아진다.- 의존관계 역전 원칙 (DIP) ⭐️
구현 클래스(구체화)에 의존하지 말고 인터페이스(추상화:역할)에 의존해야한다.
철저하게 역할과 구현을 분리하고, 인터페이스에 의존해야 유연하게 구현체를 변경할 수 있다.
~~클라이언트가 구현 클래스를 직접 선택하며 DIP를 위반하지 않도록 ~~
결론적으로, 내가 생각하는 좋은 객체 지향 프로그래밍이란?
저의 견해로는 다형성에 맞게 역할과 구현을 잘 구분하여 클라이언트와 상관없이도 서버의 구현 기능을 마음껏 유연하게 변경, 확장 할 수 있도록 개발하는 것이 좋은 객체 지향 프로그래밍이라고 생각한다.