추상화
추상화란?
추상화(Abstract)란 어떠한 기능 중 핵심적인 기능을 추출하여 이를 일반화하는 객체 지향 설계 원칙이다.
여러 개의 객체에서 공통적이고 핵심적인 기능을 추출하고 문제 영역, 관점에 따라 필요하지 않은 기능을 제거해 대상화하는 객체 지향 설계 원칙이다.
주로 인터페이스(Interface)나 추상 클래스(Abstract Class)로 구현한다.
추상화의 특징으로는 문제 영역이나 관점에 의존적이라는 점이며, 이로 인해 같은 대상이라고 하더라도 어떠한 문제 영역, 관점에서 추상화를 진행했느냐에 따라 여러 가지의 추상화 모델이 나올 수 있다는 점이다.
추상화를 진행함으로써 얻을 수 있는 장점은
- 복잡도를 관리할 수 있다.
- 유연한 설계가 가능하다.
- 코드를 간결하게 해 준다.
로 세 가지 장점을 얻을 수 있다.
유연한 설계가 가능하다는 것은, 캡슐화와 비슷하게 모듈화를 진행함으로써 유지보수 시 코드의 수정으로 인한 수정사항을 최소화하는 것이라고 이해했다.
복잡도를 관리할 수 있다는 것은 자신만의 핵심적인 개념을 가지고 있으며, 다른 것들과 구별할 수 있을 정도로 정의된 객체에서 추상화를 통해 문제 영역에 대해 필요한 부분만을 추출하고 다른 것들은 모두 삭제해 객체의 정의 및 기능을 제한한다는 점에서 복잡도를 관리한다고 이해했다.
코드를 간결하게 해 준다는 것은 의미상 동일한 행동이 여러 번 반복되거나 사용하는 데이터가 동일하지만 여러 번 선언되는 것 처럼 코드의 중복을 제거하고 코드를 간결하게 해 준다고 이해했다.
이제 추상화의 특징 및 장점에 대해서 예시를 통해 이해하고자 했다.
문제 영역이나 관점에 의존적
복잡도 관리
흔히 사용되는 예제인 동물을 사용했다.
public class Dog {
private int legs = 4;
public void talk() {
System.out.println("개는 멍멍");
}
public void walk() {
System.out.println("개가 " + legs + "개의 다리로 걸어갑니다.");
}
}
public class Cat {
private int legs = 4;
public void talk() {
System.out.println("고양이는 야옹");
}
public void walk() {
System.out.println("고양이가 " + legs + "개의 다리로 걸어갑니다.");
}
}
public class Elepant {
private int legs = 4;
public void talk() {
System.out.println("코끼리는 뿌우우");
}
public void walk() {
System.out.println("코끼리가 " + legs + "개의 다리로 걸어갑니다.");
}
}
이러한 동물 클래스가 있다고 가정하자.
이 세 개의 동물 클래스를 추상화할 때, 동물이 어떤 울음소리를 가졌는지에 대한 관점으로 진행한다면 동물이 걷는 행위를 의미하는 walk() 메소드와 해당 메소드에서 사용되는 legs 변수는 의미 없는 값이 된다.
즉 원하는 기능 이외의 필요없는 기능이 있기 때문에 복잡하다고 할 수 있다.
추상화를 진행한다면, 다음과 같은 인터페이스를 만들 수 있다.
public interface Animal {
public void talk();
}
이렇게 진행된 추상화를 통해 동물 클래스를 구현한다면 아래와 같을 것이다.
public class Dog implements Animal {
@Override
public void talk() {
System.out.println("개는 멍멍");
}
}
public class Cat implements Animal {
@Override
public void talk() {
System.out.println("고양이는 야옹");
}
}
public class Elepant {
@Override
public void talk() {
System.out.println("코끼리는 뿌우우");
}
}
현재 관점에서 의미 없던 walk() 메소드와 해당 메소드에서 사용되는 legs 변수가 사라졌다.
필요없는 기능을 제거한 것이다.
이와 같이 추상화를 통해 객체 간 복잡도를 관리하는 것이 가능하다.
다음으로 울음소리가 아닌 걷는 행위에 관점을 가진 채로 추상화를 진행할 경우, 인터페이스는 아래와 같을 것이다.
public interface Animal {
public int legs = 4;
public void walk();
}
그리고 이 인터페이스로 구현한 동물 클래스는 아래와 같을 것이다.
public class Dog {
@Override
public void walk() {
System.out.println("개가 " + legs + "개의 다리로 걸어갑니다.");
}
}
public class Cat {
@Override
public void walk() {
System.out.println("고양이가 " + legs + "개의 다리로 걸어갑니다.");
}
}
public class Elepant {
@Override
public void walk() {
System.out.println("코끼리가 " + legs + "개의 다리로 걸어갑니다.");
}
}
이와 같이 어떤 관점에 따라 객체를 추상화하느냐에 따라서 같은 객체라고 하더라도 다른 추상화 모델이 나올 수 있다.
또한 인터페이스에서 개, 고양이, 코끼리의 walk() 메소드에서 사용하는 동일한 변수인 legs를 상수로 표현해 이를 추상화 했음을 알 수 있다.
유연한 설계
유연한 설계는 캡슐화와 유사한 의미로 이해했다.
결합도를 낮춰 유지보수 때 코드의 수정을 최소화할 수 있는 설계라고 이해했다.
public class Tests {
Animal target = new Cat();
// Animal target = new Dog();
// Animal target = new Elepant();
target.walk();
}
이와 같이 고양이, 개, 코끼리에 상관없이 해당 클래스가 Animal 인터페이스를 구현한 클래스라면
개발자는 target에 어떤 클래스의 인스턴스가 올 지 구체적으로 알 필요 없이 바로 walk() 메소드를 사용할 수 있으며
고양이가 아니라 개, 코끼리의 walk() 메소드를 사용하고자 하더라도 walk() 메소드를 호출하는 코드는 수정할 필요가 없다.
간결한 코드
public interface Animal {
public int legs = 4;
public void walk();
}
public class Dog {
@Override
public void walk() {
System.out.println("개가 " + legs + "개의 다리로 걸어갑니다.");
}
}
앞서 사용했던 예시와 같이 개, 고양이, 코끼리에서 공통적으로 사용하던 legs라는 변수를 인터페이스의 상수로 추상화해 개, 고양이, 코끼리 클래스에서 추가적으로 선언하지 않고도 사용할 수 있도록 했다.
반복문을 통해 어떠한 기능을 추상화하는 것 또한 가능하다.
System.out.println(0);
System.out.println(1);
System.out.println(2);
System.out.println(3);
System.out.println(4);
System.out.println(5);
0부터 5까지 출력하는 코드이다.
이 코드에서 0부터 시작해 5까지, 1씩 증가한 값을 출력한다라는 핵심 기능에 초점을 맞춰 추상화를 진행한다면
for(int i = 0; i < 6; i++) {
System.out.println(i);
}
위와 같이 표현할 수 있다.
즉 변수와 상수의 추상화나 반복문을 이용한 추상화를 통해 코드를 간결하게 할 수 있다.
정리
- 추상화는 특정 기능의 핵심적인 기능을 추출해 이를 대상화하는 객체 지향 원리이다.
- 추상화는 여러 객체의 핵심적이고 공통적인 기능을 추출해 이를 일반화하는 객체 지향 원리이다.
- 관점이나 문제 영역에 의존적이기 때문에 같은 대상이라도 다른 추상화 모델이 나올 수 있다.
- 객체들의 복잡도를 관리할 수 있으며, 유연한 설계가 가능하다.
해당 글은 개인이 공부하면서 정리한 글이기 때문에 정확하지 않은 내용이 있을 수 있습니다.
참조