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

델파이 강좌/문서
Delphi Programming Tutorial&Documents
[41] 11월 세미나 두번째 예제
주정섭 [jjsverylong] 7025 읽음    2004-10-21 13:29
콤보박스에 이중 문자열값 보관하기(부제:쪼잔한 기능의 콤포넌트 안 만들기 방법).

어떤 콤포넌트에 몇몇 새로운 기능을 추가하거나 변경하고 싶을 때, 그 콤포넌트에서 상속받은 새로운 콤포넌트를 만들어서 기능을 추가하라고 델파이 매뉴얼에서는 권장한다.

그런데, 이 방법은 사실 문제가 많다. 몇몇 쪼잔한 기능 변경 추가를 위해서, 수 많은 콤포넌트를 만들어 개발환경에 등록하면 오히려 불편하다. 다른 개발자들에게 팔 목적이 아니라면, 콤포넌트를 가급적 만들지 말라고 권하고 싶다.

나는 DOS 시대의 순수 소스 코딩과 Window 시대의 비주얼 툴 모두를 사용해 봤다. 두 방식 모두 일장일단이 있는데, 어떤 경우 순수 코딩 방식이 월등히 더 좋은 경우가 많다.

그런 예제 중 하나로, 쪼잔한 기능의 콤포넌트 만들지 않기 방법에 대해서 논해 보려 한다.

콤보박스(TComboBox)는 Items라는 속성으로 문자열들을 보관한다는 것은 다 알것이다. 그런데 어떤 경우, 문자열을 2차원 배열 형태로 보관해야 할 때가 있다. 예를 들어 콤보박스가 사용자에게 표시하는 선택 가능한 옵션과, 실제로 프로그램 내부에서 저장하는 값이 달라야할 경우이다. 이렇게 하는 이유는 사이즈가 작은 데이타형태로 옵션값을 보관하므로서 메모리 절약이라든가, 디스크 읽기 속도 증가 등의 여러 효과가 있기 때문이다.

다음과 같이 사용자에게 보여주는 값과 실제 내부적으로 저장할 값이 다른 경우라고 가정해 보자.

보여주는 문자열     저장하는 문자열
------------------------
VISA                            VI
MASTER                        MA
EXPRESS                        EX
DINERS                         DI

첨부한 BadCombo 프라젝트는 TComboBox의 AddItem 메서드를 사용하여, 사용자에게 보여줄 문자열과 저장 문자열값을 함께 저장하고 이를 얻어내는 방법을 보여 준다.

unit BadComboFm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    ComboBox1: TComboBox;
    StaticText1: TStaticText;
    StaticText2: TStaticText;
    procedure FormCreate(Sender: TObject);
    procedure ComboBox1Change(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  ComboBox1.AddItem('VISA', TObject(PChar('VI')) );
  ComboBox1.AddItem('MASTER', TObject(PChar('MA')) );
  ComboBox1.AddItem('EXPRESS', TObject(PChar('EX')) );
  ComboBox1.AddItem('DINERS', TObject(PChar('DI')) );

  ComboBox1.Style := csDropDownList;
  ComboBox1.ItemIndex := 0;
end;

// 콤보박스의 onChange 이벤트.. 선택한 옵션의 저장 문자열값 얻기
procedure TForm1.ComboBox1Change(Sender: TObject);
begin
  StaticText1.Caption := 'ComboBox selected Item''s value : ' +
      ComboBox1.Items.Strings[ComboBox1.ItemIndex] + ' ';
  StaticText2.Caption := 'ComboBox selected object''s value : ' +
      PChar(ComboBox1.Items.Objects[ComboBox1.ItemIndex]) ;
end;

end.

상당수의 델 개발자들은 이런 경우 이런 방법을 사용할 것이다. 그런데 이 방식은 문제가 있다. 저장 문자열값을 저장하고 얻어오기 위해서 매번 강제 형변환이 필요하다는 것이다.

// 추가할 때 강제형변환 필요
ComboBox1.AddItem('VISA', TObject(PChar('VI')) );

// 값을 얻어낼 때 강제형 변환 필요.
Char(ComboBox1.Items.Objects[ComboBox1.ItemIndex]) ;

전통적인 콤포넌트 상속 방법을 사용하여 강제 형변환이 필요없는 깔끔한 코드를 만들려면, TComboBox 에서 상속받은 TDualComboBox 콤포넌트 클래스를 만들고, 문자열 두개를 동시에 인수로 전달 받는 AddItem 메서드와, 두번째 문자열값을 얻어오는 Strings라는 속성을 추가해야 할 것이다. 즉 대략 다음과 같은 콤포넌트를 만드는 것이다.

// 콤포넌트 유닛에서...
TDualComboBox = class(TComboBox)
private
  strList:TStringList;
  function GetStrings(Index: Integer): String;
public
  procedure AddItem(str1, str2: string); overload; // 문자열 두개 동시 추가 메서드
  constructor Create(AOwner:TComponent); override;
  destructor Destroy; override;
  property Strings[Index: Integer]: string read GetStrings; // 두번째 문자열 값 얻어내는 속성
end;

그런데, 이 방법은 처음 언급했듯이, 쪼잔한 기능 때문에 새로운 콤포넌트를 만들어야 하는 안 좋은 경우이며, 더 큰 문제는 새로 시작하는 프라젝트가 아니라면, 다시 말해서 기존의 여러 폼에서 표준 TComboBox 콤포넌트를 많이 사용하고 있고, 이 콤포넌트들 모두에 이중 문자열 저장 기능을 추가하려면 문제가 심각해 진다는 것이다.

여러 폼에 있는 수 많은 TComboBox를 TDualComboBox로 대체하는 작업을 수차례 행해야 하기 때문이다. 내 경험에 의하면 이 과정 중에서 항상 실수가 일어난다는 것이다. 이전 콤포넌트의 주요 속성값을 대체하려는 콤포넌트에 제대로 설정해주지 않아서 오동작하는 경우가 발생한다는 것이다. GExperts라는 툴은 콤포넌트 대체 기능을 제공하긴 하나, 완벽하지는 않다.

더 좋은 방법은 콤포넌트가 아닌 듯하면서도 콤포넌트 행세를 하고, 기존 폼의 TComboBox를 대체할 필요도 없으면서도, 새로운 메서드를 추가하는 방법이다. 즉, 기존 소스의 많은 변경없이 내가 원하는 기능만 TComboBox에 추가하는 것이다.

다음 소스는 이런 방식으로 변경(리팩토링)한 것이다.

// GoodCombo.pas
type
  TForm1 = class(TForm)
    ComboBox1: TComboBox;
    StaticText1: TStaticText;
    StaticText2: TStaticText;
    procedure ComboBox1Change(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  ComboBox1.AddItem('VISA', 'VI');
  ComboBox1.AddItem('MASTER', 'MA' );
  ComboBox1.AddItem('EXPRESS', 'EX' );
  ComboBox1.AddItem('DINERS', 'DI' );

  ComboBox1.Style := csDropDownList;
  ComboBox1.ItemIndex := 0;
end;

procedure TForm1.ComboBox1Change(Sender: TObject);
begin
  StaticText1.Caption := 'ComboBox selected Item''s value : ' +
      ComboBox1.Items[ComboBox1.ItemIndex] + ' ';
  StaticText2.Caption := 'ComboBox selected object''s value : ' +
      ComboBox1.Strings[ComboBox1.ItemIndex] ;
end;                                                                                                               

이전 소스와의 큰 차이점은 더 이상 복잡한 형변환이 필요 없다는 것이다. TComboBox에는 AddItem(str1, str2:String) 라는 메서드와 Strings라는 속성이 추가되었지만, 기존 소스에서 큰 변화는 없다. 기존 TComboBox를 다른 콤포넌트로 대체할 필요도 전혀 없다. 표준 콤보박스를 사용하는 기존 코드들은 호환성을 유지한다. 단지 이중 문자열을 사용해야할 부분에서만 추가 코드가 필요할 뿐이다.

폼 소스 파일의 상단 부분을 보면, 다음과 같은 코드가 있다.

TForm1 = class(TForm)
    ComboBox1: TComboBox; // 이 부분을 유심히 보라.

콤보박스의 타입은 여전히 TComboBox이다. TDualComboBox같은 다른 콤포넌트로 대체된 것이 아니다. 기존 소스의 변경을 최소화하면서, 원하는 기능을 기존 콤포넌트에 추가하는 이 방법을 나는 종종 사용한다.

혹자는 TComboBox가 정의된 델파이 소스를 수정하고 새로 컴파일 한 것이 아닌가 의심하는 사람도 있을 것이다. 델파이 자체 소스를 변경하면 후일 엄청난 혼란이 발생하므로 그런 방법을 나는 거의 사용하지 않는다. 따라서 델파이 자체 라이브러리 소스를 전혀 변경하지 않았음을 밝혀 둔다.

사실 이를 구현하는 팁은 매우 간단하지만, 이 방법을 아는 사람은 많지 않은 것 같다. 이 비밀은 이번 세미나에서 발표토록 하겠다. 혹시 이 비밀(?)을 눈치챈 사람은, 절대로 답변글이나 댓글로 달지 마시고, 반드시 이메일로 연락을 주시라. 예전 나의 강좌를 유심히 읽어본 사람이라면 이 비법을 간파했을 수도 있다.

비밀을 눈치챈 사람에게는 모든 방법(?)을 동원하여 후사할 작정이다.

http://cafe.daum.net/delphinegong

+ -

관련 글 리스트
41 11월 세미나 두번째 예제 주정섭 7025 2004/10/21
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.