객체 지향적으로 생각한다는 것
객체 지향적으로 생각한다는 것은 현재 가진 문제에 연관된 여러 요소를 확인하려 노력하는 것이다.
즉, 처음 생각할 것은 관련된 객체가 무엇이 있는지. 그리고 객체에 관련된 요소들을 확인한 다음에 내가 사용하고자 하는 것이 무엇인지 확인한다.
예시1) 비행
- 비행기
- 항공사, 제조사, 타입, 포지션 등 ← 데이터
- 이륙하다, 착륙하다 등 ← 행동
- 승무원
- 이름, 주소, 소속 등 ← 데이터
- 탑승수속하다, 안내하다 등 ← 행동
- 탑승객
- 이름, 주소, 국적 등 ← 데이터
- 체크인하다, 걷다, 구매하다 등 ← 행동
예시2) 온라인 쇼핑 시스템 클래스
- 고객
- 이름, 주소 등
- 로그인, 로그아웃, 물품선택, 계산 등
- 장바구니
- 상품 등
- 상품추가, 상품삭제 등
- 상품
- 이름, 가격, 재고 등
- 주문, 금액 변경 등
구조적 프로그래밍은 절차나 메서드의 관점으로 생각하는 것이 전부다.
그러나 객체지향 프로그래밍에서는 객체에 내포된 데이터는 무엇이며, 데이터의 상태는 어떠하며, 객체들에 행할 수 있는 행동Action이나 명령Operation이 무엇인지 생각하는 것이다.
class Planet { // '행성' 클래스
name, location, distanceFromSun //data/state/fields 멤버변수(멤버데이터) 객체가 내포할 수 있는 데이터
revolve(), rotate() //actions/behavior/methods 메서드(특정 객체에 행할 수 있는 동작이자 클래스의 동작)
}
Planet earth = new Planet();
Planet venus = new Planet();
//행성 클래스의 두 인스턴스, 지구와 금성. 둘은 각각 다른 값을 가질 수 있다.
즉, 객체지향 프로그래밍은 객체가 무엇이고, 가진 데이터는 무엇이며 객체에 수행할 수 있는 행위가 뭐가 있는지 생각하는 것이다.
인스턴스의 상태와 동작
인스턴스들은 객체이며, 객체에는 상태와 동작이 있다.
아래와 같이 MotorBike 클래스와 MotoBikeRunner 클래스를 각각 만들어주었다. 아래의 예시 상에서, 상태란 오토바이의 현재 상황(80km의 speed로 운전중이라는 상태)를 뜻한다. 상태는 변할 수 있다. 상태는 클래스 내 멤버 변수를 만들어 나타낼 수 있다.
public class MotorBike {
int speed; // 상태, 멤버 변수
}
public class MotorBikeRunner {
public static void main(String[] args) {
MotorBike ducati = new MotorBike();
MotorBike honda = new MotorBike();
ducati.speed = 100; // ducati의 speed에 100을 넣어준다
honda.speed = 80; // honda의 speed에 80을 넣어준다
ducati.speed = 20; // ducati의 speed에 20을 넣어준다. 100->20
honda.speed = 0; // honda의 speed에 0을 넣어준다. 80->0
//이처럼 '상태'는 변화가 가능하다.
}
}
위의 MotorBike 클래스가 가진 문제점이 있다. MotorBikeRunner 클래스가 직접적으로 MotorBike(별개의 독립적인 클래스)의 인스턴스 변수에 접근할 수 있다는 것이다.
이처럼 독립적인 또다른 클래스의 내부 변수에 접근하는 것은 ‘캡슐화’를 파괴하기에 좋지 않다.
캡슐화
캡슐화의 기본 원칙
- 클래스는 특정 클래스의 데이터를 직접적으로 바꿀 수 없다.
- 상태를 바꾸고자 한다면(데이터에 접근하려면) 해당 클래스에서 수행하는 동작(메서드)을 통해야 한다.
아래와 같이 변수를 private로 만들면 클래스 외부에서 접근할 수 없다. 즉, ducati.speed = 100 처럼 직접적으로 해당 데이터를 바꿀 수 없게 된다.
이 경우 getter 메서드와 setter 메서드를 이용하여 private한 변수에 접근할 수 있다.
- getter 메서드 : private 변수의 값을 return해준다. 따라서 값을 출력하고 싶을 때 사용하게 된다.
- setter 메서드 : private 변수에 값을 넣어주는 역할을 한다. 값을 넣기만 할 뿐 딱히 값을 반환하지 않기 때문에 void 타입으로 선언한다.
public class MotorBike {
private int speed; // 멤버변수 member variable
void setSpeed(int speed) {
this.speed = speed; // this.speed는 멤버변수. 후자의 speed는 인자로 받아온 매개변수
}
public int getSpeed() {
return speed;
}
}
public class MotorBikeRunner {
public static void main(String[] args) {
MotorBike ducati = new MotorBike();
MotorBike honda = new MotorBike();
ducati.setSpeed(100);
honda.setSpeed(80);
System.out.println(ducati.getSpeed());
System.out.println(honda.getSpeed());
}
}
캡슐화의 장점
조건식을 추가하는 등 제약을 걸면 다른 클래스 코드의 잘못된 데이터를 차단할 수 있다.
void setSpeed(int speed) {
if(speed > 0) {
this.speed = speed;
}
}
예를 들어 위와 같이 speed가 0 이상일 때만 실행한다는 조건을 추가하면, speed가 마이너스가 되는 잘못된 상황을 방지할 수 있는 것이다. 이처럼 메서드를 통해 특정 개체에 들어갈 수 있는 것과 없는 것을 검증하는 것이 캡슐화의 주요 기능이다.
객체 내 로직을 캡슐화하면 외부 코드를 많이 수정할 필요 없이 쉽게 로직을 바꿀 수 있다는 점 또한 캡슐화의 장점이다.
메서드와 인자
메서드에게 매개변수는 해당 메서드의 지역변수이다. 즉, 메서드에게 인자란 메서드 내 지역 변수와 유사하다.
위의 setSpeed 메서드에서 this.speed는 private으로 선언한 멤버변수 speed를 뜻하며, 후자의 speed는 인자로 받아온 매개변수이다.
알기 쉽게 받아온 변수명을 speedParameter라는 이름으로 바꿔보면 다음과 같다.
public class MotorBike {
private int speed; // 멤버변수 member variable
void setSpeed(int speedParameter) {
this.speed = speedParameter;
}
public int getSpeed() {
return speed;
}
}
메서드를 만들 때 생각해야 할 것
입력값, 출력값, 이름을 생각해야 한다. setSpeed의 예를 들어보면 아래와 같다.
- 입력값input : speed의 값이니. int speed
- 출력값output : 지금은 값만 설정중이니 일단은 void (딱히 return할 값이 없기 때문에)
- 이름 : 속도값을 설정할 거니까 setSpeed라는 이름
멤버 변수의 초기값은 0이다
setSpeed의 100값을 setSpeed로 보내보면, 멤버 변수의 기본값이 0이라는 것을 확인할 수 있다.
public class MotorBike {
private int speed; // <- 기본값 0 (멤버변수)
public void setSpeed(int speedParameter) {
System.out.println(speedParameter); // 받아온 값 100 출력
System.out.println(this.speed); // 0 출력 <- 기본값
this.speed = speedParameter;
}
}
public class MotorBikeRunner {
public static void main(String[] args) {
MotorBike ducati = new MotorBike();
MotorBike honda = new MotorBike();
ducati.setSpeed(100); //100을 setSpeed로 보낸다
}
추상화
추상화는 우리 삶에서 매우 중요하다.
예를 들어 오토바이를 탈 때 오토바이 엔진 내부가 어떻게 작동하는지 굳이 알 필요가 없다. 열쇠를 구멍에 꽂고 시동 거는 법, 기어와 액셀 사용법만 알아도 충분하다.
추상화와 캡슐화
추상화와 캡슐화는 연관이 깊은 개념이다.
캡슐화는 특정 개체에 속하는 데이터를 숨겨 접근을 차단하고 연산만 허용한다.
추상화는 훨씬 포괄적인 개념으로, 모든 복잡한 내용을 숨기기에 그런 세부 내용을 전부 알 필요 없이 그냥 사용하면 된다. 메서드를 호출하고 실행되겠거니 생각하면 된다는 것이다.