✔ 정보 은닉
정보 은닉?
- 객체지향 기반의 클래스 설계에 있어 가장 기본이면서 중요한 원칙 중 하나
- 자바에서 말하는 '정보'는 클래스의 '인스턴스 변수'를 의미하므로 정보를 은닉한다는 것은 인스턴스 변수를 숨긴다는 뜻
- 만약 인스턴스 변수의 직접적인 접근을 허용하면, 컴파일 과정에서 드러나지 않는 중대한 실수가 발생할 수 있음
- 이러한 오류는 실행 결과에서도 드러나지 않아 더 큰 문제가 됨
- 예) 메소드 정의를 통한 인스턴스 변수를 초기화하지 않고 직접적으로 인스턴스 변수에 접근하는 경우
class Circle {
double rad = 0;
final double PI = 3.14l
public Circle(double r) {
setRad(r);
}
public void setRad(double r) {
// 반지름은 0보다 작을 수 없음
if(r < 0) {
rad = 0;
return;
}
rad = r;
}
}
class UnsafeCircle {
public static void main(String args[]) {
Circle c = new Circle(1.5);
c.setRad(2.5);
// 좋지 않은 접근 방법, 그리고 문제가 되는 부분
c.rad = -4.5;
}
}
private 선언
- 클래스를 대상으로 정보를 은닉하기 위해서는 인스턴스 변수 앞에 private 선언을 추가하면 됨
- 그리고 해당 인스턴스 변수에 접근할 수 있는 메소드를 별도로 제공하면 정보 은닉이 완료됨
- 인스턴스 변수가 private으로 선언되게 되면 클래스 내에 정의된 메소드 내에서의 접근만을 허용하게 됨
- 그러므로 클래스 외부에서 private으로 선언된 멤버에 접근할 경우 컴파일 오류가 발생하게 됨
- 인스턴스 변수가 private으로 선언되게 되면 클래스 내에 정의된 메소드 내에서의 접근만을 허용하게 됨
- 인스턴스 변수를 private으로 선언하게 되면,
필요에 따라 인스턴스 변수에 대한 값의 설정을 위한 Setter 메소드와 값의 참조를 위한 Getter 메소드를 제공해야 함- Getter는 인스턴스 변수의 값을 참조하는 용도로 정의된 메소드로
변수의 이름이 name일 때, 메소드의 이름은 getName으로 짓는 것이 관례 - Setter는 인스턴스 변수의 값을 설정하는 용도로 정의된 메소드로
변수의 이름이 name일 때, 메소드의 이름은 setName으로 짓는 것이 관례
- Getter는 인스턴스 변수의 값을 참조하는 용도로 정의된 메소드로
class Circle {
private double rad = 0;
final double PI = 3.14l
public Circle(double r) {
setRad(r);
}
// rad에 값을 저장 (수정)
public void setRad(double r) {
// 반지름은 0보다 작을 수 없음
if(r < 0) {
rad = 0;
return;
}
rad = r;
}
// rad에 저장된 값을 반환
public doubel getRad() {
return rad;
}
}
class InfoHideCircle {
public static void main(String args[]) {
Circle c = new Circle(1.5);
c.setRad(2.5);
// 컴파일 오류 발생
c.rad = -4.5;
}
}
✔ 접근 수준 지시자
네 가지 종류의 접근 수준 지시자
- 접근 수준 지시자의 종류는 public, protected, private, default로 네 가지가 존재
- 이러한 선언을 할 수 있는 대상으로는 클래스의 정의, 클래스의 인스턴스 변수와 메소드가 존재
클래스 정의 대상의 접근 수준 지시자 : public, default
- 클래스가 public으로 선언되면 위치에 상관없이 어디서든 해당 클래스의 인스턴스를 생성할 수 있음
- 이때 하나의 소스파일에 하나의 클래스만 public으로 선언할 수 있으며,
소스파일의 이름과 public으로 선언된 클래스의 이름을 일치시켜야 함
- 이때 하나의 소스파일에 하나의 클래스만 public으로 선언할 수 있으며,
- 클래스가 default로 선언되면 동일 패키디로 묶인 클래스 내에서만 인스턴스를 생성할 수 있음
package zoo;
// default
class Duck {
...
}
// public
public class Cat {
public void makeCat() {
// 같은 패키지로 묶여 있으므로 인스턴스 생성 가능
Duck quack = new Duck();
}
}
package animal;
// public
public class Dog {
public void makeCat() {
// public으로 선언되어 있으므로 인스턴스 생성 가능
zoo.Cat yaong = new zoo.Cat();
}
public makeDuck() {
// 같은 패키지로 묶여 있지 않으므로 인스턴스 생성 불가로 컴파일 오류 발생
zoo.Duck quack = new zoo.Duck();
}
}
인스턴스 변수와 메소드 대상의 접근 수준 지시자 : public, protected, private, defualt
- 인스턴스 멤버가 public으로 선언되면 어디서든 접근이 가능함
- 인스턴스 멤버가 protected로 선언되면 상속 관계에 있는 다른 클래스에서 접근이 가능함
- 인스턴스 멤버가 private로 선언되면 동일 클래스에 정의된 메소드에서만 접근이 가능함
- 인스턴스 멤버가 default로 선언되면 동일 패키지로 묶인 클래스 내에서만 접근이 가능함
package zoo;
public class Cat {
// 어디서든 호출 가능
public void makeSound() {
...
}
// 동일 패키지로 묶인 클래스 내에서만 호출 가능
void makeHappy() {
...
}
}
package animal;
public class Dog {
public void welcom(zoo.Cat c) {
// 호출 가능, 컴파일 성공
c.makeSound();
// 호출 불가, 컴파일 오류
c.makeHappy();
}
}
package alpha;
public class AAA {
protect int num;
}
public class ZZZ extends alpha.AAA {
public void init(int n) {
num = n;
}
}
✔ 캡슐화
캡슐화란?
- 정보 은닉과 더불어 객체지향 기반의 클래스 설계에 있어 가장 기본이면서 중요한 원칙 중 하나
- 하나의 목적을 이루기 위해 관련 있는 모든 것을 하나의 클래스에 담아 두는 것
- 무조건 많이 담는다고 해서 캡슐화가 아니며, 부족해도 안되고 넘쳐도 문제가 됨
- 클래스들을 적절히 캡슐화시키면 프로그램이 간결해짐
캡슐화가 이뤄지지 않은 예제
- 캡슐화가 정상적으로 이뤄지지 않았을 때는 각각의 인스턴스를 생성해야 하며,
클래스 선언의 순서와 같은 제약사항이 있을 경우 문제가 발생하게 됨
class SinivelCap {
public void take() {
System.out.println("콧물 감기가 낫습니다!");
}
}
class SneezeCap {
public void take() {
System.out.println("재치기가 낫습니다!");
}
}
class SnuffleCap {
public void take() {
System.out.println("코감기가 낫습니다!");
}
}
class ColdPatiend {
public void takeSinivelCap(SinivelCap cap) {
cap.take();
}
public void takeSneezeCap(SneezeCap cap) {
cap.take();
}
public void takeSnuffleCap(SnuffleCap cap) {
cap.take();
}
}
public class NotEncapsulation {
public static void main(String[] args) {
ColdPatiend suf = new ColdPatiend();
suf.takeSinivelCap(new SinivelCap());
suf.takeSneezeCap(new SneezeCap());
suf.takeSnuffleCap(new SnuffleCap());
}
}
하나의 클래스로 캡슐화
- 하나의 클래스 안에 관련된 모든 내용이 캡슐화되면 하나의 인스턴스만 생성하면 되고, 순서 과정이 모두 자동화되게 됨
class SinusCap{
void sniTake(){
System.out.println("콧물이 싹 낫습니다.");
}
void sneTake(){
System.out.println("재채기가 멎습니다.");
}
void snuTake(){
System.out.println("코가 뻥 뚫립니다.");
}
void take(){
sniTake();
sneTake();
snuTake();
}
}
class ColdPatient{
void takeSinusCap(SinusCap cap){
cap.take();
}
}
class goodEncapsulation{
public static void main(String[] args){
ColdPatient suf = new ColdPatient();
suf.takeSinus(new SinusCap());
}
}
포함 관계로 캡슐화
- 한 클래스가 다른 클래스의 인스턴스를 멤버로 가지는 포함 관계를 이용해 캡슐화를 완성하는 과정에서 사용할 수 있음
class SinivelCap {
void take() {
System.out.println("콧물 감기가 낫습니다!");
}
}
class SneezeCap {
void take() {
System.out.println("재치기가 낫습니다!");
}
}
class SnuffleCap {
void take() {
System.out.println("코감기가 낫습니다!");
}
}
class SinusCap{
SinivelCap siCap = new SinivelCap();
SneezeCap szCap = new SneezeCap();
SnuffleCap sfCap = new SnuffleCap();
void take(){
siCap.take();
szCap.take();
sfCap.take();
}
}
class ColdPatient{
void takeSinusCap(SinusCap cap){
cap.take();
}
}
class goodEncapsulation{
public static void main(String[] args){
ColdPatient suf = new ColdPatient();
suf.takeSinus(new SinusCap());
}
}
'Java-Spring > 열혈 Java 프로그래밍' 카테고리의 다른 글
[Java] 메소드 오버로딩과 String 클래스 (0) | 2023.08.03 |
---|---|
[Java] 클래스 변수와 클래스 메소드 (0) | 2023.08.02 |
[Java] 패키지와 클래스 패스 (0) | 2023.08.01 |
[Java] 클래스와 인스턴스 (0) | 2023.08.01 |
[Java] 메소드와 변수의 스코프 (0) | 2023.06.28 |