최근에 학습한 것들이 과연 객체 지향 프로그래밍적으로 학습을 했을까? 단순히 자바로 객체를 만들어서 객체를 사용한 것이 객체 지향 프로그래밍일까? 지금까지 학습한 것은 절차 지향 프로그래밍이다. 객체를 만들어서 사용하는 것이 객체 지향 프로그래밍이 아니라는 것이다. 최근에 학습한 것은 좋은 절차 지향 프로그래밍이였다.
세상 모든 사물을 추상화해보면 속성과 기능 2가지로 설명할 수 있다.
절차 지향 프로그래밍은 데이터와 데이터 처리에 대한 방식이 분리되어 있다. 객체 지향 프로그래밍은 데이터와 그 데이터에 대한 행동(메서드)이 하나인 객체 안에 포함되어 있다.
package oop1;
public class MusicPlayerMain1 {
public static void main(String[] args) {
int volume = 0;
boolean isOn = false;
//음악 플레이어 켜기
isOn = true;
System.out.println("음악 플레이어를 시작합니다");
//볼륨 증가
volume++;
System.out.println("음악 플레이어 볼륨:" + volume);
//볼륨 증가
volume++;
System.out.println("음악 플레이어 볼륨:" + volume);
//볼륨 감소
volume--;
System.out.println("음악 플레이어 볼륨:" + volume);
//음악 플레이어 상태
System.out.println("음악 플레이어 상태 확인");
if (isOn) {
System.out.println("음악 플레이어 ON, 볼륨:" + volume);
} else {
System.out.println("음악 플레이어 OFF");
}
//음악 플레이어 끄기
isOn = false;
System.out.println("음악 플레이어를 종료합니다");
}
}
실행 결과
음악 플레이어를 시작합니다
음악 플레이어 볼륨:1
음악 플레이어 볼륨:2
음악 플레이어 볼륨:1
음악 플레이어 상태 확인
음악 플레이어 ON, 볼륨:1
음악 플레이어를 종료합니다
코드를 보면 절차적으로 실행 순서대로 프로그램이 작동했다.
package oop1;
public class MusicPlayerData {
int volume;
boolean isOn;
}
package oop1;
public class MusicPlayerMain2 {
public static void main(String[] args) {
MusicPlayerData data = new MusicPlayerData();
//음악 플레이어 켜기
data.isOn = true;
System.out.println("음악 플레이어를 시작합니다");
//볼륨 증가
data.volume++;
System.out.println("음악 플레이어 볼륨:" + data.volume);
//볼륨 증가
data.volume++;
System.out.println("음악 플레이어 볼륨:" + data.volume);
//볼륨 감소
data.volume--;
System.out.println("음악 플레이어 볼륨:" + data.volume);
//음악 플레이어 상태
System.out.println("음악 플레이어 상태 확인");
if (data.isOn) {
System.out.println("음악 플레이어 ON, 볼륨:" + data.volume);
} else {
System.out.println("음악 플레이어 OFF");
}
//음악 플레이어 끄기
data.isOn = false;
System.out.println("음악 플레이어를 종료합니다");
}
}
MusicPlayerData 클래스를 생성해서 기존 로직을 변경했다. 이제는 기존 변수가 변경되거나 다양한 변수가 생겨도 객체를 통해 쉽게 가져다가 사용할 수 있다.
위에 코드를 보면 볼륨 증가하는 부분이 중복이 되어 있다. 그리고 위에 코드들의 기능들은 재사용할 가능성이 높다
package oop1;
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 메인 클래스에서도 추가 또는 수정이 이루어져야 하므로 관리 포인트가 2곳으로 늘어나게 된다.
이러한 문제점들을 보완하기 위해서 객체 지향 프로그래밍으로 데이터와 해당 데이터들로 기능을 객체 클래스 안에 포함시켜서 인스턴스를 생성하면 해당 인스턴스로 가져다가 사용만 하면 된다. 이러한 예시 코드를 작성 해보려고 한다.
클래스 안에 메서드를 생성할 수 있지만 먼저 비교를 위해 멤버변수만 있는 클래스를 생성한다.
package oop1;
public class ValueData {
int value;
}
package oop1;
public class ValueDataMain {
public static void main(String[] args) {
ValueData valueData = new ValueData();
add(valueData);
add(valueData);
add(valueData);
System.out.println("최종 숫자 = " + valueData.value);
}
static void add (ValueData valueData) {
valueData.value++;
System.out.println("숫자 증가 value = " + valueData.value);
}
}
실행 결과
숫자 증가 value = 1
숫자 증가 value = 2
숫자 증가 value = 3
최종 숫자 = 3
ValueData 인스턴스를 생성해서 ValueData.value에 접근해서 add 메서드로 값을 증가시키는 코드이다. 그런데 add 메서드는 ValueData 클래스와 분리되어 있는데 포함시키는 코드를 바로 작성 정의해보자.
package oop1;
public class ValueObject {
int value;
void add() {
value++;
System.out.println("숫자 증가 value = " + value);
}
}
ValueObject 클래스에서 멤버변수 value
와 값 증가 기능인 add
메서드를 정의했다. 여기서는 add 메서드에 static을 선언하지 않았다. 간략하게 얘기하자면 static은 객체를 생성하지 않고 메서드를 호출할 수 있다는 점 때문에 메인 클래스에서 사용한 것이다.
package oop1;
public class ValueObjectMain {
public static void main(String[] args) {
ValueObject valueObject = new ValueObject();
valueObject.add();
valueObject.add();
valueObject.add();
System.out.println("최종 숫자 = " + valueObject.value);
}
}
실행 결과
숫자 증가 value = 1
숫자 증가 value = 2
숫자 증가 value = 3
최종 숫자 = 3
ValueObject 인스턴스를 생성해서 valueObject.add 메서드에도 접근을 할 수 있게 되었다.
valueObject.add(); //1
x002.add(); //2: x002 ValueObject 인스턴스에 있는 add() 메서드를 호출한다.
add
메서드 호출 후 value
변수도 호출을 하는데 기본으로 본인 인스턴스에 있는 멤버 변수에 접근한다. 그래서 x002에 접근해서 x002.value에 접근한다.
++ 연산으로 값을 증가시킨다.
위에서 음악플레이어 프로그램을 절차 지향적으로 정의한 것을 객체 지향적으로 변경해서 정의했다.
package oop1;
public class MusicPlayer {
int volume = 0;
boolean isOn = false;
void on() {
isOn = true;
System.out.println("음악 플레이어를 시작합니다.");
}
void off() {
isOn = false;
System.out.println("음악 플레이어를 종료합니다");
}
void volumeUp() {
volume++;
System.out.println("음악 플레이어 볼륨 : " + volume);
}
void volumeDown() {
volume--;
System.out.println("음악 플레이어 볼륨 : " + volume);
}
void showStatus() {
System.out.println("음악 플레이어 상태 확인");
if(isOn) {
System.out.println("음악 플레이어 ON, 볼륨 : " + volume);
} else {
System.out.println("음악 플레이어 OFF");
}
}
}
package oop1;
public class MusicPlayerMain4 {
public static void main(String[] args) {
MusicPlayer player = new MusicPlayer();
//음악 플레이어 켜기
player.on();
//볼륨 증가
player.volumeUp();
//볼륨 증가
player.volumeUp();
//볼륨 감소
player.volumeDown();
//음악 플레이어 상태
player.showStatus();
//음악 플레이어 끄기
player.off();
}
}
실행 결과
음악 플레이어를 시작합니다.
음악 플레이어 볼륨 : 1
음악 플레이어 볼륨 : 2
음악 플레이어 볼륨 : 1
음악 플레이어 상태 확인
음악 플레이어 ON, 볼륨 : 1
음악 플레이어를 종료합니다
위 예시를 보면 MusicPlayer 클래스를 통해 필요한 기능을 호출해서 사용하기만 하면 된다. 그리고 메인 클래스의 코드가 읽기 편해졌다. MusicPlayer 클래스를 가져다 사용하는 개발자들도 멤버 변수를 사용하지 않고 필요한 메서드만 가져다가 사용하면 된다. 그리고 사용하는 개발자들은 MusicPlayer 클래스 안에 멤버 변수의 이름이 변경이 되어도 같이 수정할 필요 없다. 메서드의 이름이 바뀐다면 메서드 이름은 같이 변경을 해줘야 한다.
객체를 구성하기 위해 속성(데이터 또는 멤버 변수)과 기능을 하나의 캡슐처럼 쌓여있는 것 같다. 이렇게 속성과 기능을 하나로 묶어서 필요할 때마다 외부에서 사용하는 것을 캡슐화라고 한다.
참고