ObjectOrientedProgramming
객체란?
세상의 모든 사물을 단순하게 추상화해보면 속성(데이터)과 기능 딱 2가지로 설명할 수 있다.
여기서 추상화란 복잡한 사물을 단순화하는 것을 의미한다.
여러 객체를 묶어서 하나의 객체로 표현한다는 것은 공통된 특징을 간결한 방식으로 표현하는 것을 의미한다.
자동차
속성: 차량 색상, 현재 속도
기능: 엑셀, 브레이크, 문 열기, 문 닫기
동물
속성: 색상, 키, 온도
기능: 먹는다. 걷는다.
게임 케릭터
속성: 레벨, 경험치, 소유한 아이템들
기능: 이동, 공격, 아이템 획득
객체 지향 프로그래밍은 모든 사물을 속성과 기능을 가진 객체로 생각하는 것이다.
객체에는 속성과 기능만 존재한다. 이렇게 단순화하면 세상에 있는 객체들을 컴퓨터 프로그램으로 쉽게 설계할 수 있다. 이런 장점들 덕분에 지금은 객체 지향 프로그래밍이 가장 많이 사용된다. 참고로 실세계와 객체가 항상 1:1로 매칭되는 것은 아니다.
객체 지향의 특징은 속성과 기능을 하나로 묶는 것 뿐만 아니라 캡슐화, 상속, 다형성, 추상화, 메시지 전달 같은 다양한특징들이 있다. 앞으로 이런 특징들을 하나씩 알아가보자.
절차 지향 vs 객체 지향
프로그래밍 방식은 크게 절차 지향 프로그래밍과 객체 지향 프로그래밍으로 나눌 수 있다.
사실 객체 지향도 프로그래밍이며 절차적으로 수행되기 때문에 절차도 중요합니다. 단지 객체 지향은 객체의 설계와 관계까지 중요시 하는 것입니다.
절차 지향 프로그래밍
절차 지향 프로그래밍은 이름 그대로 절차를 지향한다. 쉽게 이야기해서 실행 순서를 중요하게 생각하는 방식이다.
절차 지향 프로그래밍은 프로그램의 흐름을 순차적으로 따르며 처리하는 방식이다. 즉 “어떻게”를 중심으로 프로그래밍 한다.
객체 지향 프로그래밍
객체 지향 프로그래밍은 이름 그대로 객체를 지향한다. 쉽게 이야기해서 객체를 중요하게 생각하는 방식이다.
객체 지향 프로그래밍은 실제 세계의 사물이나 사건을 객체로 보고, 이러한 객체들 간의 상호작용을 중심으로 프로그래밍하는 방식이다. 즉 “무엇을” 중심으로 프로그래밍 한다.
둘의 중요한 차이
절차 지향은 데이터와 해당 데이터에 대한 처리 방식이 분리되어 있다. 반면 객체 지향에서는 데이터와 그 데이터에 대한 행동(메서드)이 하나의 ‘객체’ 안에 함께 포함되어 있다.
💡 우리는 지금까지 클래스와 객체를 사용해서 관련 데이터를 묶어서 사용하는 방법을 학습했다. 그럼 앞서 배운 것 처럼 단순히 객체를 사용하기만 하면 객체 지향 프로그래밍이라 할 수 있을까?
사실 지금까지 우리가 작성한 모든 프로그램은 절차 지향 프로그램이다. 그렇다면 무엇이 객체 지향 프로그래밍일까?
절차지향에서 객체 지향으로 점진적 코드를 변경해보면서 객체 지향 프로그래밍을 이해해보자.
절차 지향 프로그래밍 - 음악 플레이어
public class MusicPlayerMain3 {
public static void main(String[] args) {
MusicPlayerData data = new MusicPlayerData();
//음악 플레이어 켜기
on(data);
//볼륨 증가
volumeUp(data);
//볼륨 증가
volumeUp(data);
//볼륨 감소
volumeDown(data);
//음악 플레이어 상태
showStatus(data);
//음악 플레이어 끄기
off(data);
}
static void on(MusicPlayerData data) {
data.isOn = true;
System.out.println("음악 플레이어를 시작합니다");
}
static void off(MusicPlayerData data) {
data.isOn = false;
System.out.println("음악 플레이어를 종료합니다");
}
static void volumeUp(MusicPlayerData data) {
data.volume++;
System.out.println("음악 플레이어 볼륨:" + data.volume);
}
static void volumeDown(MusicPlayerData data) {
data.volume--;
System.out.println("음악 플레이어 볼륨:" + data.volume);
}
static void showStatus(MusicPlayerData data) {
System.out.println("음악 플레이어 상태 확인");
if (data.isOn) {
System.out.println("음악 플레이어 ON, 볼륨:" + data.volume);
} else {
System.out.println("음악 플레이어 OFF");
}
}
}
각각의 기능을 메서드로 만든 덕분에 각각의 기능이 모듈화 되었다. 덕분에 다음과 같은 장점이 생겼다.
중복 제거: 로직 중복이 제거되었다. 같은 로직이 필요하면 해당 메서드를 여러번 호출하면 된다.
변경 영향 범위: 기능을 수정할 때 해당 메서드 내부만 변경하면 된다.
메서드 이름 추가: 메서드 이름을 통해 코드를 더 쉽게 이해할 수 있다
모듈화: 쉽게 이야기해서 레고 블럭을 생각하면 된다. 필요한 블럭을 가져다 꼽아서 사용할 수 있다. 여기서는 음악 플레이어의 기능이 필요하면 해당 기능을 메서드 호출 만으로 손쉽게 사용할 수 있다. 이제 음악 플레이어와 관련된 메서드를 조립해서 프로그램을 작성할 수 있다.
절차 지향 프로그래밍의 한계
지금까지 클래스를 사용해서 관련된 데이터를 하나로 묶고, 또 메서드를 사용해서 각각의 기능을 모듈화했다. 덕분에 상당히 깔끔하고 읽기 좋고, 유지보수 하기 좋은 코드를 작성할 수 있었다. 하지만 여기서 더 개선할 수 는 없을까?
우리가 작성한 코드의 한계는 바로 데이터와 기능이 분리되어 있다는 점이다. 음악 플레이어의 데이터는 MusicPlayerData 에 있는데, 그 데이터를 사용하는 기능은 MusicPlayerMain3 에 있는 각각의 메서드에 분리되어 있다. 그래서 음악 플레이어와 관련된 데이터는 MusicPlayerData 를 사용해야 하고, 음악 플레이어와 관련된기능은 MusicPlayerMain3 의 각 메서드를 사용해야 한다.
데이터와 그 데이터를 사용하는 기능은 매우 밀접하게 연관되어 있다. 각각의 메서드를 보면 대부분 MusicPlayerData 의 데이터를 사용한다. 따라서 이후에 관련 데이터가 변경되면 MusicPlayerMain3 부분의 메서드들도 함께 변경해야 한다. 그리고 이렇게 데이터와 기능이 분리되어 있으면 유지보수 관점에서도 관리 포인트가 2곳으로 늘어난다.
객체 지향 프로그래밍이 나오기 전까지는 지금과 같이 데이터와 기능이 분리되어 있었다. 따라서 지금과 같은 코드가 최선이었다. 하지만 객체 지향 프로그래밍이 나오면서 데이터와 기능을 온전히 하나로 묶어서 사용할 수 있게 되었다.
데이터와 기능을 하나로 온전히 묶는다는 것이 어떤 의미인지 이해하기 위해 간단한 예제를 만들어보자.
클래스와 메서드

객체 지향 프로그래밍
지금까지 개발한 음악 플레이어는 데이터와 기능이 분리되어 있었다. 이제 데이터와 기능을 하나로 묶어서 음악 플레이어라는 개념을 온전히 하나의 클래스에 담아보자. 프로그램을 작성하는 절차도 중요하지만 지금은 음악 플레이어라는 개념을 객체로 온전히 만드는 것이 더 중요하다. 음악 플레이어라는 객체를 지향해보자!
그러기 위해서는 프로그램의 실행 순서 보다는 음악 플레이어 클래스를 만드는 것 자체에 집중해야 한다. 음악 플레이어가 어떤 속성(데이터)을 가지고 어떤 기능(메서드)을 제공하는지 이 부분에 초점을 맞추어야 한다.
지금부터 우리는 음악 플레이어를 개발하는 개발자가 될 것이다. 이것을 어떻게 사용할지는 분리해서 생각하자. 쉽게 이야기해서 음악 플레이어를 만들어서 제공하는 개발자와 음악 플레이어를 사용하는 개발자가 분리되어 있다고 생각하면 된다.
캡슐화
객체 내부의 데이터와 기능이 함께 모여 있어, 내부 구현을 숨기고 필요한 기능만 외부에 제공함으로써, 코드의 변경이 한 곳에 집중됩니다.
캡슐화에 관현 내용은 접근제어자 참고
메서드 만드는 경우 TIP
결국 기능을 만드는 경우에도 자신의 데이터를 사용하는 경우, 해당 클래스 안에서 해당 기능을 만들어 제공하는 것이 바람직하다. → 또 한 해당 메서드가 내부에서만 사용된다면 private 사용을 권장한다.
Last updated