IT 끄적장

OOP(Object Oriented Programming)의 특성 (캡슐화) 본문

OOP

OOP(Object Oriented Programming)의 특성 (캡슐화)

kyunghoon_dev 2019. 11. 14. 16:24
  • Encapsulation (캡슐화)
    정보은닉(information hiding)을 통하여 높은 응집도(cohesion)와 낮은 결합도(coupling)를 갖도록 하는 OOP의 설계 원리

캡슐화를 이해하기 이전에 응집도와 결합도 대하여 먼저 알아보자.

 

응집도(cohesion)는 클래스나 모듈 안의 요소들이 얼마나 밀접하게 관련되어 있어야 하는 나타내는 척도이다.

결합도(coupling)는 특정 operation을 수행하는데 다른 클래스나 모듈들에 얼마나 의존적(dependent)인지를 나타낸다.

 

대게, 이상적인 모듈을 응집도(cohesion)가 높으며, 결합도(coupling)가 낮은 모듈을 말한다. 

 

why?

 

사용자의 요구사항이 변경되면, 개발자 역시 코드 수정이 불가피하다.

이때, 응집도가 높고 결합도가 낮은 설계는 최소한의 코드 수정을 통해 사용자 요구사항에 대응할 수 있다.


정보은닉이란?

 

말 그대로 알 필요가 없는 정보는 외부에서 접근하지 못하도록 제한하는 것이다.

 

예시) 자동차의 가속페달을 밟았을 때 어떤 과정을 거쳐 속도가 올라가는지 모르더라고 운전에는 전혀 지장이 없다.

 

그렇다면, 정보은닉이 왜 필요한가? 

 

소프트웨어는 결합도가 높을수록 많은 문제가 발생한다.

한 클래스가 변경되면, 이 클래스와 dependency가 있는 모든 클래스는 변경해야 될 가능성이 높아진다는 의미이다.

 

코드를 통하여 살펴보자.

 

public class ArrayStack {
  public int top;
  public int[] itemArray;
  public int stackSize;


  public ArrayStack(int stackSize) {
    itemArray = new int[stackSize];
    this.stackSize = stackSize;
    top = -1;
  }

  public boolean isEmpty() {
    return (top == -1);
  }

  public boolean isFull() {
    return (top == this.stackSize - 1);
  }

  public void push(int item) {
    if (isFull()) {
      System.out.println("failed to insert, stack is full!");
    } else {
      itemArray[++top] = item;
      System.out.println("inserted item : " + item);
    }
  }

  public int pop() {
    if (isEmpty()) {
      System.out.println("stack is empty!");
      return -1;
    } else {
      return itemArray[--top];
    }
  }


  public class StackClient {
    public static void main(String[] args) {
      ArrayStack stack = new ArrayStack(10);
      stack.itemArray[++stack.top] = 20;
      System.out.println(stack.itemArray[stack.top]);
    }
  }
}

 

위 코드를 통해 주의해서 볼 것은, 자료구조가 모두 public으로 되어 외부에 공개되어 있다는 점이다.

 

StackClient에서 처럼 push나 pop을 사용하지 않고서도 직접 itemArray에 값을 입력할 수 있다.

이때, ArrayStack과 StackClient 간에는 강한 결합이 발생한다.

 

만약에 ArrayStack의 스택 구현이 배열이 아닌 ArrayList를 사용하여 구현이 변경된다면, StackClient 또한  다음과 같이 변경되어야 한다.

 

public class StackClient{
	
    public static void main(String[]args){
    	ArrayListStack stack = new ArrayListStack(10);
        stack.items.add(new Integer(20));
        System.out.println(stack.items.get(stack.items.Size()-1));
    }
}

 

ArrayStack 클래스에서 은닉되어야 할 정보가 모두 public으로 외부에 공개가 되었고, StackClient는 은닉되어야 할 정보를 직접 사용하였기 때문에 위와 같이 강한 결합이 일어난다. 

 

하지만, 위와 같이 변경한다 해도 자료구조가 또 변경이 되면 StackClient 또한 매번 변경이 일어날 것이다.

개발을 하다 보면 느끼겠지만, 오류를 수정하기 위해 코드를 변경하다 보면 또 다른 오류가 발생한다.

 

위의 예시에서 정보은닉의 개념을 적용해보자 

 

다음과 같이 변경될 가능성이 큰 자료구조를 private을 통해 은닉한다.

 

private int top;
private int[] itemArray;
private int stackSize;

 

이렇게 될 경우 스택에는 앞서 제공한 pop, push를 통하여만 접근이 가능하다.

즉, push 나 pop이 어떠한 자료구조를 사용해 스택의 값을 채우는지 알 수가 없게 되므로 스택과 이를 사용하는 코드의 결합도가 낮아지게 된다.

 

위와 같이 정보은닉이 이루어질 경우 StackClient의 코드는 다음과 같이 재작성된다.

 

public class StackClient{
	public static void main(String[]args){
    	ArrayListStack stack = new ArrayListStack(10);
        stack.push(10);
        System.out.println(stack.pop());
    }
}

 

 

예시를 통해 살펴보면 정말 간단한 개념이지만, 이를 비즈니스 소프트웨어와 같이 대규모 프로젝트의 관점에서 보면 유지보수하는 데 있어 매우 중요한 부분이라고 생각된다.