개, 고양이, 사자, 호랑이 → 동물
추상화
추상화란 어떤 양상, 세부 사항, 구조를 좀 더 명확하게 이해하기 위해 특정 절차나 물체를 의도적으로 생략하거나 감춤으로써 복잡도를 극복하는 방법이다.
복잡성을 다루기 위해 추상화는 두 차원에서 이뤄짐
- 첫 번째는 구체적인 사물들 간의 공통점은 취하고 차이점은 버리는 일반화를 통해 단순하게 만드는 것이다.
- 두 번째는 중요한 부분을 강조하기 위해 불필요한 세부 사항을 제거함으로써 단순하게 만드는 것이다.
1. 추상화의 목적
추상화의 목적은 복잡성을 이해하기 쉬운 수준으로 단순화 하는것이다.
ex) 지하철 노선도 (사실적인 지형과 축적 무시, 역사이의 연결성에만 집중)
2. 앨리스 이야기에서의 추상화
앨리스 이야기에 등장하는 정원사, 병사, 신하, 왕자와 공주, 하트 왕, 하트 여왕은 트럼프로 추상화가 가능하다.
→ 이들의 차이점을 의도적으로 무시하고, 공통점을 강조함으로써 추상화 할 수 있다. (추상화의 두 차원을 만족)
→ 이렇게 추상화를 함으로써 해당 인물들의 복잡다단한 면을 깔끔하게 추상화할 수 있다. (추상화의 목적)
- 추상화를 사용하지 않은 예제
class HeartKing { //하트 왕 public void 납작엎드리기(){ System.out.println("납작 엎드렸습니다."); } public void 뒤집기(){ System.out.println("뒤집었습니다."); } public void 걸을때마다_펄럭이기(){ System.out.println("걸을때마다 몸이 종이처럼 좌우로 펄럭거립니다."); } public void 신하들_부르기() { System.out.println("신하들을 불렀습니다."); } }
- 추상화를 사용한 예제
class abstract Trump { //추상화 된 트럼프 public void 납작엎드리기(){ System.out.println("납작 엎드렸습니다."); } public void 뒤집기(){ System.out.println("뒤집었습니다."); } public void 걸을때마다_펄럭이기(){ System.out.println("걸을때마다 몸이 종이처럼 좌우로 펄럭거립니다."); } } class HeartKing extends Trump { //하트 왕 public void 신하들_부르기() { System.out.println("신하들을 불렀습니다."); } }
- 이렇게 공통점을 기반으로 객체를 묶기 위한 그릇을 개념(Concept)이라고 한다.
- 개념을 이용하면 객체를 여러그룹으로 분류(Classification)할 수 있다.
- 객체에 어떤 개념을 적용해 개념 그룹의 일원이 될때 객체를 그 개념의 인스턴스(instance)라고 한다.
Trump heartKing = new HeartKing();
개념과 타입
1. 개념의 세가지 관점
- 심볼(Symbol): 개념을 가리키는 간략한 이름이나 명칭
- 트럼프
- 내연(Intension): 개념의 완전한 정의를 나타내며 내연의 의미를 이용해 객체가 개념에 속하는지 여부를 확인할 수 있음
- 몸이 납작하고 두 손과 두 발은 네모 귀퉁이에 달려 있는 등장인물
- 외연(extension): 개념에 속하는 모든 객체의 집합
- 정원사, 병사, 신하, 왕자와 공주, 하트 왕과 하트 여왕 등
이러한 개념을 이용해 객체를 분류할 수 있다.
객체를 적절한 개념으로 분류하지 못한 애플리케이션은 유지보수가 어렵고 변화에 쉽게 대처하지 못한다.
분류는 추상화를 위한 도구이다.
객체를 분류하기 위해선 데이터가 아니라 행동을 먼저 생각해야함
→ 책임 주도 설계, DDD 단점 개선
2. 타입
타입은 공통점을 기반으로 객체들을 묶기 위한 틀이다.
타입의 정의는 개념의 정의와 완전히 동일하다.
타입 == 개념
객체가 어떤 행동을 하느냐에 따라 객체의 타입이 결정된다.
객체를 결정하는것은 행동이다. 이것이 객체를 객체답게 만드는 가장 핵심적인 원칙이다.
객체의 내부 표현과는 아무런 상관이 없다.
- 객체의 내부 표현 방식이 다르더라도 어떤 객체들이 동일하게 행동한다면 그 객체들은 동일한 타입에 속한다.
- 여기서의 동일한 행동은 동일한 책임을 의미하며, 동일한 책임이란 동일한 메세지 수신을 의미한다.
이는 다형성이랑 연관되어 있음
- 다형성 예제
interface Barista { void makeCoffee(String orderedMenu); } class StarbucksBarista implements Barista { private int coffeeAmount; @Override public void makeCoffee(String orderedMenu) { ... } } class TwoSomeBarista implements Barista { private int coffeeAmount; @Override public void makeCoffee(String orderedMenu) { ... } }
스타벅스 바리스타와 투썸플레이스 바리스타는
주문된 메뉴(orderedMenu)
라는 메세지를 수신해 동일한 행동(makeCoffee)
을 하게 된다.스타벅스 바리스타와 투썸플레이스 바리스타의
커피 제조(makeCoffee)
만이 고려 대상이며, 커피의 양(coffeeAmount)
을 얼마나 사용하는 지 등 내부 표현 방식은 고려하지 않는다.스타벅스 바리스타와 투썸플레이스 바리스타는 동일한 바리스타 타입이다.
→ 데이터의 내부 표현 방식과 무관하게 행동만이 고려 대상이라는 사실은 외부에 데이터를 감춰야한다는것을 의미함
→ 이 원칙을 캡슐화 라고 한다.
3. 타입의 계층
이렇게 위에 나온 추상화를 사용하게 되면 일반화와 특수화, 슈퍼타입과 서브타입을 이해할 수 있게 된다.
- 일반화와 특수화
class Animal { public void eat(); public void sleep(); } class Bird extends Animal { public void fly(); }
Bird는 Animal 객체가 할 수 있는 모든 행동(먹고, 자기)를 할 수 있을 뿐만 아니라 날아다니는 행동을 더 할 수 있다.
Animal은 Bird를 포괄하는 좀 더 일반적인 개념이며,
Bird는 Animal보다 좀 더 특화된 행동을 하는 특수한 개념
이 두 개념 사이의 관계를 일반화/특수화 관계라고 함
그러면 이러한 일반화/특수화 관계를 결정하는 것은 무엇일까?
바로 객체의 상태를 표현하는 데이터가 아니라 행동이다.
이러한 일반적인 타입은 슈퍼 타입, 특수한 타입을 서브 타입이라고 한다.
- 슈퍼타입과 서브타입
슈퍼타입과 서브타입에서 중요한 것은 두 타입 간의 관계가 행동에 의해 결정된다는 점이다.
일반적으로 서브타입은 슈퍼타입을 대체할 수 있어야 한다.
→ Bird는 날아다니는것 이외에 먹거나 잘 수 있으므로 Animal을 대체할 수 있다는 말이다.
→ 이는 객체지향 설계 5대 원칙 중 하나인 리스코프 치환의 법칙이다.
결국 타입은 추상화다.