1. 객체지향 프로그래밍
프로그래밍은 크게 절차지향 프로그래밍과 객체지향 프로그래밍으로 나눌 수 있음.
* 절차지향 프로그래밍 *
- 이름 그대로 절차를 지향. 즉 실행 순서를 중요하게 생각하는 방식.
- 프로그램의 흐름을 순차적으로 따르고 처리하는 방식. => "어떻게"를 중심으로 프로그래밍.
* 객체지향 프로그래밍*
- 이름 그대로 객체를 지향. 즉 객체를 중요하게 생각하는 방식.
- 실제 세계의 사물이나 사건을 객체로 보고, 이러한 객체들 간의 상호작용을 중심으로 프로그래밍하는 방식. => "무엇을" 중심으로 프로그래밍.
* 둘의 차이점 *
- 절차지향은 데이터와 해당 데이터에 대한 처리 방식이 분리.
- 객체지향은 데이터와 그 데이터에 대한 행동(메서드)이 하나의 '객체'안에 함께 포함.
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("음악 플레이어를 종료합니다.");
}
}
[MusicPlayerMain1]
문제 : 음악 플레어를 만들어보기.
요구사항 :
1. 음악 플레어를 켜고 끌 수 있어야 함.
2. 음악 플레이어의 볼륨을 증가, 감소할 수 있어야 함.
3. 음악 플레이어의 상태 확인할 수 있어야 함.
예시 출력 :
음악 플레이어를 시작합니다.
음악 플레이어 볼륨 : 1
음악 플레이어 볼륨 : 2
음악 플레이어 상태 확인
음악 플레이어 ON, 볼륨 : 1
음악 플레이어를 종료합니다.
순서대로 프로그램이 작동하도록 단순하게 작성한 프로그램. 다음에는 이 코드를 점진적으로 변경해 봄.
package oop1;
public class MusicPlayerData {
int volume = 0;
boolean isOn = false;
}
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, MusicPlayerMain2]
MusicPlayerData라는 클래스 생성 후, 음악 플레이어에 사용되는 데이터들을 여기에 묶어서 멤버 변수로 사용.
- 음악 플레이어와 관련된 데이터는 MusicPlayerData 클래스에 존재.
- 프로그램 로직이 복잡해져서 다양한 변수들이 추가되어도 음악 플레이어와 관련된 변수들은 MusicPlayerData data 객체에 속해있으므로 쉽게 구분 가능.
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");
}
}
}
[MusicPlayerMain3]
코드를 보면 중복되는 부분들이 있으므로, 각각의 기능들이 이후에 재사용될 가능성이 있음. 따라서 메서드를 사용해서 각각의 기능 구분.
각각의 기능을 메서드로 만든 덕분에 각각의 기능이 모듈화 됨. 덕분에 다음과 같은 장점이 생김.
1. 중복 제거 : 로직 중복 제거. 같은 로직이 필요하면 해당 메서드를 여러 번 호출하면 됨.
2. 변경 영향 범위 : 기능 수정할 때 해당 메서드 내부만 변경하면 됨.
3. 메서드 이름 추가 : 메서드 이름을 통해 코드를 더 쉽게 이해할 수 있음.
* 절차지향 프로그래밍의 한계 *
클래스를 사용해서 관련된 데이터를 하나로 묶고, 또 메서드를 사용해서 각각의 기능을 모듈화 함. 덕분에 상당히 깔끔하고 읽기 좋고, 유지보수 하기 좋은 코드로 작성할 수 있었음.
하지만 여기서 더 개선할 수 없을까라는 의문이 생김.
우리가 지금까지 작성했을 때 데이터와 데이터를 사용하는 기능은 각각의 메서드에 분리되어 있음.
그래서 음악 플레이어와 관련된 데이터는 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, ValueDataMain]
ValueData라는 인스턴스 생성 후 외부에서 ValueData.value에 접근해 숫자를 하나씩 증가하는 단순한 코드.
코드를 보면 데이터인 value와 value의 값을 증가시키는 기능인 add() 메서드가 서로 분리되어 있음.
자바 같은 객체지향 언어는 클래스 내부에 속성(데이터)과 기능(메서드)을 함께 포함할 수 있음.
클래스 내부에 멤버변수뿐만 아니라 메서드도 함께 포함할 수 있다는 뜻.
따라서 아래처럼 바꿀 수 있음.
package oop1;
public class ValueObject {
int value;
// static 키워드 사용하지 않음. (자세한 내용은 추후에)
void add() {
value++;
System.out.println("숫자 증가 value = " + value);
}
}
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, ValueObjectMain]
valueObject라는 객체 생성. 이 객체는 멤버 변수뿐만 아니라 내부에 기능 수행하는 add() 메서드도 함께 존재.
인스턴스의 메서드 호출하는 방법은 멤버 변수 사용하는 방법처럼 .(dot)을 찍어서 객체 접근 후 원하는 메서드 호출하면 됨.
정리
- 클래스는 속성(데이터, 멤버변수)과 기능(메서드)을 정의할 수 있음.
- 객체는 자신의 메서드를 통해 자신의 멤버변수에 접근할 수 있음.
(객체의 메서드 내부에서 접근하는 멤버변수는 개체 자신의 멤버변수임.)
*객체지향 프로그래밍 *
지금까지 개발한 음악 플레이어는 데이터와 기능이 분리되어 있음.
이제 데이터와 기능을 하나로 묶어서 음악 플레이어라는 개념을 온전히 하나의 클래스에 담아보기.
프로그램을 작성하는 절차도 중요하지만 지금은 음악 플레이어라는 개념을 객체로 온전히 만드는 것이 더 중요.
프로그램 실행 순서보다는 음악 플레이어 클래스를 만드는 것 자체에 집중해야 함. 음악 플레이어가 어떤 속성(데이터)을 가지고 어떤 기능(메서드)을 제공하는지 이 부분에 초점을 맞춰야 함.
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 musicPlayer = new MusicPlayer();
// 음악 플레이어 켜기
musicPlayer.on();
// 볼륨 증가
musicPlayer.volumeUp();
// 볼륨 증가
musicPlayer.volumeUp();
// 볼륨 감소
musicPlayer.volumeDown();
// 음악 플레이어 상태
musicPlayer.showStatus();
// 음악 플레이어 끄기
musicPlayer.off();
}
}
[MusicPlayer, MusicPlayerMain4]
음악플레이어
- 속성 : volume, isOn
- 기능 : on(), off(), volumeUp(), volumeDown(), showStatus()
*캡슐화 *
- MusicPlayer를 보면 음악 플레이어를 구성하기 위한 속성과 기능이 마치 하나의 캡슐에 쌓여있는 것 같음. 이렇게 속성과 기능을 하나로 묶어서 필요한 기능을 메서드를 통해 외부에 제공하는 것.
위에 프로그램을 짜면서 객체지향 프로그래밍 덕분에 음악 플레이어 객체를 사용하는 입장에서 진짜 음악 플레이어를 만들고 사용하는 것처럼 친숙하게 느껴짐. 그래서 코드가 더 읽기 쉬운 것은 물론이고, 속성과 기능이 한 곳에 있기 때문에 변경도 더 쉬워짐.
1-1. 객체지향 프로그래밍 문제
[Rectangle, RectangleOopMain]
절차 지향 직사각형 프로그램을 객체 지향으로 변경.
- Rectangle 클래서 생성.
- RectangleOopMain 에 해당 클래스를 사용하는 main() 코드 생성.
// 절차지향 코드
package oop.ex;
public class RectangleProceduralMain {
public static void main(String[] args) {
int width = 5;
int height = 8;
int area = calculateArea(width, height);
System.out.println("넓이 : " + area);
int perimeter = caculatePerimeter(width, height);
System.out.println("둘레 길이 : " + perimeter);
boolean square = isSquare(width, height);
System.out.println("정사각형 여부 : " + square);
}
static int calculateArea(int width, int height) {
return width * height;
}
static int calculatePerimeter(int width, int height) {
return 2 * (width + height);
}
static boolean isSquare(int width, int height) {
return width == height;
}
}
package oop1.ex;
public class Rectangle {
int width;
int height;
int calculateArea() {
return width * height;
}
int calculatePerimeter() {
return 2 * (width + height);
}
boolean isSquare() {
return this.width == this.height;
}
}
package oop1.ex;
public class RectangleOopMain {
public static void main(String[] args) {
Rectangle rectangle = new Rectangle();
rectangle.width = 5;
rectangle.height = 8;
int area = rectangle.calculateArea();
System.out.println("넓이 : " + area);
int perimeter = rectangle.calculatePerimeter();
System.out.println("둘레 길이 : " + perimeter);
boolean square = rectangle.isSquare();
System.out.println("정사각형 여부 : " + square);
}
}
// 실행결과
넓이 : 40
둘레 길이 : 26
정사각형 여부 : false
Rectangle 클래스에서 매개변수는 필요 없음. 자기 자신 것을 사용하면 됨. (객체지향이기 때문)
[Account, AccountMain]
은행 계좌를 객체로 설계.
Account 클래스 생성
- int balance 잔액
- deposit(int amount) : 입금 메서드
- 입금 시 잔액 증가.
- withdraw(int amount) : 출금 메서드
- 출금 시 잔액 감소.
- 만약 잔액 부족 시 '잔액 부족'을 출력.
AccountMain 클래스 생성하고 main() 메서드 통해 프로그램 시작.
- 계좌에 10000원을 입금해라.
- 계좌에서 9000원을 출금해라.
- 계좌에서 2000원을 출금 시도해라. -> 잔액 부족 출력을 확인.
- 잔고를 출력해라. 잔고 : 1000
package oop1.ex;
public class Account {
int balance;
void deposit(int amount) {
balance += amount;
}
void withdraw(int amount) {
if(balance >= amount) {
balance -= amount;
} else {
System.out.println("잔액 부족");
}
}
}
package oop1.ex;
public class AccountMain {
public static void main(String[] args) {
Account account = new Account();
account.deposit(10000);
account.withdraw(9000);
account.withdraw(2000);
System.out.println("잔고 : " + account.balance);
}
}
// 실행결과
잔액 부족
잔고 : 1000
2. 정리
절차지향 프로그래밍
- 절차지향 프로그래밍은 이름 그대로 절차를 지향.
- 쉽게 이야기해서 실행 순서를 중요하게 생각하는 방식.
- 프로그램의 흐름을 순서적으로 따르며 처리하기 때문에 "어떻게"를 중심으로 프로그래밍.
객체지향 프로그래밍
- 객체지향 프로그래밍은 이름 그대로 객체를 지향.
- 쉽게 이야기해서 객체를 중요게 생각하는 방식.
- 실제 세계의 사물이나 사건을 객체로 보고, 이러한 객체들 간의 상호작용을 중심으로 프로그래밍하는 방식이기 때문에 "무엇을" 중심으로 프로그래밍.
둘의 중요한 차이
- 절차 지향은 데이터와 해당 데이터에 대한 처리 방식이 분리되어 있음.
- 반면 객체 지향에서는 데이터와 그 데이터에 대한 행동(메서드)이 하나의 '객체'안에 함께 포함.
객체란
- 세상의 모든 사물을 단순하게 추상화해 보면 속성(데이터)과 기능 딱 2가지로 설명 가능.
객체지향 프로그래밍은 모든 사물을 속성과 기능을 가진 객체로 생각하는 것.
객체에는 속성과 기능만 존재함. 이렇게 단순화하면 세상에 있는 객체들을 컴퓨터 프로그램으로 쉽게 설계 가능.
이런 장점들 덕분에 지금은 객체지향 프로그래밍이 가장 많이 사용됨.
참고로 객체와 실세계가 항상 1:1로 매칭되는 것은 아님.
객체 지향의 특징은 속성과 기능을 하나로 묶는 것뿐만 아니라 캡슐성, 상속, 다형성, 추상화, 메시지 전달 같은 다양한 특징들이 있음.
앞으로 하나씩 알아가 볼 예정.
'Studying > JAVA' 카테고리의 다른 글
[JAVA]_Package(패키지) (0) | 2025.01.31 |
---|---|
[JAVA]_Constructor(생성자) (0) | 2025.01.24 |
[JAVA]_Primitive and Reference(기본형과 참조형) (0) | 2024.12.24 |
[JAVA]_Class and Data (클래스와 데이터) (3) | 2024.12.23 |
[JAVA]_Method(메서드) (3) | 2024.12.14 |