Delphi Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
델파이 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
FreePascal/Lazarus
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
델마당
볼랜드포럼 광고 모집

델파이 강좌/문서
Delphi Programming Tutorial&Documents
[42] 코딩을 잘하려면 추상화(abstraction)을 잘해야 한다
주정섭 [jjsverylong] 6849 읽음    2004-10-25 13:26
제목:코딩을 잘하려면 추상화(abstraction)을 잘해야 한다.

abstraction(추상화)라는 단어를 프로그래밍에 관한 책에서 한번쯤 들어봤을 것이다. 그런데, 대다수의 책이 이 단어의 의미를 매우 장황하고 어렵게 설명하기 때문에, 이해하기 어렵다. 추상화란 간단히 이야기하자면 다음과 같이 정의할 수 있다.

"어떤 대상에 명칭(이름)을 부여하고, 그 대상의 성질을 파악하고 분류하려는 인간적 능력"

말이 어려운가? 달리 예를 들어 보자.

"감정에는 아픔, 슬픔, 기쁨, 고통 등이 있다."

아픔, 슬픔, 기쁨, 고통은 실존하는 대상(물체)이 아니다. 그러나 우리 인간들은 이 실존하지 않는 애매한 개념에 이름을 붙이고 그것들을 분류하였다. 다시 말해서, 감정이란 대표 개념의 일부로서 아픔, 슬픔, 기쁨, 고통이 존재한다는 식으로 추상화시킨 것이다.

즉 추상화는 인간 사고 과정에서 가장 기본적인 것이며, 우리는 일상생활에서 이 과정을 알게 모르게 자주 행하고 있다.

추상화 과정 중 매우 중요한 것이 이름 짓기라고 했는데, 이름 짓기를 프로그램에 비유한다면, 함수명, 변수명, 클래스명 작성등과 다를 바 없다.

프로그램 개발에서 추상화란, 복잡한 개념을 단순화하는 모든 작업을 가리키는 것이다. 여러 라인의 코드를 한꺼번에 실행하기 위해서 함수 혹은 메서드를 작성하므로, 함수(메서드) 작성은 매우 복잡한 동작(코드)들의 집합을 대표하는 새로운 명칭을 만드는 추상화 작업이라고 할 수 있다. 클래스를 작성하는 이유는 데이타(멤버 필드)와 이를 처리하는 메서드를 모아서 그 대표 명칭을 만드는 것이다. 이 모두가 바로 추상화인 것이다.

델파이 기본 콤포넌트의 하나인 TEdit는, 문자열 입력을 처리하기 위해서, 윈도우 API를 추상화시킨 클래스라고 할 수 있다. 만일 델파이에 TEdit라는 콤포넌트가 없다면, 델 개발자들은 복잡한 Windows API를 매번 호출하여 문자열 입력을 처리해야만 할 것이다.

코딩을 잘하는 개발자는 이 추상화 작업을 매우 잘하는 능력을 가진 사람이라고도 할 수 있다.

코드 리팩토링에서 이 추상화 작업이 매우 기본적이고도 중요하다. 추상화의 간단한 예를 들어 보자.

다음 코드는 어떤 TDataset(TTable, TQuery)에서 모든 레코드를 대상으로 특정 작업을 행하는 전형적인 델파이 코드이다.

// 나쁜 방식의 코드
procedure TForm1.Button1Click(Sender: TObject);
var
  csr : TCursor;
  curMark : TBookMark;
begin
  csr := Screen.Cursor;
  Screen.Cursor := crHourGlass;
  try
    Table1.DisableControls;
    curMark := Table1.GetBookmark;
    Table1.First;
    while not Table1.Eof do
    begin
      // 현재 레코드를 대상으로 중요한 집계, 혹은 기타 작업 수행
      Table1.Next;
    end;
  finally
    Table1.GotoBookmark(curMark);
    Table1.FreeBookmark(curMark);
    Table1.EnableControls;
    Screen.Cursor := csr;
  end;
end;

위 코드는 대략 다음과 같은 작업을 행한다.

1. 장시간 동안 작업을 행할 것임을 사용자에게 알려주기 위해서 커서를 모래시계 모양으로 바꾼다.
2. TDataset 처리 속도를 빠르게 하기 위해서, DisableControls 를 호출하고, 현재 레코드 포인터를 보관한다.
3. while 안의 반복문에서 모든 레코드를 대상으로 작업을 수행한다.
4. while 반복문이 끝나면, finally 블럭에서 레코드 포인터를 원래 위치로 환원하고, EnableControls를 호출하고, 커서를 원래 모양대로 복원한다.

이 코드에서 가장 중요한 부분은 3항의 while 반복문이다. 1,2,4항은 어찌 보면 3항을 처리하기 위한 유틸러티 성격의 코드이고, 후일 유지보수를 위해서 별로 유심히 봐야할 부분이 아니다. 후일 유지보수에서 중점적으로 수정하게 될 코드는 3항의 코드임이 분명하다.

그런데 의외로 1,2,4항의 코드때문에 쓸데없는 에러가 자주 발생한다는 것이다. 커서를 복원하지 않거나, EnabledControls 메서드 호출을 잊어먹거나, GotoBookmark로 원래 레코드 포인터로 환원하지 않거나하는 실수를 자주 한다는 것이다.

처음에 어떤 코드를 수행하고, 메인 작업을 행하고, 처음 코드의 부작용을 없애기 위해서 원래대로 환원하는 코드를 마지막에 두는 이런 방식의 코딩이, 바로 절차가 복잡한 코딩 방식이다. 이는 가급적 피해야 될 방식이다. 이런 방식으로 코드를 해도 무방한 경우는, 이런 코드가 이 한부분에서만 있고, 후일 다른 소스나 다른 프라젝트에서 전혀 반복하여 사용할 일이 없는, 일회성일 때만으로 제한해야 한다.

추상화를 통하여 코드를 단순화하는 상상력(?)을 동원하여 리팩토링하면, 앞 코드는 다음과 같이 변경할 수 있다.

// 좋은 방식의 코드
procedure TForm1.Button3Click(Sender: TObject);
var
  GuardObj: IGuard;
begin
  Guard(TWaitCursor.Create, GuardObj);
  Guard(TDBIterator.Create(Table1), GuardObj);

  while not Table1.Eof do
  begin
      // 레코드에 대한 중요 작업 처리
      Table1.Next;
  end;
end;

위 코드에서 화면 커서의 변경/복원은 TWaitCursor 클래스로 추상화되었으며, 레코드 포인터 복원과 DisableControls/EnableControls 메서드 호출은 TDBIterator 클래스로 추상화 되었다. 그리고 이 객체들의 파괴는 Guard패턴으로 자동화(추상화)했다. 이전 코드의 try finally 구조 제어문이 사라졌기 때문에, 메인 작업 코드가 훨씬 더 명확하게 된다.

이 코드는 예전 부산 세미나때 발표한 예제이지만, 예전 올린 글 중에서, 인터페이스 자동 파괴 개념을 이해한 독자라면, 내가 어떻게 이 코드를 만들었는지 알 것이다. 예전에 올린 글 중에서 이에 대한 소스도 있을 것이다.

내가 본 많은 델 개발자들의 소스에서, 처음에 보인 나쁜 방식의 코드를 자주 발견한다. 소스를 단순화하는 작업은 후일 디버깅에서 여러모로 유리해지기 때문에, 이를 위한 노력을 게을리하면 안된다.

뛰어난 개발자는 에러를 유발할 가능성이 있는 코드를 사전에 차단한다. 개발 과정에서 에러를 유발할 가능성이 있는 코딩 방식을 없애는 것이 여러모로 유리하기 때문이다.

후일 개발 막바지 시점에서, 한꺼번에 모든 에러를 잡겠다고 생각하는 것은 위험천만한 발상이다.

http://cafe.daum.net/delphinegong 주정섭의 델파이 강좌.

+ -

관련 글 리스트
42 코딩을 잘하려면 추상화(abstraction)을 잘해야 한다 주정섭 6849 2004/10/25
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.