데이터 추상화(Data Abstraction)
- 물리적 시스템은 한 가지 타입만 처리 : 비트 문자열(bit strings)
- 고급 언어는 다양한 값에 대한 "래핑(wrapping)"을 제공
- 타입 : 값 + 작업
- 데이터 추상화 : 값은 하나로 결합될 수 있으며 적절한 작업을 통해 새롭고 더욱 추상적인 유형을 형성 가능
- 이 새로운 유형을 사용하여 현실 세계에서 더욱 복잡한 객체를 표현 가능
- 예) 레고 작품을 만들 때 아래 구조부터 쌓아서 올려서 합친다.
장점
- 복잡한 데이터 구조를 쉽게 처리한다.
- 세부 사항에 신경 쓸 필요가 없다.
- 인터페이스(Interface)와 구현(implementation)의 분리
- 구현(implementation)을 몰라도 인터페이스와 함께 사용 가능
- 정보 숨기기(Information Hiding): 내부 데이터는 정의된 인터페이스를 통해서만 액세스
추상 데이터 타입(Abstract Data Type)
- 추상 데이터 타입(ADT) 고려
- 타입 부분(type part), 작업 부분(operation part)
- 사양(Specification) : ADT 작업의 의미에 대한 설명
- 예) push() : 스택의 맨 위에 요소 삽입
abstract Stack { type Stack = // 타입 부분 struct { int info; Stack next; } operations: // 작업 부분 Stack create(){..} Stack push(Stack s, int x){..} Stack pop(Stack s){..} int top(Stack s){..} } |
- 사양을 올바르게 구현하고 인터페이스를 유지하는 한 ADT 사용에는 차이 없다.
- 사용으로부터 정의(definition)를 분리
- 정보(데이터 및 구현)가 숨겨지고 보호
표현 독립성(Representation Independence)
- 단일 사양 ADT의 두 가지 구현 : 이런 타입의 클라이언트에서 구별할 수 없다.
- type-safe 언어
- 하나의 ADT를 동일한 서명(signature 또는 인터페이스)이지만 구현이 다른 다른 ADT로 교체
- 타입 오류가 발생하지 않는다.
ADT 한계
- Counter 예제
- 타입 부분(type part) : Counter가 정수임 나타낸다.
- 작업 부분(operation part) : Counter의 일부 주요 기능을 구현
abstract Counter { type Counter = int; operations: void reset(Counter c) { c = 0; } int get(Counter c) { return c; } void increment(Counter c) { c = c + 1; } } |
- 진보된 타입의 Counter가 필요하다고 가정
- 재설정 횟수도 계산
- ADT에 필드를 추가
- reset()은 Counter와 다르다.
- 캡슐화는 잘 유지된다.
abstract Counter2 { type Counter2 = struct { int x; int num_reset; } operations: void reset(Counter2 c) { c.x = 0; c.num_reset += 1; } int get(Counter2 c) { return c; } void increment(Counter2 c) { c = c + 1; } int get_resets(Counter2 c){ return c.num_reset; } } |
- 문제는 Counter와 Counter2 사이에 아무런 관계가 없다는 것
- 어느 쪽이든 수정하면 다른 쪽에서는 인식할 수 없다.
- Counter 사양이 변경된 경우
- 두 ADT 모두에서 동일한 작업이 있더라도 모두 변경해야 한다.
- 예) get() 및 increment().
abstract Counter2 { type Counter2 = struct { int x; int num_reset; } operations: void reset(Counter2 c) { c.x = 0; c.num_reset += 1; } int get(Counter2 c) { return c; } void increment(Counter2 c) { c = c + 1; } int get_resets(Counter2 c){ return c.num_reset; } } |
Counter 상속(Inherit)
- Counter의 이전 작업을 활용
- Counter와 Counter3 모두에 변경 사항을 적용하려면 Counter만 수정
- Counter에서 가능한 모든 작업은 Counter3에서도 가능
- Counter3은 Counter와 호환
abstract Counter3 { type Counter3 = struct { Counter x; int num_reset; } operations: void reset(Counter3 c) { reset(c.x); c.num_reset += 1; } int get(Counter3 c) { return get(c.x); } void increment(Counter3 c) { increment(c.x); } int get_resets(Counter3 c){ return c.num_reset; } } |
문제가 해결되었는가?
- 100개의 카운터(Counter 또는 Counter3)가 있다고 가정
- 모두 재설정하고 싶다.
- reset(arr[i])을 호출하면
- 작업 고려 - Counter 또는 Counter3을 매개변수로 사용
- reset(Counter)만 실행
- 적절한 방법을 동적으로 선택할 수 없다.
Counter arr[100]; ... arr[9] = new Counter(); arr[10] = new Counter3(); ... for(int i=0; i<100; i++){ reset(arr[i]); } void reset(Counter c) { c = 0; } void reset(Counter3 c) { reset(c.x); c.num_reset += 1; } |
ADT 한계
- 캡슐화 및 정보 숨기기 적합하다.
- 그러나 복잡한 설계 사용에 유연성 부족하다.
- 객체 지향 패러다임이 충족할 수 있는 다음 네 가지 요구 사항
- 캡슐화(Encapsulation), 정보 숨기기(Information hiding)
- 상속(Inheritance.)
- 하위 타입 호환성(Subtype compatibility)
- 동적 방법 선택(Dynamic method selection)
'프로그래밍언어론' 카테고리의 다른 글
[프로그래밍언어론] 31. OOP 개념 (0) | 2023.11.06 |
---|---|
[프로그래밍언어론] 29. 스크립팅 언어(Scripting Languages) (0) | 2023.11.06 |
[프로그래밍언어론] 28. 프로그래밍 언어 패러다임(PL Paradigm) (0) | 2023.11.06 |
[프로그래밍언어론] 27. 자료형(Data Types) (1) | 2023.10.17 |
[프로그래밍언어론] 26. 바인딩 정책(Binding Policy) (1) | 2023.10.17 |