좋은 언어와 좋은 프래임워크란 ?
언어와 framework의 발전사가 이렇다고 하고, 그러면 제목에서 적은 좋은 언어와 좋은 프래임워크란 무엇일까요?
가장 기본적인 기준이 되는 것이 2가지 일것 입니다.
첫번째로 프래임워크에서 제공하는 사용성과 정책이 프로그래밍 언어에서 제공하는 것과 일관되어야 합니다.
두번째는 일반적일 것 같은 동작은 그렇게 동작하도록 해야 합니다.
위에서 예기한 것들을 좀 살펴봅시다.
우선 첫 번째 "프래임워크와 언어의 일관성"에 대한 예를 들면,
프로그래밍 언어에서 var A = a; 라는 코드가 A에 a를 copy하는 것이라고 정의하고 있다면, 이 언어로 작성된 프래임워크의 MyObject obj = OldObj; 의 코드 역시 obj 에 OldObj의 내용들을 copy하는 것이어야 합니다. 그런데 만약 여기서, obj에 copy 하는 것이 아니라 OldObj의 내용을 가리키도록 동작하도록 작성되었다고 한다면,
obj의 내용을 변경했을때 왜 OldObj의 내용이 바뀌는지등에 대한 의문을 만들게 됩니다.
또는 실제 obj가 쓰일때 copy를 한다거나, obj.sync() 를 불렀을때 실제 복사가 일어난다거나 한다면, 사용자 입장에서 '=' 가 불렸을때 copy가 될 것을 기대하고 퍼포먼스를 높이기 위해 튜닝을 했는데, 실제로는 다른 timing에서 data가 copy 되기 때문에 제대로 된 튜닝을 못하게 되는 상황이 발생합니다.
이는 사용자에게 framework에서 제공하는 "=" 동작에 대해 혼란을 주는 결과가 됩니다.
이는 실수를 유발하거나 직관성을 떨어뜨리게 됩니다.
두번째 "일반적일것 같은 동작" 대한 예를 들어보자면,
ImageViewer class 가 있고, ImageViewer에는 Draw()라는 함수가 있습니다. 이 Draw() 라는 함수는 ImageViewer의 image를 화면에 그려주는 기능을 하는 함수입니다.
Draw()기능을 좀더 세밀하게 정의 해보자면,
1. Draw()는 ImageViewer가 들고있는 display용 context에 image를 drawing하는 함수라고 합시다.
2. Draw()가 불렸을때 화면에 보이지는 않고 framework의 rendering 시점에 이 display context의 내용이 화면에 출력되는 방식입니다.
그런데 이 class 개발자가 Draw()가 불렸을때 image를 직접 display context에 drawing하는 것보다, rendering 시점에 display context에 drawing 하도록 하는것이 성능상 이득이라고 판단하여, Draw()함수가 나중에 drawing이 필요하다는 flag만 설정하고 나오는 것으로 정의했습니다.
Draw() 함수에 대한 정의가 좋은 방향으로 결정 된것 일까요? 옳다 그르다의 표현은 좋지 못하고, API의 직관성을 흐리는 방향으로 간것 같다는 표현이 맞죠.
만약 위와 같은 경우에 사용자는 ImagViewer의 Draw()호출한 다음에 Display context에서 draw된 image를 꺼내와서, 그위에 text를 더 그리려는 작업을 하려 했다면, 전혀 엉뚱한 결과를 얻게 될것입니다.
여기에서 과연 Draw()라는 함수이름에서 보여주는 직관적인 동작은 무엇인가요? 바로 drawing입니다.
즉, 만약 ImageViewer의 성능을 높이기 위해서 flag 설정을 하여 Lazy update을 구현 할 생각이었다면, 다른 이름을 사용하는게 옳습니다.
많은 방법이 있죠. requestDraw나, Invalidate(windows에서 많이 사용하는 용어죠), SetRenderingTimeDrawEnabled() 등 API이름에서 해당 목적이 명확하게 나오도록 하는 것이 더 낳았을것 입니다.
framework은 직관성을 유지 하면서, 일반적인으로 예상할 수 있는 동작을 기본 동작으로 하고, 다양한 옵션을 제공하는 방향으로 되어야 한다고 생각합니다.
다음과 같은 항목들이 대표적인 사항들일 것입니다.
- Sync call와 Async call의 구분
- lazy update와 immediate update
- resource 소유권에 대한 규칙
이와 같은 부분들이 아마도 platform에서 일관되게 동작하도록 정의하고 정리하기 어려운 부분일 것입니다.
특히 Resource 라고 표현한 객체의 lifecycle 관리 즉, 객체를 생성한 주체와 삭제하는 주체에 대한 것은 아주 자주 접하는 케이스이기 때문에, 명확히 하고 일반적으로 방법(개발자들이 흔히 예상할 수 있는 )으로 제공하는 것이 맞습니다.
아주 특별하고 획기적인 기능이라 하더라도, 그게 일반적인 상식이나 생각에서 벗어난다면, 좋지 못하겠죠.
몇가지 예를 들어보겠습니다.
DDD::CreateObj()
{
ABC* obj = new ABC;
Dock.Set(obj);
}
라는 아주 간단한 code가 있다고 합시다. 이 경우 obj는 누가 관리하는 것이 맞을까요?
1. DDD class에서 해야한다.
2. Dock에서 하는 것이 맞다.
3. 가비지 collector에 맡긴다.
또 다른 예를 들어보자면, array와 list가 있다고 합시다.
C++로 SoCool이라는 프래임워크 를 개발하고 있는 탐 이라는 사람이 있다고 합시다.
탐은 Array 와 List 를 만들때, "왜 다른 언어들은 index를 0 부터 시작하지? 그래서 개수가 10개 이면 0~9까지 카운트 되게 했지? 이건 일반인들은 상상도 할 수 없는 일이야" 라고 생각했습니다.
이 생각을 바탕으로 하나씩 짚어봅시다.
Array의 index는 프로그래밍 세계에서는 일반적으로 0부터 시작하는 반 열린 집합 형태로 표기 됩니다.
즉 개수가 10개인 array는 0<= index<10 으로 표현하는 것이 일반적인 표현 방식이죠. 이유는 memory와 관련 있는데, array 의 변수명은 그 변수가 잡고있는 memory의 시작 위치를 표시합니다. 따라서 Array[0]의 표시는 address + 0의 위치 시작 위치로 표시되는 것이죠.
이를 실생활에서 처럼 첫번째 item이니까 Array[1] 로 표현 한다면, address + 1 -1 과 보정을 하거나 아니면, 메모리상 address +1 위치 부터 data를 사용하도록 해야 합니다.
이 두가지 모두 Computer 구조랑 잘 맞지 않는 표현 방법이 되죠.
(생각하기 나름이긴 하지만 시스템 이나 메모리구조 등을 이해하고 있는 많은 프로그래머들의 일반적인 생각은 이렇다는 것입니다.)
이러한 이유에 의해서 인지는 모르지만, 결과적으로 보면, 아래와 같은 의미가 되었습니다.
개념 상 뭐 어떻게 생각하느냐에 따라 다를 수 있습니다. 하지만 수학의 집합 형태로 생각 해봅시다.
우선 수학에서 어떤 집합을 표현 할때 열린 집합, 닫힌 집합, 반 열린 집합 등의 명칭으로 분류를 하기도 합니다.
무슨 의미냐 하면, 1 부터 9까지의 숫자들의 모음(1과 9을 포함하는)을 위에서 예기한 집합으로 표현하자면 다음과 같습니다.
1. [열린집합] 0<집합<10 즉, 경계인 0을 포함하지 않고, 10을 포함하지 않는 집합.
2. [닫힌집합] 1<=집합<=9 즉, 경계인 1을 포함하고 , 9는 포함하는 집합.
3. [반 열린 집합] 1<=집합<10 즉, 경계인 1을 포함하고 , 10을 포함하지 않는 집합.
4. [반 열린 집합] 0<집합<=9 즉, 경계인 0을 포함하지 않고 , 9는 포함하는 집합.
Computer programming 에서는 memory addressing 방식 때문에 자연스럽게 3번 형태를 띄게 되었습니다.
Computer에서는 시작을 1부터가 아닌 0부터 시작하고 있기 때문에, 10개 라는 의미가 0~9까지를 의미하는 것이 되었기 때문이죠.
라고 생각하고 list의 index를 1부터 시작하는 것으로 제공하기로 하였습니다.
만약 list 의 index를 1부터 시작하겠다고 하는 것은, "2번 닫힌 집합으로 표현 해라." 라는 의미가 되는 것입니다. 즉, 10개가 있다는 의미가 1~10까지 닫힌 집합으로 표현해야 한다가 되는것이고, code로는 1<= index <=10 이라는 의미가 되는 것이죠.
array에서는 0<=index<10 이고, list에서는 1<=index<=10 이라고 표현 하면, 결국 data의 집합을 표현 하는 자료 구조인데 한 언어나 프래임워크 내에서 서로 다른 규격을 제시 하는 것이 됩니다.
이런 비슷한 기능을 제공하는 class들이 각각 다른 기준의 동작을 유도하는 것은, 어플리케이션 개발에 수많은 혼선을 줄 가능성이 있습니다.
단순히 list와 array의 차이가 아니라, 어떤 API들도 직관적으로 접근 할 수가 없고, 숨은 의도들을 파악해야 하는 상황이 발생하죠.
위의 경우, 어플리케이션 개발자는 "아 array는 memory 블럭을 잡는 것이니까 index는 0 부터 사용해야 하고, list는 동적이기 때문에 메모리 블럭과 관련이 없어 그러니까 프로그래머가 아닌 일반적인 사람 입장에서 생각해서 index가 1부터 인거야, 이건 꼭 외워야 해. 나중에 index array와 list간의 index변환도 고려 해야 하니까. 이런게 또 뭐가 있을까? Stack ? Stack은 어떻게 동작하지?" 라는 생각을 하게 할겁니다.
전반적인 S/W 도메인 별로 보면 여러 주제들 예를 들면 Synchronous와 Asynchronous, mutable, thread, lock, ipc, data binding, runtime, reflection 등등, 이러한 요소들을 프레임워크와 언어에 대해서 일관성, 과 일반적이고 상식적인 기본 동작과 컨셉을 가져가야 할 것인지를 잘 정리하고 제공되어야 좋은 플랫폼 또는 프래임워크 라고 할 수 있을 것입니다.
이런 이유에서 규모가 큰 S/W 회사들은 , 프래임워크 구축하려 할때, 기존의 언어로 새로운 패러다임을 반영하기 힘들거나 , 프래임워크에서 제공하려는 기능이 기존의 언어로 표현이 힘들 경우, 새로운 언어들을 만들기도 하는 것입니다. (C#, Java가 대표적이랄까요?)
'개발 Note > UI Framework 개발하기' 카테고리의 다른 글
Core Image Concepts (0) | 2011.09.20 |
---|---|
stdafx.h 를 없애보자! (0) | 2011.08.18 |
CoreAnimation in iOS (0) | 2011.07.13 |
key - value 관리 (0) | 2011.06.08 |
[애자일] 릴리즈 계획 과정에서 가장 중요한 것들 (0) | 2010.10.29 |