본문 바로가기

Academy I/Tech Academy

[Delphi]Spring4d 강좌 3 Generic Collection Library

List, Map, Dictionary, Queue, Stack 등과 같은 컬렉션 라이브러리가 있다면 매우 코딩이 편해진다. 예전 델파이에는 이것들이 없어서, TList를 상속받거나 내부 멤버로 가지거나 해서 이들을 흉내냈다. 또는 개발자 스스로 별도의 컬렉션 라이브러리를 만들었다. 그런데,  과거의 델파이 컬렉션 라이브러리들은 요소를 참조할 때 형변환을 해야 하기 때문에 코딩이 쓸데없이 복잡했다. 그러다, 드디어 델파이 XE 이상 버전에서 지네릭이 추가되고, 더불어 진정한 지네릭 컬렉션 라이브러리가 추가되면서 이 형변환의 불편함은 사라졌다. 그러나 델파이 컬렉션 라이브러리에는 치명적인 단점이 있다. 자동 파괴가 안된다는 것이다. 

Spring4d 는 별도의 Generic Colllection Library를 제공한다. 아마도 Spring4d개발자는 델파이 자체 컬렉션 라이브러리의 불편함을 너무나 잘 알았는 듯 하다. Spring4d 개발자는 모든 코드에서 자동파괴를 염두에 두고 만든 듯 하다. 따라서 Spring4d 컬렉션 라이브러리는 기본적으로 자동 파괴를 지원할 뿐더러, 훨씬더 다양한 기능을 가진다. Spring4d의 자동파괴가 얼마나 편한지 알아보자.

델파이로 새로운 어플을 하나 만들고 메인폼의 implementation 라인 밑에 다음 라인을 작성하자.

// Spring4d와 델파이 지네릭 컬렉션 라이브러리 유닛 
uses Spring.Collections, Generics.Collections;

type
  TMyObject = class
 // 중략
  end;

메인폼의 OnCreate 이벤트에 다음 라인을 기술하자. 

procedure TForm4.FormCreate(Sender: TObject);
begin
  ReportMemoryLeaksOnShutdown := True; // 메모리 누수를 종료시 보고하게 함 
end;

메인폼에 버튼 두개를 두고, 각각의 OnClick 이벤트에 델파이 방식과 Spring4d 방식으로 list 객체를 사용하는 메서드를 2개 만든다 

// Spring4d 컬렉션 방식
procedure TForm4.Button1Click(Sender: TObject);
var
  list : IList<TMyObject>; // 그렇다.. Spring4d는 IList 즉 인터페이스를 사용하여 자동 파괴한다
begin
  list := TCollections.CreateList<TMyObject>(true); // 인수 true가 자동 파괴를 명시함, 이를 false로 주면 자동 파괴를 안함
  list.Add(TMyObject.Create);
  list.Add(TMyObject.Create); 
end;

// 델파이 컬렉션 방식
procedure TForm4.Button2Click(Sender: TObject);
var
  list: TList<TMyObject>;
begin
  list := TList<TMyObject>.Create;
  list.Add(TMyObject.Create);
  list.Add(TMyObject.Create);
end;

프로그램을 실행한 다음, 델파이 방식 버튼을 클릭하고 종료해보라. 분명하게 메모리 누수 에러가 뜰 것이다. 반면 Spring4d 방식 버튼을 클릭하고 종료하면 메모리 누수 에러가 뜨지 않는다. 이말인즉 델파이의 TLIst<T>는 list 객체와 더불어 list가 가지는 요소 객체들도 모두 개발자가 스스로 알아서 파괴해줘야 한다는 것이다. 이는 매우 복잡한 코드를 강요한다. 따라서 델파이 컬렉션 List를 사용할때 올바른 방식은 다음과 같다. 

// 올바른 델파이 컬렉션 TList<T> 사용법
var
  list: TList<TMyObject>;  
begin
  list := TList<TMyObject>.Create;
  list.Add(TMyObject.Create);
  list.Add(TMyObject.Create);
  
  // 요소 모조리 파괴
  while list.Count > 0 do begin
    list[0];.Free;
    list.Delete(0);
  end;
  
  //리스트 파괴
  list.Free;
end;

Spring4d 컬렉션은 자동 파괴를 하기 때문에 전혀 저런 청소용 코드가 필요없다. 만일 list에 요소를 수시로 add, delete 한다면, 델파이 방식은 정말로 머리 아프다. 삭제할 요소에 대해서 일일이 free 를 호출해 줘야 하기 때문이다. 반면에 Spring4d 방식은 요소를 삭제할 때도 자동 파괴를 한다. 

Spring4d는 List 외에도 훨씬 더 다양한 컬렉션 타입을 지원한다. 또 더 많은 컬렉션 메서드를 가진다. 예를 들어 skip, skipwhile, all, any, foreach 등이다. 

Spring4d 컬렉션 List의 경우, 모든 요소에 대해 어떤 일괄적인 작업을 수행하고 싶다면 굳이 for나 while 반복문 대신에 다음과 같이 처리할 수 있다.

  // list 모든 요소에 대해서 익명함수를 각각 실행 시킨다
  list.forEach( procedure(const obj:TMyObject)  begin
      // obj 사용
    end);

java 프로그래밍을 해본 사람이라면 이 forEach 메서드가 매우 눈에 익을 법하다. 

Spring4d 컬렉션은 C#의 LINQ 비슷한 문법을 지원하는데, Take, Skip, SkipWhile 등이다. 다음 코드는 정수 요소를 가지는 리스트를 만들고, 세개를 건너뛰고(skip), 요소들을 출력하는 코드다. 

var
  list : IList<Integer>;
begin
  list := TCollections.CreateList<Integer>([1,2,3,4,5,6]);

  list.Skip(3).ForEach(procedure(const val:Integer)
    begin
      outputDebugstring(PChar(inttostr(val)) );
    end);

이 외에도 다양한 기능들이 많다. ISet의 경우 차집합, 교집합, 공집합 등을 구할 수 있다. 

Spring.Collections 유닛의 TCollections 클래스 선언부를 보면 대충 어떤 컬렉션 타입을 지원하는지, 그리고 어떤 Interface를 사용하는지, 각 인터페이스는 어떤 메서드를 가지는지 자세한 정보를 알수 있다.




[출처 : https://www.delmadang.com/community/bbs_view.asp?bbsNo=3&bbsCat=0&indx=452265&page=2]