StartprogrammingusingObjectPascal:QueueApplication

From 흡혈양파의 번역工房
Jump to: navigation, search

Queue 프로그램

Queue 는 자료구조 형식의 한 예입니다. 요소를 순서대로 삽입하고 저장하기 위해 사용하며, 삽입한 순서대로 삭제합니다. 이러한 규칙을 선입선출(First-In-First-Out) 이라고 합니다.


이 예제에서는 TQueue 클래스가 있는 Queue라고 하는 Unit을 작성합니다. TQueue 클래스는 이름과 같은 데이터를 저장하는데 사용할 수 있으며, 순서대로 데이터를 가져올 수 있습니다. 큐로부터 데이터를 가져오면 큐에서 데이터를 삭제합니다. 예를 들어 큐에 10개의 항목이 들어있고, 3개의 항목을 가져올 때, 큐에는 7개의 항목이 남아있을 것입니다.

Queue 유닛입니다.

unit queue;
// This unit contains TQueue class,
// which is suitable for any string queue

{$mode objfpc}{$H+}

interface
uses
    Classes, SysUtils;

type

    { TQueue }

    TQueue = class
    private
        fArray: array of string;
        fTop: Integer;
    public
        constructor Create;
        destructor Destroy; override;
        function Put(AValue: string): Integer;
        function Get(var AValue: string): Boolean;
        function Count: Integer;
        function ReOrganize: Boolean;
    end;

implementation

{ TQueue }

constructor Tqueue.create;
begin
    fTop:= 0;
end;

destructor TQueue.destroy;
begin
    SetLength(fArray, 0); // Erase queue array from memory
    inherited destroy;
end;

function TQueue.Put(AValue: string): Integer;
begin
    if fTop >= 100 then
        ReOrganize;
    SetLength(fArray, Length(fArray) + 1);
    fArray[High(fArray)]:= AValue;
    Result:= High(fArray) - fTop;
end;

function TQueue.Get(var AValue: string): Boolean;
begin
    AValue:= '';
    if fTop <= High(fArray) then
    begin
        AValue:= fArray[fTop];
        Inc(fTop);
        Result:= True;
    end
    else // empty
    begin
        Result:= False;
        // Erase array
        SetLength(fArray, 0);
        fTop:= 0;
    end;
end;

function TQueue.Count: Integer;
begin
    Result:= Length(fArray) - fTop;
end;

function TQueue.ReOrganize: Boolean;
var
    i: Integer;
    PCount: Integer;
begin
    if fTop > 0 then
    begin
        PCount:= Count;
        for i:= fTop to High(fArray) do
            fArray[i - fTop]:= fArray[i];
        // Truncate unused data
        setLength(fArray, PCount);
        fTop:= 0;
        Result:= True; // Re Organize is done
    end
    else
        Result:= False; // nothing done;
end;

end.


큐 프로그램의 메인 폼입니다.

Lazarus 12.png


메인 Unit의 코드입니다.

unit main;

{$mode objfpc}{$H+}

interface

uses
    Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics,
    Dialogs, Queue, StdCtrls;

type

    { TfmMain }

    TfmMain = class(TForm)
        bbAdd: TButton;
        bbCount: TButton;
        bbGet: TButton;
        edCustomer: TEdit;
        Label1: TLabel;
        Memo1: TMemo;
        procedure bbAddClick(Sender: TObject);
        procedure bbCountClick(Sender: TObject);
        procedure bbGetClick(Sender: TObject);
        procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
        procedure FormCreate(Sender: TObject);
    private
        { private declarations }
    public
        MyQueue: TQueue;
        { public declarations }
    end;

var
    fmMain: TfmMain;

implementation


{ TfmMain }

procedure TfmMain.FormCreate(Sender: TObject);
begin
    MyQueue:= TQueue.Create;
end;

procedure TfmMain.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
    MyQueue.Free;
end;

procedure TfmMain.bbCountClick(Sender: TObject);
begin
    Memo1.Lines.Add('Queue length is: ' + IntToStr(MyQueue.Count));
end;

procedure TfmMain.bbAddClick(Sender: TObject);
var
    APosition: Integer;
begin
    APosition:= MyQueue.Put(edCustomer.Text);
    Memo1.Lines.Add(edCustomer.Text + ' has been added as # ' +
    IntToStr(APosition + 1));
end;

procedure TfmMain.bbGetClick(Sender: TObject);
var
    ACustomer: string;
begin
    if MyQueue.Get(ACustomer) then
    begin
        Memo1.Lines.Add('Got: ' + ACustomer + ' from the queue');
    end
    else
        Memo1.Lines.Add('Queue is empty');
end;

initialization
    {$I main.lrs}

end.


TQueue 클래스에서 동적 배열을 확장하여 새로운 항목을 추가하기 위해 Put 메서드를 사용했고, 배열의 마지막 요소에 새로운 항목을 삽입했습니다.

큐의 위 꼭대기에서 항목을 제거하기 위해 Get 메서드를 호출할 때, fTop 포인터는 큐의 다음 항목으로 이동할 것입니다.

동적 배열의 꼭대기에서 항목을 제거하는 동작은 fTop 포인터가 이동하는 결과를 가져다주지만, 동적 배열의 항목은 메모리에 그대로 남아 있을 것이고, 공간을 차지할 것입니다. 왜냐하면, 동적 배열의 바닥부터만 지울 수 있기 때문에, 항목 갯수가 100개에 도달하면 동적 배열의 꼭대기에 있는 큐 요소를 이동하기 위해 ReOrganize 메서드를 호출할 것이며, 이렇게 하여 배열의 사용하지 않는 요소를 삭제하기 때문입니다.


동적 배열의 꼭대기 방향으로 큐의 요소들을 이동하는 코드입니다.

   for i:= fTop to High(fArray) do
      fArray[i - fTop]:= fArray[i];


그리고 바닥으로부터 동적 배열을 자르는 코드입니다.

    // Truncate unused data
    setLength(fArray, PCount);
    fTop:= 0;


이 예제에서 정보 은닉을 도입한 객체지향 프로그래밍을 알았습니다. 변수로의 접근과 같은 민감한 데이터로의 접근을 거부할 것입니다. 대신 객체에 대한 이상한 동작을 하지 않을 특정 메서드를 사용할 수 있습니다.


민감한 데이터나 메서드는 클래스 선언의 private 섹션에 위치할 것입니다.

    private
        fArray: array of string;
        fTop: Integer;


이 클래스를 사용하는 프로그래머는 이 변수들에 직접 접근할 수 없습니다. 만약 이 변수에 접근할 수 있었다면, 고의적으로 fTop 이나 fArray 를 수정하여 큐를 깨뜨릴 수 있습니다. 예를 들어 큐에 고작 10개의 요소를 가지고 있었는데 fTop을 1000으로 수정했다면, 실행 도중에 접근 위반(Access violation) 오류가 발생할 것입니다.


직접 변수 사용의 대안으로서, 배열로부터 안전하게 항목을 추가하고 제거하기 위한 메서드 PutGet을 구현했습니다. 이 메서드는 요소들 내부를 제어하기 위한 게이트를 사용하는 것과 같습니다. OOP의 이러한 특징을 캡슐화(encapsulation)라고 합니다.