Goal : 캡슐화의 의도 & 목적을 이해하고 코드에서는 어떻게 구현되는지 알아본다
Encapsulation 캡슐화란 ?
객체 지향 프로그래밍 (OOP)에서 캡슐화는 데이터를 해당 데이터에 대해 작동하는 메서드와 번들로 묶거나 일부 개체 구성 요소에 대한 직접 액세스를 제한하는 것을 말합니다. 캡슐화는 클래스 내부의 구조화된 데이터 개체의 값이나 상태를 숨기는 데 사용되며 숨겨진 구현 세부 정보를 노출하거나 메서드에서 유지 관리하는 상태 불변성을 위반할 수 있는 방식으로 클라이언트가 직접 액세스하는 것을 방지합니다.
public class MotorBike {
//state
int speed;
//behaviour
void start(){
System.out.println("MotorBike.start");
System.out.println("speed :::" + speed);
}
}
MotorBike라는 클래스는 speed 멤버변수와 start라는 메소드를 가지고 있음
public class Runner {
public static void main(String[] args) {
MotorBike ace = new MotorBike();
ace.start();
ace.speed = 100;
ace.speed = 20;
ace.start();
}
}
메인함수 클래스에서 start() 메소드 실행 후 speed를 변경 후 다시 start()를 실행하면?
결과
MotorBike.start
speed :::0
MotorBike.start
speed :::20
최초 멤버변수의 스피드는 기본 값인 0이고, 이후 100 -> 20으로 변경되어 최종 스피드는 20이 찍힘
여기서 문제는 다른 클래스(Runner)가 MotorBike 직접적으로 접근하여 내부 변수를 변경할 수 있다는 점이다.
=> 외부의 접근으로 객체를 손상시킴
여기서 Encapsultation 캡슐화의 개념이 등장한다.
Encapsultation is the concept that only this class(MotorBike) should have access to the data inside the specific class. all the other classes should access data through the behaviour(method) of the class first.
so other classes should not be able to change the date of specific class directly.
= > 즉, 다른 클래스는 해당 클래스에 직접 접근할 수 없고 오직 method를 통해서만 접근 / 변경이 가능하다
package com.in28mins.oop;
public class MotorBike {
private int speed;
public int getSpeed(){
return speed;
}
public void setSpeed(int speed){
if(speed > 0)
this.speed = speed;
}
}
멤버변수 앞에 접근자 private을 생성해주면 다른 클래스에서 사용할 수 없다. 접근이 불가함!!
- ace.speed = 100; 이렇게 사용하지 못함
setSpeed()를 통하여 로컬변수 인자값을 받아서 멤버변수의 값을 변경 할 수 있음
- 멤버변수 private int speed와 로컬변수 int speed는 다름
package com.in28mins.oop;
public class Runner {
public static void main(String[] args) {
MotorBike ace = new MotorBike();
ace.setSpeed(-100);
System.out.println(ace.getSpeed());
}
}
결과 : 0
- setter를 통해서만 변수의 값을 변경 할 수 있음 but 위 코드에서는 if조건을 걸어주었기 때문에 조건에 부합하지 않아 0이 출력됨
public class Runner {
public static void main(String[] args) {
MotorBike ace = new MotorBike();
// get ace speed
// increase it by 100
// set it to ace
int aceSpeed = ace.getSpeed();
aceSpeed += 100;
ace.setSpeed(aceSpeed);
System.out.println(ace.getSpeed());
}
}
결과 : 100
위와 같은 방식으로 속도가 증가 될때는 MotorBike 클래스의 게터를 통해 멤버 변수를 가져와서, 100을 증가시키고 세터를 통해 멤버변수를 변경하면 된다. 그런데 문제는 bravo라는 새로운 객체를 만들고 똑같은 상황을 연출하고 싶다면 중복코드가 발생한다는 점이다
----> 비지니스 로직 코드 중복 !!!!!!!
public class Runner {
public static void main(String[] args) {
MotorBike ace = new MotorBike();
MotorBike bravo = new MotorBike();
// get ace speed
// increase it by 100
// set it to ace
int aceSpeed = ace.getSpeed();
aceSpeed += 100;
ace.setSpeed(aceSpeed);
int bravoSpeed = bravo.getSpeed();
bravoSpeed += 200;
bravo.setSpeed(bravoSpeed);
System.out.println(ace.getSpeed());
System.out.println(bravo.getSpeed());
}
}
그런데 이 비지니스로직도 캡슐화를 통해 구현을 하면?
public class MotorBike {
private int speed;
public int getSpeed(){
return speed;
}
public void setSpeed(int speed){
if(speed > 0)
this.speed = speed;
}
public void increaseSpeed(int howMuch){
this.speed = this.speed + howMuch;
}
}
public class Runner {
public static void main(String[] args) {
MotorBike ace = new MotorBike();
MotorBike bravo = new MotorBike();
ace.increaseSpeed(100);
bravo.increaseSpeed(200);
System.out.println(ace.getSpeed());
System.out.println(bravo.getSpeed());
}
}
비지니스 로직 메소드를 MotorBike클래스에 구현하고 다른 클래스에서는 그 메소드를 호출해서 사용하면 되는 것이다!!!!!
int aceSpeed = ace.getSpeed();
aceSpeed = aceSpeed + 100;
ace.setSpeed(aceSpeed);
----------------------->
ace.increaseSpeed(100);
코드 한 줄로 완료.
그런데 비지니스 메소드에도 현재 속도가 0이상일 때만 속도 변경가능한 validation을 진행하고 싶다면?
모든 비지니스 메소드에 똑같이 if(speed > 0)라는 중복코드가 발생할 것이다.
그럼 우리는 어떻게 이 코드 중복을 해결할 수 있는가?
public class MotorBike {
private int speed;
public int getSpeed(){
return speed;
}
public void setSpeed(int speed){
if(speed > 0)
this.speed = speed;
}
public void increaseSpeed(int howMuch){
setSpeed(this.speed + howMuch);
}
public void decreaseSpeed(int howMuch){
setSpeed(this.speed - howMuch);
}
}
이렇게 이미 구현되어 있는 메소드의 벨리데이션을 사용하면 된다 !
=> 이 말인 즉슨, 게터와 세터 그리고 모든 비지니스 로직은 클래스 안의 오브젝트와 연관되어 있다는 것이다
정리
- 캡슐화는 외부로 부터의 객체 손상을 막는다 : 정보의 은닉화
- 은닉하는 방법으로는 다음과 같은 접근제한자를 사용한다
접근제한자 | |
public | 모든 클래스에서 접근 가능 |
protected | 자기 자신, 같은 패키지, 서로 다른 패키지라도 상속받은 자식 클래스에서 접근 가능 |
private | 오로지 자기 자신만 접근 가능 |
접근제한자를 적지 않으면 default접근 지정자 | 자기자신과 같은 패키지에서만 접근 가능 |
접근 제한자 사용
- 클래스 : public, default
- 생성자 : public, protected, default, private
- 멤버 변수 : public, protected, default, private
- 멤버 메서드 : public, protected, default, private
- 지역 변수 : 접근 제한자를 사용할 수 없음
- method를 통하여 객체의 변수를 수정할 수 있다.
- 해당 클래스에 비지니스로직 메소드를 추가하므로서 코드 중복을 줄일 수 있다.
수도 없이 외웠던 객체지향의 특징 캡슐화, 은닉화, 다형성 그러나 설명하라고 하면 또 말은 나오지 않았던 나,, 이 기회를 빌어서 다시 재정리를 마음먹으며 쭈우욱 하는데, 웬걸. 그때도 이렇게 공부했다면 참 좋았을텐데..! 그때 당시엔 당장 눈앞이 캄캄해서 책보고 정의만 달달 외우니.. 머릿속에 남는 것이 없즤,,, 역시 코드한번 치고 아키텍쳐 그려보고 그것의 등장배경을 아는 것이 이해하는 것임을 또 한번 깨친다. 단순 현상보다는 왜 이것이 등장했는가 어떤 개념과 목적을 가지고 탄생하게 되었는가에 집중하기를. 시간은 더 걸려도 길게 보면 오히려 시간을 더 버는 것이다.
@ The teacher Mr. Ranga 쌤의 강의를 참조하고 있습니다 (완전 쉽게 알려주심 왕추천)
'JAVA > 객체지향의 원리' 카테고리의 다른 글
[OOP] interface / 인터페이스 (0) | 2022.09.07 |
---|---|
[OOP] abstract / 추상화 (0) | 2022.09.05 |
[OOP] Inheritance / 상속 (0) | 2022.08.19 |
[OOP] Composition / 구성 (0) | 2022.08.15 |
[OOP] Constructor / 생성자 (0) | 2022.08.09 |