정보 은닉 그리고 캡슐화

gustjtmd·2022년 1월 24일
0

Java

목록 보기
3/40

정보 은닉

정보를 은닉해야 하는 이유

다음 코드를 확인해보자 컴파일도 실행도 잘되지만 내용산 문제가 되는 부분이 있다.
class Circle{
    double rad = 0; //원의 반지름
    final double PI = 3.14;

    public Circle(double r){
        setRad(r); //아래에 정의된 setRad 메소드 호출을 통한 초기화
    }
    public void setRad(double r){
        if(r < 0){ //반지름은 0보다 작을 수 없으므로
            rad = 0;
            return; //이 위치에서 메소드 빠져나감
        }
        rad = r;
    }
    public double getArea(){
        return (rad * rad) * PI;  //원의 넓이 반환
    }
}

public class UnsafeCircle {
    public static void main(String[] args) {
        Circle c = new Circle(1.5);
        System.out.println(c.getArea());

        c.setRad(2.5);
        System.out.println(c.getArea());
        c.setRad(-3.3);
        System.out.println(c.getArea());
        c.rad = -4.5;	//옳지 않은 접근 방법, 그리고 문제가 되는 부분
        System.out.println(c.getArea());
    }
}


7.065
19.625
0.0
63.585



-------------------------------------------------------------------------------

public void setRad(double r){
        if(r < 0){ //반지름은 0보다 작을 수 없으므로
            rad = 0;
            return; //이 위치에서 메소드 빠져나감
        }
        rad = r;
    }
    
Circle 클래스를 정의한 이의 다음 의도
"반지름의 길이 rad에 0보다 작은 값이 저장되는 일이 발생하지 않도록 하겠다"

때문에 이러한 의도를 따르기 위해서라도 반지름의 길이를 변경할 때에는 반드시 위의 메소드
호출을 통해서만 변경을 진행해야 하는데 다음과 같은 잘못된 접근이 발생하였다

c.rad = -4.5	//옳지 않은 접근 방법

이렇듯 인스턴스 변수의 직접적인 접근을 허용하면 컴파일 과정에서 드러나지 않는 큰 실수가 
발생할 수 있다. 때문에 위와 같은 접근을 허용하지 않도록 클래스를 설계할 필요가 있다.
그리고 이러한 클래스의 설계를 가리켜 '정보 은닉'이라 한다.

정보의 은닉을 위한 private 선언

'정보 은닉'이 의미하는 바는 중요하지만 방법은 간단하다 인스턴스 변수의 앞에 private
언언을 하면 된다

정보 은닉을한 코드

class Circle {
    private double rad = 0; //클래스 내부 접근만 허용
    final double PI = 3.14;

    public Circle2(double r) {
        setRad(r);
    }

    public void setRad(double r) {
        if (r < 0) {
            rad = 0;
            return;
        }
        rad = r;
    }
    public double getRad() {
        return rad;
    }

    public double getArea(){
        return (rad * rad) * PI; //원의 넓이 반환
    }
}


--------------------------------------------------------------------------

클래스 Circle의 다음 인스턴스 변수가 private으로 선언이 되면
"변수 rad는 클래스 내부에서만 접근을 허용하겠다"라는 뜻

private double rad = 0;	//클래스 내부 접근만 허용

클래스 외부에서 선언된 멤버에 접근할 경우 컴파일 오류발생

public static void main(String args[]){
	Circle c = new Circle(1.5);
    	c.rad = -4.5	//컴파일 오류
}

접근 수준 지시자

public protected private default

클래스 정의 대상으로는 public default만 선언 가능하다

public : 어디서든 인스턴스 생성이 가능하다.
default : 동일 패키지로 묶인 클래스 내에서만 생성을 허용한다

인스턴스 변수, 메소드 대상은 public protected private default 선언 가능하다

캡슐화

캡슐화는 정보 은닉과 더불어 객체지향 기반의 클래스 설계에 있어 가장 기본이면서 중요한 원칙 중
하나이다. 캡슐화는 문법적인 내용은 아니다 클래스 안에 '무엇을 넣을까'에 대한 이론을 제시하는
내용이다.

캡슐화가 이뤄지지 않은 코드

class SinivelCap{   //콧물 처치
    void take(){
        System.out.println("콧물이 낫습니다");
    }
}
class SneezeCap{    //재채기 처치
    void take(){
        System.out.println("재채기가 멎습니다.");
    }
}
class SnuffleCap{   //기침 처치
    void take(){
        System.out.println("기침이 멈춥니다.");
    }
}

class ColdPatient{
    void takeSinivelCap(SinivelCap cap){
        cap.take();
    }
    void takeSneezeCap(SneezeCap cap){
        cap.take();
    }
    void takeSnuffleCap(SnuffleCap cap){
        cap.take();
    }
}
public class BadEnacapsulation {
    public static void main(String[] args) {
        ColdPatient suf = new ColdPatient();

        suf.takeSinivelCap(new SinivelCap());
        suf.takeSneezeCap(new SneezeCap());
        suf.takeSnuffleCap(new SnuffleCap());
    }
}


콧물이 낫습니다
재채기가 멎습니다.
기침이 멈춥니다.


-----------------------------------------------------------------------

위와 같은 클래스 구성에서는 
"코감기 증상 완화를 위해 SinivelCap, SneezeCap, SnuffleCap 인스턴스를 생성해야 한다"

더불어 다음 사항도 지켜야 한다고 가정해보자

"약은 SinivelCap, SneezeCap, 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 takeCap(SinusCap cap){
        cap.take();
    }
}

public class OneClassEncapsultation {
    public static void main(String[] args) {
        ColdPatient coldPatient = new ColdPatient();
        coldPatient.takeCap(new SinusCap());
    }
}


콧물이 낫습니다.
재채기가 멎습니다.
기침이 멈춥니다.


------------------------------------------------------------------------

위 코드즌 그전 코드와 결과면에서는 동일하지만 SinusCap 클래스 안에 감기에 관련된 모든 
내용이 캡슐화되었다. 그래서 이의 복용과 관련된 코드를 담고 있는 ColdPatient 클래스와
main 메소드가 이전 코드와 비교해서 매우 간단해졌다.

"SinusCap 인스턴스를 생성하고 take 메소드 호출한다"

캡술화는 절대로 클래스를 크게 만들라는게 아니다, 캡슐화에서 말하고자 하는 것은 크기가 아닌
내용이다. 해당 클래스와 관련 있는 내용을 하나의 클래스에 모두 담되 부족하게 담아서도
넘치게 담아서도 넘치게 담아서도 안된다는 뜻

캡슐화가 잘 이뤄진 두번째 코드 : 포함 관계로 캡슐화 완성하기

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();
    }
}

public class CompEncapsultation {
    public static void main(String[] args) {
        SinusCap sinusCap = new SinusCap();
        sinusCap.take();
    }
}

콧물이 낫습니다
재채기가 멎습니다.
기침이 멈춥니다

-----------------------------------------------------------------------

한 클래스가 다른 클래스의 인스턴스를 멤버로 가질수 있는데 이러한 관계를 가리켜 '포함 관계'라
한다. 이러판 포함 관계는 캡슐화를 완성하는 과정에서도 사용이 된다.
앞서 캡슐화가 완성되지 않은 예에서 다음 클래스들을 정의하였다

SinivelCap, SneezeCap, SnuffleCap

따라서 이들을 인스턴스 멤버로 갖는 다음과 같은 클래스를 정의할 수 있으며
이러한 방식으로 캡슐화를 완성시킬수 있다.


class SinusCap{
    SinivelCap sicap = new SinivelCap();
    SneezeCap szCap = new SneezeCap();
    SnuffleCap sfCap = new SnuffleCap();

    void take(){
        sicap.take();szCap.take();sfCap.take();
    }
}
출처 : 윤성우의 열혈 Java 프로그래밍
profile
반갑습니다

0개의 댓글