Java 언어를 배우면서 항상 들어오던말, "객체지향언어라 유지보수성, 재사용성 좋아~"
하지만 매번 대충 뭐 상속때문인가, 오버라이딩?@$#$@# 그런가보다 생각만했지, 대체 왜? 그런지 생각해보지 않았다. 이번 기회에 제대로 알아보자.
0. 배경

데이터 흐름(flow)에 기반한 절차지향 프로그래밍은
엄격한 순서에 의해 실행되며, 모든 구성요소가 유기적으로 연결 되어 있으므로
문제가 생겼을 시 전체 시스템에 문제가 발생할 수 있어서 디버깅의 어려움이 있었다.
또한, 다양한 데이터 상태에 따라 다르게 동작하는 함수들이 많아지면서
각 함수들로 전달되는 변수들이 적절한 값을 확인하는 코드의 복잡성도 늘어났으며,
문제가 발생했을 때 함수에 영향을 주는 변수를 추적하는 것이 어려웠다.
이런 상황에 점점 더 프로젝트 규모가 커져버리게 되면서
복잡성을 절차지향의 언어로 커버하기에는 힘든 지경이 이르렀다.
즉, 소프트웨어의 유지 관리 비용이 규모에 따라 대폭 증가하는 문제가 발생!!
이러한 문제들을 해결하기 위해 등장한 것이 바로
객체지향 프로그래밍(Object-Oriented Programming)이다.
1. 정의
객체지향이란,
프로그래밍에서 필요한 데이터를 추상화시켜
속성(Attribute)와 행위(behavior)를 가진 객체를 만들고
객체들 간의 유기적인 상호작용을 통해 로직을 구성하는 프로그래밍 방법이다.
여기서 추상화란, 현실세계에서 특정한 대상을 관찰하여
핵심적이고 특징적인 공통점들을 뽑아내는 과정을 말한다.
이렇게 추상화를 통해 속성(Attribute)와 행위(behavior)를 뽑아내고
이것들을 하나로 묶은 것을 개념적(추상적)인 주체라 하며, 이것이 바로 클래스(class)이다.
객체(Object)는 클래스로부터 실체화된 것이다.
앞서 문제점으로 지적된 규모가 큰 프로젝트에서의 복잡성을
작은 문제들을 해결할 수 있는 객체들을 만든 뒤,
이 객체들의 조합으로 큰 문제를 해결하는 방식인 것이다.
2. 그래서 객체지향이 어떤게, 왜 좋은건데?
객체지향 언어에서 가장 중요한 핵심은
"코드 간 의존성을 줄이고, 결합도를 낮추는 것"이다.
앞서, 객체지향은 객체들이 독립적으로 운용되어 유기적인 상호작용을 한다고 했다.
이 독립적이라는 것은 객체 간에 의존성(dependency)이 낮다는 것으로,
의존성이 낮은 객체는 하나의 객체를 수정해도 그 객체를 사용하는 다른 객체를 수정하지 않아도 된다.
이처럼 독립성이 높게 설계된 프로그램이라면, 요구사항의 변경이 필요할때도
절차지향처럼 전체를 대상으로 수정을 할 필요가 없고, 요구사항과 관련한 객체만
즉, 적은 범위의 코드에 대해서만 수정하면 되기 때문에 유지 보수, 재사용성이 용이하다.
이런 특징은 다음을 통해 가능한 것이다.
2-1. 캡슐화(encapsulation)
캡슐화란 객체를 외부에서 접근이 필요한 부분을 제외하고는 내부로 숨길 수 있는 특성이다.
이는 접근제한자를 통해 접근할 수 있는 정보를 제한할 수 있다.
- public : 클래스 외부에서 제한 없이 접근 가능
- private : 클래스 외부에서 접근 불가
- protected : 상속한 하위 클래스에서만 접근 가능
따라서 객체를 외부에서 사용할때는 인터페이스와 같은 public 메소드로 정의한다.
그리고 외부에서 사용되는 인터페이스를 제외하고는 접근제한자(private, protected)를 통해
객체의 기능이나 속성은 접근이 불가능하도록 정보 은닉(inforamtion hiding)이 가능하다.
이렇게 숨겨진 객체 정보는 외부로부터 독립적으로 존재하여,
외부에서 변경되거나 다르게 동작할 위험이 없어 신뢰성을 높일 수 있다.
이렇게 세부 구현을 숨기는 목적은 클래스 내부 구현의 응집도를 높이고,
외부와의 결합도, 의존도를 낮추어 요구사항 변경에 대해 관련한 부분이 최소화되어
결과적으로 유지보수성을 높이는 데 있다.
2-2. 상속(inheritance)
상속은 부모 클래스의 모든 특징들을 상속한 자식 클래스가 물려 받는 것을 말한다.
이때 부모 클래스로부터 특징들을 물려받게 되면 자식 클래스에서는 다시 구현할 필요가 없기때문에
코드의 재사용성(resusability)이 향상되는 것이다.
상속은 단순히 코드를 재사용에만 주안점을 둔다면 잘못된 사용이다.
그렇다면 상속은 어디에 사용하는 것이 좋을까?
바로 다형성을 구현하기 위해서 사용하는 것이 좋다.
2-3. 다형성(polymorphism)
다형성이란,
"여러 형태로 존재한다"
"하나의 속성이나 행위가 상황에 따라 다른 의미로 해석될 수 있는 특징" 이라고 정의한다.
다형성을 실현하기 위해서는 대표적인 방법으로 상속을 이용한 방법이 있다.
상위 클래스 메소드를 하위 클래스에서 재정의(Override)하여 상위 클래스의 참조변수가 어떠한
하위 클래스의 인스턴스를 참조하느냐에 따라 동작이 달라지는 개념으로 이러한 방식으로
구현되는 다형성을 서브타입 다형성(subtype polymorphism)이라고 한다.
이처럼 하위클래스들의 이동(move)이라는 행위에 대해 여러가지 형태가 존재하기 때문에
전통적인 프로그래밍 방식으로는 아래와같이 if 구문이 반복적으로 사용되는 복잡한 코드가 생겼다.
// 전통적인 방식
if(사람) {
} else if(새) {
} else if(호랑이) {
}
하지만 객체지향에서는 상속(Inheritance)과 재정의(Override)를 통해 효과적으로 구현해낼 수 있다.
사람, 새, 호랑이 클래스에서 Animal 클래스를 상속하고 move() 메소드를 재정의 하고있는 것을
전제로 한다면 아래와 같이 Animal 클래스 타입으로 다형성을 구현할 수 있다.
List<Animal> animals = new ArrayList<>();
animals.add(new Human());
animals.add(new Bird());
animals.add(new Tiger());
for(Animal animal : animals) {
animal.move();
}
이렇게 Animal 타입으로 일반화하여 Move() 메소드를 호출하면 Animal 클래스에 정의되어있는
Move()가 실행되는 것이 아니라 각각의 하위 클래스에 구현된 Move() 메소드가 호출된다.
이렇게 비즈니스 로직을 일반화된 코드로 구현해놓으면 여러 가지 장점이 있다.
요구사항이 변경되어도 일반화된 코드는 변경하지 않고, 기존 클래스의 Move() 메소드만
수정하거나 새로운 클래스를 추가 구현하는 것으로 해결할 수 있다.
또한 코드의 가독성이 높아지고 일관성이 생기기 때문에 개발자의 실수를 줄일 수 있게 된다.
3. 정리
객체지향은 독립적인 객체로 추상화하고 객체 간의 유기적인 상호작용으로 복잡한 문제를 해결한다.
이 과정에서 캡슐화를 통해 객체의 독립성을 높이고 객체 간 의존도, 결합도를 낮춘다.
또한 상속과 다형성을 통해서 코드의 재사용성, 유지보수성, 가독성이 높은 프로젝트를 구성할 수 있도록한다.
References
https://devkingdom.tistory.com/95
[OOP] 객체 지향 설계를 해야 하는 이유
내가 컴퓨터 공학을 전공으로 선택한 후, 처음으로 흥미를 느낀 영역은 객체 지향 프로그래밍 (OOP) 이었다. 심지어 휴학을 하고 한 학기 동안 객체지향만 공부한 적도 있었다. (그렇다고 잘하는
devkingdom.tistory.com
https://hyeon9mak.github.io/consider-oop/
객체지향 프로그래밍에 관한 고찰
얼마전 회사에서 조영호님의 객체지향 프로그래밍 강연이 진행되었다. 그간 ‘객체지향이란 무엇인가’, ‘객체지향 프로그래밍을 왜 해야 하는가?’ 홀로 고민이 많았는데, 조영호님의 강연
hyeon9mak.github.io
https://velog.io/@jihogitgit/도대체-객체지향프로그래밍이란-뭘까
도대체 객체지향프로그래밍이란 뭘까? 1부
프로그램의 언어의 역사에 대해 먼저 알아야 하는 것 이지만 우선 출처: https://gracefulprograming.tistory.com/130 [Peter의 우아한 프로그래밍] 이분의 글을 인용해서 설명 하려한다. (나보다 설명이 잘 되
velog.io