'구조적 프로그래밍' 검색 결과 4건

  1. 2009.07.22 OOP와 생산성 - 다형성의 문제점? (12)
  2. 2009.06.21 OOP란 조건문(if)을 줄이는 것 (29)
  3. 2009.04.02 기획자를 위한 분석 및 설계 기법 (5)
  4. 2008.12.11 모드 없는 소스코드 (4)

Object Oriented Programmer's Productivity 를 읽고 씁니다. 글에서 주장하는 바를 간략히 요약하자면 이렇습니다:

  1. OOP 하면 동적 디스패치(dynamic dispatch, 대충 쉽게 말해서 다형성)가 가장 먼저 떠오르는데,
  2. 동적 디스패치는 코드를 읽기 어렵게 만들기 때문에 위험하고, OOP는 이를 장려하기 때문에 결국 문제가 된다.
  3. OOP의 장점은 다형성에 있다기 보다는 캡슐화에 있는데, 그 이유는 구조적 프로그래밍에 비해 전역 변수를 덜 쓰도록 장려하기 때문이다.

1)번은 동의하는 바이고(OOP란 조건문을 줄이는 것 참고), 2,3번에는 동의하지 않습니다. 하나씩 따져보면


1. "동적 디스패치는 코드를 읽기 어렵게 만든다"는 주장에 대해

이 문제는 정적인 프로그램(즉 소스 코드)과 동적인 프로세스(dynamic process) 사이의 간극이 넓어지면 프로그램을 분석하기가 어려워진다는 문제의 한 가지 사례인데요(혹시 부연 설명이 필요하시면 Goto 문과 AOP, 그리고 Subtext 참고), 간단히 말해서 코드를 읽다 말고 실제로 무엇이 실행되는지 파볼(drill-down) 필요가 생기지만 않는다면 문제될 것이 없습니다. 그러니깐, 설계를 잘 하면 된다는 말입니다.

이 맥락에서 올바른 설계란... 대충 중요한 것을 꼽자면 첫째, 일반화(generalization)가 올바르게 되어 있고(즉, LSP 혹은 contract - design by contract에서 말하는 - 를 잘 지키고 있고), 둘째, 클래스 및 인스턴스의 이름이 적절히 지어져 있으며(intention revealing), 3) 해당 코드의 주변부와 추상화의 수준(level of abstraction)에 일관성이 있는 것을 말합니다.

이런 상황이라면 특정 오퍼레이션(operation)에 대한 구현 코드(method)의 내용이 무엇인지 궁금해할 일이 없습니다.

사실 동적 디스패치가 코드 읽기를 어렵게 만든다는 식의 문제 제기라면 글쓴이가 좋아하는 Haskell(함수형 언어의 일종입니다)도 문제가 되는데요, 왜냐하면 higher-order programming이라는 것 자체가 함수를 인자로 넘기거나 함수를 반환값으로 받아서 쓰는 것이고 이렇게 되면 결국 늘상 일어나는 일이 동적 디스패치거든요.


2. "OOP의 장점은 다형성에 있다기 보다는 캡슐화에 있는데, 그 이유는 구조적 프로그래밍에 비해 전역 변수를 덜 쓰도록 장려하기 때문이다"는 주장에 대해

다형성은 문제이고 캡슐화가 진정한 장점이라는 얘긴데 그럴거면 OOP라는 것이 있을 이유가 없고 그냥 모듈화 프로그래밍(modular programming)이라는 말이면 충분하겠죠.

캡슐화라는 것은 구조적 프로그래밍이나 객체지향 프로그래밍과 직교적(orthogonal)인 개념으로 보는 것이 자연스럽습니다. 대부분의 구조적 언어는 모듈화 프로그래밍을 지원하고 있고, 객체지향 언어 또한 마찬가지라서 프로그래머가 어떻게 잘 쓰느냐에 따라 캡슐화가 잘 될 수도 있고 아닐 수도 있습니다.

이를테면 코드와 데이터를 묶으려면 구조체에 함수 포인터 넣어두고, 각 함수는 첫번째 인자로 자신이 속한 구조체를 받으면(python의 self와 유사) 되는 것이죠. 구조체가 없거나 함수 포인터가 없는 언어(구조적 언어의 요건은 모두 갖추었으나 함수 포인터가 없는 언어로는... 이를테면 QBasic이나 QuickBasic이 그렇습니다)라면 prefix나 postfix로 그룹핑을 하는 관습을 만들면 그만입니다(실제로 널리 쓰이던 관습입니다. namespace 개념이 없거나 약했거든요).

뭐 구현 가능성에 대해서는 그렇다치고, 구조적 프로그래밍에 비해 객체지향 프로그래밍이 캡슐화를 좀 더 강조하고 있다는 주장도 있는데 이 또한 별 설득력이 없습니다(시스템을 모듈로 나누는 기준에 대하여 참고. 추측컨데 저자가 구조적 프로그래밍을 충분히 경험해보지 못한 것이 아닌가 싶습니다).

저자는 캡슐화의 단적인 예로 전역 변수(global variable) 문제를 들고 있는데 이에 대해서는 의존의 자기유사성을 참고하시면 좋겠습니다. 전역 변수 문제라는 것은 사실 스케일의 차이만 있을 뿐 일반적인 의존성 문제의 하나이고, 객체지향 프로그래밍을 하건 구조적 프로그래밍을 하건, 전역 변수가 있건 없건 항상 존재하는 문제이고 신경 써야 하는 문제입니다.

게다가 자바처럼 전역 변수라는 개념이 아예 없는 언어에서도 전역 변수 문제는 여전히 존재할 수 있고(전역 변수가 없지만 전역 변수 문제는 존재한다는 표현이 좀 이상하지만 뭐 맞는 말입니다) 실제로 많은 개발자들이 이 문제로 허덕이고 있습니다. 이에 대해서는 모드 없는 소스코드 중 "모드와 숨은 변수들" 섹션 참고하시기 바랍니다.

OOP 덕에 전역 변수가 많이 줄었다는 것은 아쉽게도 착각입니다. 일례로 Singleton Pattern을 보세요. 이건 사용이 장려되고 있는 (디자인 패턴이라는 탈을 쓰고 나타난) 전역 변수 아닌가요?


3. 결론 및 부연

첫째, 동적 디스패치가 일어나는 부분이 읽기 어렵다면 그건 설계/코딩을 잘못했기 때문입니다. 아무리 좋은 도구라도 쓰는 사람이 잘 쓰지 못하면 문제가 생기는거죠.

둘째, 캡슐화라는 것은 그걸 ADT(Abstract Data Type)라고 표현했건, 모듈이라고 표현했건 간에 객체지향프로그래밍과 무관하게 예전부터 있어 왔던 개념과 별 다른 점이 없습니다. 애초에 모든 (쓸모있는) 프로그래밍 언어는 조합(combination)과 추상화(abstraction) 요소를 갖추고 있는데(Structure and Interpretation of Computer Programs), 객체지향언어도 예외는 아닌 것이죠.

저작자 표시
신고

OOP에 대해서, 아는 사람은 다 알만한 이야기 및 이 글에서 인용하고 있는 大山님의 글 객체지향 프로그래밍에 대한 오해와 진실 1편, 2편을 읽고 씁니다(3년 전 글입니다). 딱히 관련 글이라기 보다는 읽고 떠오르는 생각들 몇 가지 + 제가 평소에 가지고 있던 생각을 늘어 놓으려고 합니다.

OOP를 바라보는 (유일하게 올바른 관점이 아닌 다양한 관점 중) 한 가지 관점에 대해 쓰고자 합니다. 하나의 대상을 한 측면에서만 보는 것 보다는 여러 측면에서 보면 좀 더 그 대상에 대해서 잘 알 수 있을 것이라고 생각하기 때문입니다. 제가 제시하고자 하는 관점은 다음과 같습니다:

“OOP는 무엇을 제거 혹은 대체하고자 하는가?”

부연하자면 1) 레퍼런스는 포인터에서 산술연산을 제거하였고, 2) 구조적 프로그래밍은 GOTO 류의 흐름제어 방식을 제거하였는데, 3) OOP는 무엇을 제거/대체하는가에 대해 쓰고자 합니다.

 

포인터 - 산술연산 = 레퍼런스

예를 들어 자바나 C#의 레퍼런스(reference) 개념을 살펴보죠. 레퍼런스는 보안성, 편의성, GC 효율성 등을 향상시키기 위해 기존의 포인터 개념으로부터 산술연산(arithmatic operations)을 제거하였습니다. (결과적으로 보안성, 편의성, GC 효율성 등이 향상 되었는지 아닌지에 대한 판단은 각자 알아서)

포인터는 전지전능한 주소지칭 수단인데, 사실은 전지전능한 주소지칭 수단이 없어도 모든 프로그램을 작성할 수 있다는 것을 알게 된 것이죠(성능 이슈 등을 제외한다면).

 

스파게티 - GOTO = 구조적프로그래밍

또 다른 예로, 구조적 프로그래밍은 프로그램의 흐름을 제어하는 방법 중 goto를 제거(다익스트라 식으로 표현하자면 의미있는 프로그래머 독립 좌표계 – programmer independent coordinates - 를 훼손하는 장치들을 제거)하였습니다. 구조적 프로그래밍에서는 다음 세 가지 수단만으로 모든 프로그램의 흐름 제어를 구현해냅니다:

  • 순서(sequence) – 쉽게 말해서 코드가 위에서 아래로 실행되는 것
  • 선택(selection) – 쉽게 말해서 if와 switch
  • 반복(iteration) – 쉽게 말해서 while(혹은 do, until, for 등)

조건문(if)과 결합된 goto 문은 전지전능한 흐름제어 수단인데, 사실은 전지전능한 흐름제어 장치가 없어도 모든 프로그램을 작성할 수 있다는 것을 알게 된 것이죠(성능 이슈 등을 제외한다면).


구조적프로그래밍 - IF = OOP

드디어 본론인데요, OOP는 뭘 제거(혹은 대체)하였을까요? 혹자는 OOP가 구조적 프로그래밍을 대체한다고 말하는데 1/3 쯤 맞는 표현이라고 생각합니다. OOP는 구조적 프로그래밍의 세 가지 흐름 제어 수단(sequence, selection, iteration) 중에서 선택(selection) – 즉 if 문 – 을 제거합니다.

구조적 프로그래밍에 대한 설명에서 “조건문(if)과 결합된 goto 문은 전지전능한 흐름제어 수단”이라고 표현하였는데 이 중 goto는 제거되었으나 if는 살아남은 것이 구조적 프로그래밍이라면 if 까지도 제거하고자 하는 흐름을 OOP라고 보는 관점이 있을 수 있다는 것이죠.

오해의 소지가 있으니 잠깐 부연하자면, 모든 if를 제거해야 진정한 OOP라는 류의 주장은 아닙니다. 이에 대해서는 글 뒷부분에서 좀 더 이야기하도록 하겠습니다(진정한 OOP 따위 대체 뭔가요, 먹는거임? 우걱우걱).

그럼 조건문을 무엇으로 대체하고 있나요? 다형성(polymorphism)이죠. 그냥 다형성이라고 하면 너무 광범위하죠. 이를테면 파라메터 다형성(parametric/parameterized polymorphism)은 OOP 보다는 Generic Programming과 관련이 있습니다. OOP의 핵심이라고 하는 다형성은 특히나 서브타입 다형성(subtype polymorphism)을 말합니다. 이후에는 그냥 다형성이라고만 쓰겠습니다.

그럼 다형성이 조건문을 어떻게 대체하나요? 조건문이란 애초에 선택(selection)을 위한 것인데, 여기에서 선택이란 특정 변수의 값에 따라 이후에 실행할 코드가 달라지도록 하는 것을 말합니다. if 문에서는 조건절의 boolean 값에 따라 선택이 수행되는 것이고, 다형성에서는 메시지(message)에 담긴 파라메터(메시지를 받는 대상 개체도 파라메터로 칩시다. 파이선 self 마냥)에 따라 선택이 수행됩니다.


4. 다형성과 의존성 역전(DI – Depedency Inversion)

if를 다형성으로 대체하는 것이 정말 OOP를 특징지을만큼 중요한가요? 네, 전 그렇다고 생각합니다. 이쯤에서 밥 삼촌(Uncle Bob)을 인용해주어 (올바른) 권위에의 호소를 한 번 시도하도록 하겠습니다:

프로그램이 어떤 언어로 작성되었는지는 중요치 않습니다. 만약 의존성이 역전되어 있다면 이는 객체지향적 설계인 것입니다(It doesn't matter what language a program is written in. If its dependencies are inverted, it has an OO design).

--Robert Cecil Martin, Agile Software Development

if 얘기 하다말고 갑자기 왜 의존성 역전(DI) 떡밥이 나왔을까요? 그 전에, 애초에 의존성 역전이라고 할 때 이 역전이란 뭘 뒤집었다는 뜻일까요? David Parnas 큰형님이 지금으로부터 무려 37년 전에 쓴 논문에 답이 나옵니다:

첫째, 하위 계층의 서비스를 활용함으로써 시스템의 일부가 이득을 취했다(간결해졌다). 둘째 상위 계층을 들어내더라도 여전히 유용하고 사용 가능한 산출물(하위 계층 서비스들)이 남는다. (…중략…) 만약 하위 계층의 모듈이 상위 계층의 모듈을 사용하게끔 설계를 했다면 올바른 위계를 얻어낼 수 없었을 것이고 시스템의 일부만 들어내기가 훨씬 더 어려웠을 것이며, 계층이라는 말 자체가 의미를 잃게 될 것이다(First, parts of the system are benefited (simplified) because they use the services of lower levels. Second, we are able to cut off the upper levels and still have a usable and useful product. (..omitted...) If we had designed a system in which the "low level" modules made some use of the "high level" modules, we would not have the hierarchy, we would find it much harder to remove portions of the system, and "level" would not have much meaning in system).

--On the Criteria to be used in Decomposing Systems into Modules

시스템을 모듈화할 때 계층 간의 위계를 명확히 나누고 상위 계층이 하위 계층을 사용(즉 의존)하도록 만들어야 한다는 주장이며 이는 구조적 프로그래밍 혹은 모듈화 프로그래밍의 핵심 설계 원칙 중 하나입니다. 여기에서 상위 계층이란 좀 더 추상적인(abstract) 계층을 말하고 하위 계층이란 좀 더 구체적인(concrete) 계층을 말합니다. 그림으로 그려보면 아래와 같습니다:


모든 화살표가 위에서 아래로 내려가는 모습을 담고 있습니다. 의존성 역전이란 바로 이러한 의존관계를 뒤집는다는 의미로, 의존성 역전이 일어난 설계에서는 화살표 일부가 아래에서 위로 올라갑니다. UML로 치자면 속이 빈 삼각형 화살표(generalization 혹은 realization)로 표현합니다:


의존성 역전이 가능한 이유는 하위 계층이 상위 계층에 의존하면서도 순환 참조(circular dependency)가 일어나지 않게 만들어서 이를 통해 원래 David Parnas가 강조하던 목적(시스템의 일부를 쉽게 들어낼 수 있도록 하는 것)을 여전히 달성할 수 있게 되기 때문입니다.

순환 참조는 어떻게 끊나요? 즉, 위 그림의 속이 빈 삼각형 화살표는 언어에서 어떻게 표현될까요? 상속으로 표현합니다. 자바로 치자면 extends 혹은 implements가 되겠죠. 대표적 사례는? Observer Pattern. (다시 보니 위 그림은 별로 예쁘지 않군요. 대충 예쁜 의존성 역전의 사례를 상상해주세요)

잠깐 딴소리: 아마도 GoF 패턴책이 번역된 후로, 다른 커뮤니티는 모르겠고, 자바 개발자들 사이에서 extends는 꼬지고 implements는 좋은거라는 미신이 널리 퍼졌죠. 좀 과장하자면 구현 상속은 delegation(and/or composition)으로 대체하고 오직 타입 상속이 쵝오, 뭐 이런 얘기인데 “extends == 구현 상속”, “implements == 타입 상속”이라는 잘못된 공식이 오해의 원인이 아니었을까 추측해봅니다. 자바의 경우 “extends == 구현 상속+타입 상속”이라고 해야 맞습니다. 즉, implements이건 extends이건 타입 상속은 일어나는거죠. 단 extends를 했을 때는 구현 상속이 따라오게 되어 있는데 이 때의 문제를 잘 인지하고 해결할 수 있으면 그냥 쓰면 되는 것이죠.

이를테면 blackbox reuse vs. whitebox reuse 문제 같은 것이 있는데, 애초에 public/published 구분 잘 하고, protected 따위 안쓰고 메서드는 몽땅 public, 필드는 몽땅 private 같은 몇 가지 원칙들만 잘 지키면 문제의 소지가 크게 줄어듭니다.

다시 본론으로 돌아와서 지금까지의 얘기를 정리해보자면 이렇습니다:

  1. OOP는 구조적 프로그래밍의 selection(if/switch)을 서브타입 다형성으로 대체합니다.
  2. if를 서브타입 다형성으로 대체하면 하위 계층 뿐 아니라 상위 계층 까지도 더 잘 모듈화할 수 있습니다.
  3. 이러한 생각을 표현하는 말이 의존성 역전(DI) 입니다.

 

5. OOP의 나머지 개념들은 뭔가?

그럼 캡슐화(encapulation), 정보은닉(information hiding)은 안중요한가요? 중요합니다. 다만 OOP와 별 관련 없이 원래 있던 개념들입니다.

 

6. if는 다 잡아 없애야 진정한 OOP인가?

if 뿐만 아니라 switch도 다 없애야 진정한 OOP라능. 농담입니다. --;

두 가지를 이야기할 수 있겠습니다. 첫째, 진정한 OOP 아무 짝에도 쓸모 없습니다. 그런거 추구하지 마세요. 둘째, 상위 계층의 모듈화를 저해하는 if만 제거하세요. 다른 말로 바꿔서 표현하자면, 도메인 로직을 서술하기 위한 if는 남겨두세요.

 

7. OOP에 대하여 지금 서술한 관점이 킹왕짱인가?

저는 그렇다고 생각합니다. 하지만 “제일 좋은 관점”이랑 “완전한 관점”은 다른 것이죠. 제일 좋은 관점이라고 믿고 있기는 하지만 이 관점만으로 밀기엔 불충분하고 생각도 덜 다듬어진 것 같습니다. 따라서 여러 관점을 머리 속에 담아두고 그때그때 가져다 쓰는 식으로 생각하고 있습니다.


신고

예전부터 고민하던 문제인데 자꾸 미루게 되어서 일단 질러놓고 봅니다. 연재 형식으로 쓰려고 하며, 예상 독자는 게임 기획자나 웹 기획자 혹은 기획에 관심있는 개발자 입니다. 주제는 아래와 같으며 순서나 일정은 미리 정하지 않았습니다.

  • 복잡한 논리의 흐름을 몇 가지 기본적인 단위로 분해하는 방법
  • 거대한 개념을 작은 단위로 나누는 다양한 방법
  • 구체화되지 않은(혹은 의도적으로 구체화하지 않은) 매우 추상적인 개념을 효과적으로 다루는 방법
  • 서로 다른 여러 개념 사이의 공통점을 찾아내는 방법
  • 생각을 정리하기 위한 정보 시각화 방법
  • 효과적인 의사소통을 위한 정보 시각화 방법
  • 특정한 기획 요소의 변화가 다른 기획 요소에 미치는 영향을 추적하고 관리하는 방법

거의 모든 주제가 “논리적이고 체계적인 사고”에 대한 것입니다. 따라서 독자께서 이런 생각을 하실 수도 있습니다:

기획은 논리만으로 되는 것이 아니다. 기획자는 사용자의 이성적 측면 뿐 아니라 감성적 측면도 고려해야 한다.

이러한 의문에 대해서는 두 가지 생각을 가지고 있습니다.

첫째, 기획의 모든 면을 다루는 것은 이 연재의 목적이 아닙니다. 그러기에는 제 지식이나 경험이 턱없이 부족합니다.

둘째, 감성과 논리는 대치되는 개념이 아닙니다. 논리라고 하면 보통은 ‘이성’에 대한 것으로 한정지어 생각하는 경향이 있는데 감성이나 도덕에도 논리가 있습니다. 감성적 측면을 다루는 능력이 진정 중요하다고 생각한다면 인간의 감성을 지배하는 논리를 파악하고 이를 체계적으로 다루는 기술을 연마해야 한다고 생각합니다. 이 중 감성의 논리를 파악하기 위해 필요한 내용(심리학, 사회학, 생물학, 미학 등)은 아마도 이번 연재의 범위에 포함되지 않을 것이고, 파악된 논리를 체계적으로 분석하고 활용하기 위해 필요한 내용을 주로 다룬다고 보면 될 것 같습니다.

이런 종류의 글을 쓰기에 더 적합할 것 같은 분들이 주위에 좀 계셔서 부담스럽지만 스스로 공부도 할겸 제가 먼저 깝쭉 나대보려고 합니다. ㅋ

앞으로 연재가 진행되는 동안 댓글로 의견/제안/질문 등을 남겨주시면 큰 도움이 되겠습니다. :-)

신고

스터디를 하면서 속으로:
프로그래밍에는 어떻게 적용할 수 있을까? 서비스 기획에는 어떻게 적용할 수 있을까? 조직에는 어떻게 적용할 수 있을까?
등등을 생각하고 있습니다. 알고 있는 지식들을 서로 연결짓는... 일종의 놀이죠. 개인적으로 이 방법에서 많은 도움을 받고 있습니다. 김창준님께 들은 이야기들, 개인위키를 쓰면서 생긴 습관 등이 두루 작용한 것 같아요.


모드(mode)와 변수(variable)

직전 글에서 모드 없는 인터페이스에 대한 요약문을 썼는데요, 프로그래밍에서의 모드란 변수가 아닐까 하는 생각을 해봤습니다.

사용자 인터페이스에서 모드란 아래와 같이 정의됩니다:
사용자의 제스처에 대해 인간-기계 인터페이스가 다음의 두 조건을 만족시킬 때, 그 인터페이스는 모드적이라 할 수 있다. (1) 인터페이스의 현상태가 사용자의 주의 소재가 아니며, (2) 인터페이스가 하나의 제스처에 대해 시스템의 현상태에 따라 달리 반응한다. --p54
프로그래밍에서의 모드란 아래와 같이 정의할 수 있겠습니다:
소스코드가 다음의 두 조건을 만족시킬 때, 그 소스코드는 모드적이라 할 수 있다. (1) 변수의 현재 값이 개발자의 주의 소재가 아니며, (2) 하나의 모듈(문장, 표현식, 함수, 객체, 서브시스템 등)이 변수의 현재 값에 따라 달리 작동한다.
이 정의에 의거하여 "모드 없는 소스코드"를 만들려면 아래와 같은 노력들을 해볼 수 있겠습니다:
  • 함수의 길이가 짧아야 합니다. 함수의 동작에 영향을 주는 요인들(파라메터, 지역변수, 인스턴스 변수, 전역변수 등)의 상태를 머리속으로 추적하는 일이 점점 어려워집니다. 함수의 길이가 짧으면 짧을수록 (1)에 해당하는 상황에 직면한 소지가적어집니다.
  • 함수에서 의존하는 파라메터/지역변수/인스턴스 변수/전역변수가 적을수록 좋습니다. 의존하는 변수의 수가 줄어들면 (2)에 직면할 소지가 줄어들 뿐 아니라, 기억할 변수의 수가 줄어들기 때문에 (1)을 회피하는 일에도 도움을 줍니다.
변수 중에서도 특히나 위험한 변수라면 주의 소재에서 멀어진 변수들이겠죠. 주의 소재에서 멀어질 가능성이 높은 것에서 낮은 순으로 줄을 세워보면 전역변수->클래스변수->인스턴스변수->지역변수 및 파라메터 순이 될 것입니다. 이 관점에서 보더라도 역시 전역 변수는 제거 대상 1순위 입니다.

이 예시에서는 함수라는 단위를 다루고 있지만 의존(dependency)의 자기유사성(self-similarity)에서와 비슷한 방식으로 더 큰 단위나 더 작은 단위에 적용해볼 수 있을 것입니다.

(Jef Raskin이 모드 없는 인터페이스라는 이상을 추구하는 것을 보면서 순수한 함수형 언어가 아련히 떠올랐는데 이에 대해서는 다음 기회에 써볼게요.)


모드와 숨은 변수들

한편, 프로그래밍 언어에서 변수라고 불리는 것들만이 시스템의 상태인 것은 아닙니다. 문법상 변수는 아니지만 변수 역할을 하고 있는 숨은 변수들이 있게 마련이죠. 특히 전역 변수라는 것은 여러 가지 섹시한 옷을 입고 마치 전역 변수가 아닌 척 하면서 개발자를 유혹하곤 합니다.

싱글톤 패턴(singleton pattern)이라는 멋진 이름으로 불리기도 하고, ServletContext, ActionContext 같이 "뭐시기 Context"라는 이름으로 불리기도 합니다. 전역 변수의 둔갑술에 대한 글은 응주님의 글 시대에 따른 전역 변수의 변신을 참고하시기 바랍니다.


모드와 GOTO

마지막으로, 위에서 설명한 숨은 변수들보다 더 깊고 찾기 힘든 숨은 상태(hidden states)를 만들어내는 주범이 있으니, 바로 그 유명한 GOTO 문입니다. 다익스트라는 그 유명한 Goto Statement Considered Harmful에서 이런 말을 합니다:
We should do (as wise programmers aware of our limitations) our utmost to shorten the conceptual gap beween the static program and the dynamic process, to make the correspondence between the program(spread out in text space) and the process(spread out in time) as trivial as possible.

우리는(자신의 한계를 잘 인식하고 있는 현명한 프로그래머로서) 정적인 프로그램과 동적인 프로세스 사이의 개념적 격차를 줄이기 위해 최선을 다해야 한다. 텍스트 공간 상에 펼쳐져 있는 프로그램과 시간 상에 펼쳐져 있는 프로세스 사이의 대응을 최대한 쉽게 하기 위해서다.
프로그래밍을 한다는 것은:
  • 소스 코드를 읽으면서 이 코드의 실행 결과, 즉 프로세스를 그려내거나
  • 원하는 프로세스로부터 소스 코드를 만들어내는
것인데, 소스 코드라는 것은 2차원 평면(종이 혹은 편집기 화면)이라는 공간 상에 펼쳐진 문자열인 반면, 프로세스는 시간 상에 펼쳐진 개념입니다. 따라서 이 둘 사이의 차이가 적을 수록 프로그래밍이 (읽고/쓰기에) 쉬워진다는 것입니다.

소스 코드를 읽으면서 이 코드가 어떻게 실행될지를 상상하는 일, 즉 코드를 통해 프로세스를 유추하는 일을 할 때 우리는 머리 속에 숨은 변수들을 만들어내야 합니다. 예를 들어 제어문(조건문/반복문 등)이 전혀 없는 코드는 위에서 아래로 순서대로 실행되는데, 이러한 코드는 시간과 공간 사이의 대응이 아주 쉽습니다. 머리 속에 기억하고 있어야 할 숨은 변수는 딱 하나, "지금 몇 번째 줄 까지 실행되었나" 입니다. 이 머리 속 변수를 textural index라고 합니다.

하지만 프로시저(procedure) 혹은 함수(function) 같은 개념을 도입하는 순간 변수 하나로는 프로세스를 추적하는 것이 불가능해집니다. 하지만 호출 스택(call stack) 깊이 만큼의 textual index가 있으면 해결 가능하니 큰 문제는 아닙니다.

반복문이 추가되면? 특정 반복 구간(loop나 for 문의 body)을 읽는 중에는 추가적인 변수를 머리 속에 기억하고 있어야 합니다. 바로 "지금 여기 몇 번째 돌았나?" 하는 지표죠. 이를 dynamic index라고 합니다. 물론 모든 반복(repetition)은 재귀(recursion)로 표현 가능하기 때문에 dynamic index 개념은 필요 없을 수도 있습니다만, 지금 논의에서 이게 필요하냐 안하냐는 중요한 문제가 아니므로 넘어갑시다.

지금까지 설명한 index들을 프로그래머 독립 좌표계(programmer independent coordinate system)라고 하는데 이게 바로 숨은 변수(혹은 숨은 상태)들입니다.

함수와 반복문이 있는 코드의 동적 프로세스를 추적하기 위해서는 위에서 설명한 바와 같이 몇 개의 잘 정의된 프로그래머 독립 좌표계들을 기억하고 있으면 됩니다. 그리고 이 좌표계의 값들은 동적 프로세스의 이해를 직접적으로 도와줍니다. 예를 들면 "지금 루프를 다섯번 돌았으니까, x라는 변수의 값은 5 * 2, 즉 10 이겠군" 하는 식의 추론이 가능해집니다.

한편, GOTO가 추가되면? 의미 있는 프로그래머 독립 좌표계가 사라질 수 있습니다. 물론 모종의 index를 만들 수는 있겠죠. 다익스트라는 "프로그램 실행 후 지금까지 수행된 명령어 갯수" 같은 index를 예로 들고 있습니다. 프로그램이 외부 상태에 영향을 받지 않는다면 이 index 하나만으로 프로세스의 임의의 순간을 온전히 기록할 수 있습니다. 하지만 이 index를 가지고 프로세스에 대해 의미있는 추론을 하는 것은 더이상 가능하지 않습니다. ( 어려운 얘기 같은데, 쉽게 말해서 "스파게티 코드"가 된다는 말입니다 --; )

GOTO 문이 있는 상황에서 코드의 동적 프로세스에 대한 의미 있는 추론을 하려면 온갖 index를 임시 방편으로 만들어서 머리 속에 우겨 넣어야 하는데(혹은 종이에 적을 수도 있겠습니다만), 이것들이 바로 모드 오류를 일으키기에 딱 좋은 숨은 변수(혹은 상태)들인거죠.

따라서,드 없는 소스코드라면 최소한 구조적 프로그래밍 정도는 기본으로 해주어야 한다는 얘기입니다. (헥헥 글재주가 없으니 참말 어렵게도 쓰는군요 쩝)

신고
< Newer     Older >

티스토리 툴바