DesignPatternDelphi

From 흡혈양파의 번역工房
Revision as of 02:28, 10 January 2013 by Onionmixer (talk | contribs) (메소드 > 메서드 수정)
Jump to navigation Jump to search
Introduction to Design Patterns in Delphi

원문 사이트
http://www.obsof.com/delphi_tips/pattern.html

번역진행
박진아

검수진행
없음


Introduction to Design Patterns in Delphi

이 요약문은 James Heyworth에 의해 Canberra PC Users Group Delphi SIG에서 1996년 11월 11일 프리젠테이션에 사용되었습니다.


문의사항이 있으시다면 james@obsof.com 으로 내용을 보내주시기 바랍니다.


서문

디자인패턴은 객체지향 디자인중 관계성과 구조에 대한것중 자주반복되는것에 대한것을 의미합니다.


이것을 사용하면 더 나은 디자인과 더 많은 재사용가능코드, 그리고 더 복잡한 시스템을 디자인하는것을 배우는데 도움이 됩니다.


작업에 많은 개선을 주는 디자인패턴을 제공하는 책은 이것입니다. 디자인패턴 : 재사용 가능한 객체지향 소프트웨어의 요소, Gamma, Helm, Johnson and Vlissides.[1]


이 저자들은 또한 "the Gang of Four"라고 불리기도 합니다.


당신의 객체를 디자인하기전 아직 이책을 읽지 않았다면 이건 당신의 디자인 구조에 훌륭한 입문서가 될것입니다.


이 예제들을 최대한 활용하기 위해서는 이 책을 읽을것을 권장합니다.


본 문서는 디자인패턴에서 가져온 몇개 예제패턴을 델파이로 구현하였습니다.


패턴컨셉의 또 다른 좋은 참고는 이 책입니다. Object Models :Strategies, Patterns and Applications/Peter Coad 지음.[2]


Coad의 예제들은 더 많은 비즈니스를 지향하며 당신의 일에서 패턴을 식별하고 학습할 수 있는 부분을 강조합니다.


델파이는 어떻게 당신이 패턴을 정의할 수 있도록 도울 수 있습니까?

델파이 환경은 심플한 개발환경으로서 실전적이고 많은 개선이 이루어진 완벽한 객체지향 언어입니다.


델파이 클래스의 개념에 대한 요약은 부록에서 찾을 수 있습니다.


패턴의 관점에서 가장 중요한 클래스의 속성은 클래스 상속의 기본- 가상 그리고 추상메서드, 그리고 protected 또는 public의 범위를 사용한다는 사실-이다.


이것(델파이)은 패턴을 재사용하고 확장할 수 있는 패턴을 생성하는데 유용하거나, 변하지않는 기본 속성을 기능에따라 다양하게 분리하는 등에 대한 유용한 도구를 제공한다.


델파이는 확장가능한 어플리케이션, 컴포넌트 아키텍처를 아우르는 IDE인터페이스와 도구인터페이스의 아주 좋은 예입니다.


이런 인터페이스는 많은수의 가상, 추상 생성자와 행동등을 정의합니다.


델파이로 만든 디자인패턴 예제

일단 먼저 말할것이 있는데, 이것에 대한 대안이라거나 또는 패턴을 구현하기위한 보다 좋은 방법이 있다면 내게 방법을 알려주세요. 환영합니다.


디자인패턴 책에 있는 패턴을 따라가다보면, 델파이패턴을 구현하는데 있어 델파이에서 논의 및 설계를 만들 수 있는 시작점을 당신은 가지게 됩니다.

패턴 이름 정의

  • Singleton "특정 클래스에 인스턴스 하나만 보장하고 글로벌 지점의 접근권을 제공합니다."
  • Adapter "클래스의 인터페이스를 고객이 원하는 인터페이스로 변환합니다. Adapter는 호환되지 않는 인터페이스 때문에 작동할 수 없었던 클래스를 함께 작동하도록 해줍니다."
  • Template Method "파생 클래스에 몇 가지 단계를 연기하며 운영 알고리즘의 기본틀을 정의합니다. Tenmplete Method는 서브클래스가 알고리즘의 구조를 변경하지 않고 알고리즘의 특정 단계를 재정 할 수 있도록 해줍니다."
  • Builder "객체의 코딩에 있어 작성과정이 다른 표현을 만들 수 있도록 객체의 표현에서 복잡한 객체의 작성을 분리합니다."
  • Abstract Factory "자신의 구체적인 클래스를 지정하지 않고 관련되거나 의존적인 객체의 집합을 만들기 위한 인터페이스를 제공합니다."
  • Factory Method "객체를 생성하기 위한 인터페이스를 정의하지만, 서브클래스가 어떤 클래스로 보여질 것인지를 결정할 수 있습니다. Factory Method는 클래스가 서브클래스에 인스턴스화를 연기 시킬 수 있습니다."


주의: 이 정의들은 디자인패턴에서 가져왔습니다.


Pattern: Singleton

정의

"특정 클래스에 인스턴스 하나만 보장하고 글로벌 지점의 접근권을 제공합니다."
이 패턴은 적용하기 가장 쉬운 패턴 중 하나입니다.


델파이에서의 응용

델파이 VCL에서 이러한 종류의 클래스에 예제로는 TApplication, TScreen 또는 TClipboard가 있습니다. 응용 프로그램에서 하나의 전역 객체를 원하는 경우 이 패턴이 유용합니다. 다른 용도로는 global exeception handler, 응용 프로그램 보안 또는 다른 응용 프로그램 인터페이스의 단일 지점을 포함 할 수 있습니다.


구현 예제

이 유형의 클래스를 구현하려면 클래스의 생성자와 소멸자가 클래스의 전역변수를 참조하도록 기각합니다. 변수가 할당된 경우, 생성자를 중단합니다.

그렇지 않을 경우, 인스턴스를 생성하고 변수를 할당합니다.


소멸자에서는 인스턴스가 destroy되는 걸 의미하는 경우 변수를 없앱니다. Note: 단일 인스턴스 자동에 대한 creation과 destruction를 만들려면, Unit의 초기화 섹션에 creation이 포함되어 있어야합니다. 인스턴스를 destruction하려면 ExitProc (델파이 1) 또는 기기의 finalization 섹션 (델파이 2)에 파괴가 있어야 합니다.

다음 델파이 1 예제는 두개의 싱글톤 클래스를 설명하고 있습니다. 하나는 TComponent으로부터 파생된 것이고 또 다른 하나는 TObject으로부터 파생된 것입니다.


unit Singletn;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs;

type
  TCSingleton = class(TComponent)
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

  TOSingleton = class(TObject)
  public
    constructor Create;
    destructor Destroy; override;
  end;

var
  Global_CSingleton: TCSingleton;
  Global_OSingleton: TOSingleton;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Design Patterns', [TCSingleton]);
end;

{ TCSingleton }

constructor TCSingleton.Create(AOwner: TComponent);
begin
  if Global_CSingleton <> nil then
    {NB could show a message or raise a different exception here}
    Abort
  else begin
    inherited Create(AOwner);
    Global_CSingleton := Self;
  end;
end;

destructor TCSingleton.Destroy;
begin
  if Global_CSingleton = Self then
    Global_CSingleton := nil;
  inherited Destroy;
end;

{ TOSingleton }

constructor TOSingleton.Create;
begin
  if Global_OSingleton <> nil then
    {NB could show a message or raise a different exception here}
    Abort
  else
    Global_OSingleton := Self;
end;

destructor TOSingleton.Destroy;
begin
  if Global_OSingleton = Self then
    Global_OSingleton := nil;
  inherited Destroy;
end;

procedure FreeGlobalObjects; far;
begin
  if Global_CSingleton <> nil then
    Global_CSingleton.Free;
  if Global_OSingleton <> nil then
    Global_OSingleton.Free;
end;

begin
  AddExitProc(FreeGlobalObjects);
end.


Pattern: Adapter

정의

"클래스의 인터페이스를 Client가 원하는 인터페이스로 변환합니다. Adapter는 호환되지 않는 인터페이스 때문에 작동할 수 없었던 클래스를 함께 작동하도록 해줍니다."


델파이에서의 응용

Adapter의 전형적인 예는 VBX이나 OCX를 가져올 때 델파이에서 생성시키는 래퍼입니다. 델파이는 외부(다른환경)의 인터페이스를 파스칼(Pascal) 호환 인터페이스로 바꾸는 새로운 클래스를 생성합니다. 또 다른 일반적인 경우는 하나의 인터페이스를 과거와 현재 시스템으로 만들 때입니다.[3]

Note: 델파이는 디자인 패턴에서 설명 된 것처럼 여러 유산을 통한 클래스 적응을 허용하지 않습니다. 대신, 어댑터는 기존 클래스의 특정 인스턴스를 참조해야 합니다.


구현 예제

다음 예제는 신규 고객 클래스, Adapter 클래스와 기존 고객 클래스의 간단한 (read only) 경우입니다. adapter는 두 자리 수 년을 포함하는 기존 고객 기록을 새로운 날짜 형식으로 바꾸며 2000년 문제를 처리하는 것을 보여줍니다.

이 래퍼를 사용하는 Client는 신규 고객 클래스에 대해서만 알고 있습니다. 클래스 간의 번역[4]은 속성에 대한 가상 액세스 방법의 사용에 의해 처리됩니다. 기존 고객 클래스와 어댑터 클래스는 Unit의 구현에 숨겨져 있습니다.


unit Adapter;

interface

uses SysUtils, Classes;

type

  { The new class }
  TNewCustomer = class
  private
    FCustomerID: Longint;
    FFirstName: string;
    FLastName: string;
    FDOB: TDateTime;
  protected
    function GetCustomerID: Longint; virtual;
    function GetFirstName: string; virtual;
    function GetLastName: string; virtual;
    function GetDOB: TDateTime; virtual;
  public
    constructor Create(CustID: Longint); virtual;
    property CustomerID: Longint read GetCustomerID;
    property FirstName: string read GetFirstName;
    property LastName: string read GetLastName;
    property DOB: TDateTime read GetDOB;
  end;

  { An interface method }
  { Lets us hide details of TOldCustomer from the client }

function GetCustomer(CustomerID: Longint): TNewCustomer;

implementation

const
  Last_OldCustomer_At_Year_2000 = 15722;
  Last_OldCustomer_In_Database = 30000;

{ The new class }

constructor TNewCustomer.Create(CustID: Longint);
begin
  FCustomerID := CustID;
  FFirstName := 'A';
  FLastName := 'New_Customer';
  FDOB := Now;
end;

function TNewCustomer.GetCustomerID: Longint;
begin
  Result := FCustomerID;
end;

function TNewCustomer.GetFirstName: string;
begin
  Result := FFirstName;
end;

function TNewCustomer.GetLastName: string;
begin
  Result := FLastName;
end;

function TNewCustomer.GetDOB: TDateTime;
begin
  Result := FDOB;
end;

type
  { The old class }
  TOldDOB = record
    Day: 0..31;
    Month: 1..12;
    Year: 0..99;
  end;

  TOldCustomer = class
    FCustomerID: Integer;
    FName: string;
    FDOB: TOldDOB;
  public
    constructor Create(CustID: Integer);
    property CustomerID: Integer read FCustomerID;
    property Name: string read FName;
    property DOB: TOldDOB read FDOB;
  end;

constructor TOldCustomer.Create(CustID: Integer);
begin
  FCustomerID := CustomerID;
  FName := 'An Old_Customer';
  with FDOB do begin
    Day := 1;
    Month := 1;
    Year := 1;
  end;
end;

type
  { The Adapter class }
  TAdaptedCustomer = class(TNewCustomer)
  private
    FOldCustomer: TOldCustomer;
  protected
    function GetCustomerID: Longint; override;
    function GetFirstName: string; override;
    function GetLastName: string; override;
    function GetDOB: TDateTime; override;
  public
    constructor Create(CustID: Longint); override;
    destructor Destroy; override;
  end;

{ The Adapter class }

constructor TAdaptedCustomer.Create(CustID: Longint);
begin
  inherited Create(CustID);
  FOldCustomer := TOldCustomer.Create(CustID);
end;

destructor TAdaptedCustomer.Destroy;
begin
  FOldCustomer.Free;
  inherited Destroy;
end;

function TAdaptedCustomer.GetCustomerID: Longint;
begin
  Result := FOldCustomer.CustomerID;
end;

function TAdaptedCustomer.GetFirstName: string;
var
  SpacePos: integer;
begin
  SpacePos := Pos(' ', FOldCustomer.Name);
  if SpacePos = 0 then
    Result := ''
  else
    Result := Copy(FOldCustomer.Name,1,SpacePos-1);
end;

function TAdaptedCustomer.GetLastName: string;
var
  SpacePos: integer;
begin
  SpacePos := Pos(' ', FOldCustomer.Name);
  if SpacePos = 0 then
    Result := FOldCustomer.Name
  else
    Result := Copy(FOldCustomer.Name,SpacePos+1,255);
end;

function TAdaptedCustomer.GetDOB: TDateTime;
var
  FullYear: Word;
begin
  if CustomerID > Last_OldCustomer_At_Year_2000 then
    FullYear := 2000 + FOldCustomer.DOB.Year
  else
    FullYear := 1900 + FOldCustomer.DOB.Year;
  Result := EncodeDate(FullYear, FOldCustomer.DOB.Month, FOldCustomer.DOB.Day);
end;

function GetCustomer(CustomerID: Longint): TNewCustomer;
begin
  if CustomerID > Last_OldCustomer_In_Database then
    Result := TNewCustomer.Create(CustomerID)
  else
    Result := TAdaptedCustomer.Create(CustomerID) as TNewCustomer;
end;

end.


Pattern: Template Method

정의

"서브클래스에 몇 가지 단계를 연기하며 운영 알고리즘의 기본틀을 정의합니다. Template Method 서브클래스가 알고리즘의 구조를 변경하지 않고 알고리즘의 특정 단계를 재정의 할 수 있도록 해줍니다."

이 것은 근본적으로 abstract method을 더 복잡한 알고리즘으로 확대해놓은 패턴입니다

델파이에서의 응용

추상화라는 것은 추상적인 가상 메서드로 델파이에서 구현되고 있습니다. 추상화 라는것은 기본 클래스가 어떤 구현도 제공하지 않는것으로서 가상메서드와는 다릅니다. 하위 클래스는 추상메서드를 구현하기 위한 모든 책임이 있습니다. Ovberriden하지 않은 추상메서드를 호출하면 런타임 오류가 발생합니다. 추상화의 전형적인 예는 TGraphic 클래스입니다.

TGraphic 클래스는 TBitmap, TIcon 및 TMetafile을 구현하는 데 사용되는 추상 클래스입니다. 다른 개발자들은 자주 PCX, GIF, JPG 표현과 같은 다른 그래픽 객체에 대한 기본으로 TGraphic을 사용했습니다. TGraphic은 concrete 클래스에서 기각된 Draw, LoadFromFile 및 SaveToFile의 추상메서드를 정의합니다. 이러한 TCanvas와 같은 TGraphic 사용하는 다른 객체들은 추상적 Draw 방법에 대해만 알며 concrete 클래스와 런타임시 함께 사용됩니다. 복잡한 알고리즘을 사용하는 많은 클래스들은 Templete Method를 사용하는 추상화로부터 이익을 얻을 가능성이 높습니다. 일반적인 예로는 데이터 압축, 암호화 및 고급 그래픽 처리 등이 있습니다.


구현 예제

Template Method를 구현하려면 각 대체 구현을 위한 추상 클래스와 concrete 클래스가 필요합니다. 추상 기본 클래스에서 알고리즘의 공용 인터페이스를 정의합니다. 해당 공용 방법에서는 클래스의 추상메서드를 보호하기 위한 호출에서 알고리즘의 단계를 구현합니다. 기본 클래스에서 파생 된 concrete 클래스에서는 그 클래스에 맞는 구체적인 구현과 함께 알고리즘의 각 단계를 기각합니다.

이 예제는 매우 간단한 알고리즘 단계를 보여줍니다. 하지만 서브클래스에 구현을 미루는[5] 원리를 보여줍니다.


unit Tpl_meth;

interface

type
  TAbstractTemplateClass = class(TObject)
  protected
    function Algorithm_StepA: Integer; virtual; abstract;
    function Algorithm_StepB: Integer; virtual; abstract;
    function Algorithm_StepC: Integer; virtual; abstract;
  public
    function Algorithm: Integer;
  end;

  TConcreteClassA = class(TAbstractTemplateClass)
  protected
    function Algorithm_StepA: Integer; override;
    function Algorithm_StepB: Integer; override;
    function Algorithm_StepC: Integer; override;
  end;

  TConcreteClassB = class(TAbstractTemplateClass)
  protected
    function Algorithm_StepA: Integer; override;
    function Algorithm_StepB: Integer; override;
    function Algorithm_StepC: Integer; override;
  end;


Pattern: Builder

정의

"객체의 코딩에 있어 작성과정이 다른 표현을 만들 수 있도록 객체의 표현에서 복잡한 객체의 작성을 분리합니다."

Builder는 Abstract Factory 개념과 비슷한 것 같습니다. 제가 보기에 차이점은 Builder는 많은 부분을 가지고 있는 다른 concrete 클래스[6]의 단일 복잡한 객체를 의미하는 반면에 abstract factory는 concrete 클래스의 전체 패밀리 만들 수 있습니다. 예를 들어, Builder는 집, 별장이나 사무실을 구축 할 수 있습니다. 벽돌 집이나 목조 주택에 각자 다른 Builder를 고용 할 수 있습니다. 한편, Factory는 부분이 아닌 전체를 생성합니다. 건물들의 창문들을 생산하거나 또는 다양한 자동차 창문들을 생산 할 수 있습니다.

델파이에서의 응용

양식 및 구성 요소를 만드는 경우 델파이의 VCL에 사용되는 기능은 Builder와 개념이 비슷합니다. 델파이는 Application.CreateForm 과 TForm 클래스 생성자를 통해 공통의 인터페이스를 사용하여 form을 만듭니다. TForm는 form이 소유하는 구성 요소들을 예시화하는 자원 정보 (DFM 파일)를 사용하여 일반적인 생성자를 구현합니다. 많은 자손 클래스들은 다른 표현을 만들기 위해 이 같은 작성 과정을 재사용합니다. 또한, 델파이는 개발자에 의한 확장이 쉽습니다. TForm의 OnCreate 이벤트도 기능을 확장하기 쉽게 할 수 있도록 Builder 과정에 Hook를 더합니다.

구현 예제

다음 예제는 TAbstractFormBuilder 클래스와 두 개의 구체 클래스인 TRedFormBuilder와 TBlueFormBuilder이 포함되어 있습니다. 개발의 용이성에 대한 concrete 클래스의 몇 가지 일반적인 기능은 공유된 TAbstractFormBuilder 클래스로 이동되었습니다.


type
  TAbstractFormBuilder = class
  private
    FForm: TForm;
    procedure BuilderFormClose(Sender: TObject; var Action: TCloseAction);
  protected
    function GetForm: TForm; virtual;
  public
    procedure CreateForm(AOwner: TComponent); virtual;
    procedure CreateSpeedButton; virtual; abstract;
    procedure CreateEdit; virtual; abstract;
    procedure CreateLabel; virtual; abstract;
    property Form: TForm read GetForm;
  end;

type
  TRedFormBuilder = class(TAbstractFormBuilder)
  private
    FNextLeft, FNextTop: Integer;
  public
    procedure CreateForm(AOwner: TComponent); override;
    procedure CreateSpeedButton; override;
    procedure CreateEdit; override;
    procedure CreateLabel; override;
  end;

type
  TBlueFormBuilder = class(TAbstractFormBuilder)
  private
    FNextLeft, FNextTop: Integer;
  public
    procedure CreateForm(AOwner: TComponent); override;
    procedure CreateSpeedButton; override;
    procedure CreateEdit; override;
    procedure CreateLabel; override;
  end;


At runtime the client application instructs one of the concrete classes to create parts using the public part creation procedures. The concrete builder instance is passed to the folliwing procedure:

런타임시에는 client 응용 프로그램이 공용 부분 생산 procedure를 사용하여 부품을 만들 수 있도록 concrete 클래스 중 하나를 지시합니다. concrete Builder 인스턴스는 다음 procedure에 전달됩니다:

procedure TForm1.Create3ComponentFormUsingBuilder(ABuilder: TAbstractFormBuilder);
var
  NewForm: TForm;
begin
  with ABuilder do begin
    CreateForm(Application);
    CreateEdit;
    CreateSpeedButton;
    CreateLabel;
    NewForm := Form;
    if NewForm <> nil then NewForm.Show;
  end;
end;


Pattern: Abstract Factory

구현

"자신의 구체적인 클래스를 지정하지 않고 관련되거나 의존적인 객체의 집합을 만들기 위한 인터페이스를 제공합니다."

아래 Factory 메서드 패턴은 일반적으로 이 패턴에 사용됩니다.

델파이에서의 응용

concrete 클래스의 구현으로부터 응용 프로그램을 분리하고 싶다면 이 패턴이 이상적입니다. 예를 들어, 16 및 32 비트 응용 프로그램을 위한 일반적인 VCL 층을 델파이의 VCL과 덮어씌우고 싶다면 abstract factory를 기초로 시작할 수 있습니다.

구현 예제

다음 예제는 사용자 인터페이스 구성 요소의 다른 스타일을 구현하는 abstract factory와 두 개의 concrete factory 클래스를 사용합니다. 일반적으로 전체 응용 프로그램에 한 개의 factory사용을 원하기 때문에 TOAbstractFactory는 Singleton 클래스입니다.

TOAbstractFactory = class(TObject)
  public
    constructor Create;
    destructor Destroy; override;
    { abstract widget constructors }
    function CreateSpeedButton(AOwner: TComponent): TSpeedButton; virtual; abstract;
    function CreateEdit(AOwner: TComponent): TEdit; virtual; abstract;
    function CreateLabel(AOwner: TComponent): TLabel; virtual; abstract;
  end;

TORedFactory and TOBlueFactory override the abstract interface to support different widget styles.

TORedFactory = class(TOAbstractFactory)
  public
    { concrete widget constructors }
    function CreateSpeedButton(AOwner: TComponent): TSpeedButton; override;
    function CreateEdit(AOwner: TComponent): TEdit; override;
    function CreateLabel(AOwner: TComponent): TLabel; override;
  end;

TOBlueFactory = class(TOAbstractFactory)
  public
    { concrete widget constructors }
    function CreateSpeedButton(AOwner: TComponent): TSpeedButton; override;
    function CreateEdit(AOwner: TComponent): TEdit; override;
    function CreateLabel(AOwner: TComponent): TLabel; override;
  end;

At runtime, our client application instantiates the abstract factory with a concrete class and then uses the abstract interface. Parts of the client application that use the factory don't need to know which concrete class is actually in use.

런타임시에는 우리의 client 응용 프로그램이 추상 클래스와 함께 abstract factory를 예시하고 추상적인 인터페이스를 사용합니다. Factory를 사용하는 클라이언트 응용 프로그램의 일부는 어떤 concrete 클래스가 실제로 사용되는지 알 필요가 없습니다.

Pattern: Factory Method

정의

"객체를 생성하기 위한 인터페이스를 정의하지만, 서브클래스가 어떤 클래스로 보여질 것인지를 결정할 수 있습니다. Factory Method는 클래스가 서브클래스에 인스턴스화를 연기 시킬 수 있습니다."

추상 factory 패턴은 factory 방법의 모음으로 볼 수 있습니다.

델파이에서의 응용

클래스의 작성을 요약하고 concrete 클래스의 내용을 추상적 인터페이스를 통해 고객 응용프로그램으로부터 분리할 때 이 패턴이 유용합니다.

여러 대상의 DBMS에 잠재적으로 객체지향 비즈니스 응용프로그램을 가지고 있다면 이 중 하나의 예가 발생 할 수 있습니다. Client 응용 프로그램은 그들의 구현 특정 저장 및 검색에 대해 알고 싶어하지 않고 비즈니스 클래스에 대해 알고 싶어합니다.

구현 예제

Abstract Factory 예에서는 각 가상 위젯 생성자 기능들이 factory 메서드입니다. 그들의 구현에서는 반환 할 특정 위젯 클래스를 정의합니다.


TRedSpeedButton = class(TSpeedButton)
  public
    constructor Create(AOwner: TComponent); override;
  end;

constructor TRedSpeedButton.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  Font.Color := clRed;
end;

function TORedFactory.CreateSpeedButton(AOwner: TComponent): TSpeedButton;
begin
  Result := TRedSpeedButton.Create(AOwner);
end;


부록: 델파이 클래스 정의에 있어서 핵심요소

Unit 구조

델파이 Unit (. PAS 파일)는 인터페이스 및 구현 부분의 선언을 할 수 있도록 허락합니다. 인터페이스는 그 Unit를 사용하여 다른 Unit를 표시한 부분을 정의합니다. 키워드 사용은 Unit는 Unit의 인터페이스 또는 Unit가 쓰고 있는 다른 Unit 목록에 구현 부분을 추가 할 수 있습니다. 이 것은 Unit가 사용된 Unit의 인터페이스의 일부를 의미하는 컴파일러를 나타냅니다. 구현 부분에 공표된 Unit의 일부는 그 Unit에게 전부 비공개입니다. 즉, 다른 Unit한테는 보일 수가 없습니다. Unit의 인터페이스에 선언된 유형, 기능과 절차는 해당 구현이 있거나 외부적으로 선언이 되어야 합니다. (예: DLL에 있는 기능 호출)

Class 인터페이스

클래스들은 델파이의 Type으로 정의되어 있으며 표준 데이터 유형 분야 또는 기능이나 절차 및 성질로 선언된 객체나 방법을 포함 할 수 있습니다. 클래스의 Type 선언은 인터페이스와 클래스의 필드, 메서드 및 속성에 대한 접근 범위를 정의합니다. 클래스 인터페이스는 일반적으로 Unit를 사용하여 다른 구성 단위들이 접근 할 수 있도록 하는 Unit의 Interface[7]에 정의되어 있습니다. 그러나, 그럴 필요가 없습니다. 때로는 클래스의 Type 선언은 Unit의 Implementation[8] 부분 내에서만 사용할 수 있습니다.

속성(Properties)

속성은 읽고 쓰는 방법을 통해 접근 제어를 허용하는 정의 형식의 필드에 특화된 인터페이스입니다. 속성은 가상이 아니고, 같은 이름의 다른 속성으로 속성을 대체할 수 있지만, 부모(위에)클래스는 새 속성에 대해 알고 있지 않습니다. 그러나 가상 속성의 접근 방법을 만들기가 가능합니다.

상속(Inheritance)

델파이의 상속 모델은 단일 계층 구조에 기반을 두고 있습니다. 모든 클래스는 TObject으로부터 상속하고 단 하나의 부모만 가질 수 있습니다. 하위(자식)클래스는 아래에 설명 된 범위에 따라 상위 클래스의 인터페이스와 기능 모두를 상속합니다.

하나 이상의 부모(클래스)로부터 다중상속을 할 수는 없습니다. 이 경우 하나 이상의 다른 클래스를 생성하고 선택적으로 포함 된 클래스의 일부를 노출하는 컨테이너 클래스를 사용하여 구현 될 수 있습니다.

Private, Protected, Public and Published Scope

범위는 클래스의 인터페이스에 정의된 방법 및 데이터의 가시성을 의미합니다. 즉, 무엇이든 클래스의 부분이면 응용프로그램의 나머지 부분이나 하위 클래스에게 접근이 가능합니다.

기본 범위 구성 요소는 공개합니다, 예를 들어, 유형에 구성요서 인스턴스를 정해진 시간에 더한 경우. 대중은 "와서 잡아 봐라"라고 말합니다.

그것은 런타임 때 방법 또는 데이터들이 모든 것에게 보여지도록 만듭니다. 클래스의 게시 된 부품은 공개 범위의 전문 형태입니다.

이것은 TPersistent에서부터 파생 된 클래스에 대한 특별한 동작을 나타냅니다. 지속된 클래스는 델파이의 표준 스트리밍 방법을 사용하여 발행된 속성을 지속적인 저장고로 저장하고 복원 할 수 있습니다. 발행된 속성은 또한 IDE에 델파이 객체 검사기와 상호 작용할 수 있습니다. Published를 사용하려면 클래스는 TPersistent으로부터 내려와야 합니다. 델파이 컴파일러가 그것을 막지는 않지만 그것들을 저장할 수 없음으로 publishing 방법에 중요한 포인트는 없습니다. Published는 델파이의 런타임 형식 정보를 통해 다른 응용 프로그램이 클래스의 세부 사항들에 접근할 수 있도록 합니다. VCL과 함께 델파이의 설계 시간 상호작용 이외에는 이것은 거의 사용하지 않을 것입니다.

객체 오리엔테이션에게 요약화 또는 정보 감춤은 필수적입니다. 때문에 보호 및 개인 범위가 클래스 일부의 접근 범위를 좁혀 줍니다.

보호 부분은 하위 클래스 또는 같은 단위에 정의 된 다른 클래스에게만 보여집니다.

개인 부분은 정의된 클래스 또는 같은 단위에 정의 된 다른 클래스에게만 보여집니다.

한번 어떤 것이 공개 또는 발행 범위에 주어지면 이것은 하위 클래스에서 숨길 수가 없으니 이용에 참고하시는 것이 중요합니다.

Static, Virtual and Dynamic Methods; Override and Inherited

가상 또는 동적으로 선언된 방법들은 하위 클래스에서 무효화를 이용하여 행동을 바꿀 수 있게 해줍니다. 델파이에서 금지하지는 않지만 같은 단위에서만 기각이 가능하기 때문에 클래스의 개인 부분에서 가상 방법을 볼 가능성은 없습니다.

오버라이드는 부모(위)클래스에서부터 같은 이름의 방법을 새로운 방법으로 바꾸는 것을 나타냅니다. 오버라이드는 원래 방법과 동일한 이름과 한도로 선언되어야 합니다.

메서드가 오버라이드된 경우, 부모 클래스의 메서드 호출이 객체의 실제 클래스에서 오버라이드 방법을 실행합니다. 반면에 정적 메서드는 가상 또는 오버라이드 메서드가 없습니다. 그러나 이것은 오브젝트 지향적이 아닙니다.

만약 하위 클래스를 부모(위) 형식으로 참고하고 바뀐 방법을 호출하려고 할 경우, 부모(위) 클래스의 Static 메서드가 실행됩니다.

그래서 대부분의 경우, 정적 메서드를 바꾸는 것은 나쁜 아이디어 입니다. 가상 및 동적 방법은 서로 교대해서 쓸 수 있습니다. 그들은 컴파일러와 런타임 라이브러리에 의한 처리방법에서만 차이가 있습니다.

델파이의 도움말은 동적 메도스는 구현이 컴파일링 시점에서 해결되고 약간 빠르게 실행이 되는 반면 가상 메서드는 런타임 때 해결이 되며 결과적으로 조금 느리게 접속이 되지만 작은 번역 프로그램을 낳는다고 설명하고 있습니다. 가상은 일반적으로 선호하는 선언(방법)입니다. 델파이의 도움말은 기본 클래스와 함께 방법을 오버라이드하지 않은 많은 하위 클래스들을 가지고 있을 경우 동적인 방법을 이용하라고 제안합니다.

상속 지시어는 부모 클래스에 선언 된 것과 같은 속성이나 메서드를 다시 참조 할 수 있도록 해줍니다. 부모 클래스에서 상속된 메서드를 호출하고 그 행동을 보완하기 위해 사용되며 이것은 오버라이드 메서드의 구현에 가장 많이 사용됩니다.


Abstract Methods

추상은 인터페이스에서 메서드를 선언하고 하위 클래스에게 구현을 연기하기 위한 기본 클래스에서 사용됩니다. 즉, 이것은 근본적인 작동이 아닌 인터페이스를 정의합니다.[9] 추상은 virtual 또는 지시어와 함께 사용되어야 합니다. 추상메서드는 절대 기본 클래스에서 구현되지 않고 사용할 하위 클래스에서 구현해야 합니다. 오버라이드되지 않은 추상 메서드를 실행하려고 하면 런타임 오류가 발생합니다. 또한, 추상메서드의 오버라이드된 구현에서 상속적인 행동이 없기 때문에 상속된/유전된 것을 호출 시 런타임 오류가 발생합니다.

Messages

델파이가 윈도우 메시지를 처리하는 것은 가상 방법의 특별한 경우입니다. 메시지 담당자들은 TControl에서부터 내려오는 클래스들에서 구현되어 있습니다. 즉, Handle이 있는 클래스들은 메시지를 받을 수 있습니다. 메시지 handle은 항상 virtual이고 클래스 인터페이스의 private 부분에서 선언 될 수 있지만 아직까지 상속 메서드를 호출 할 수 있습니다. 메시지 Haldler에서 키워드를 상속해서 사용하는 경우, 호출되는 메서드의 이름을 제공 할 필요가 없습니다.

Events

클래스의 인스턴스에 확장을 위임하게 때문에 이벤트도 델파이의 중요한 특징입니다. 이벤트는 다른 객체의 방법을 참조하는 속성입니다. 이벤트는 델파이 1에서는 상속되어있지 않습니다. 델파이2는 상속을 이벤트에 사용할 수 있도록 이 행동을 확장합니다.

이벤트에 있는 상속 담당자는 상속된 키워드만 사용합니다. 메서드가 호출될때 이름을 제공 할 필요가 없습니다. 사용자의 구성 요소가 작성된 시점에서 예측 할 수 없는 방법으로 행동을 수정할 수 있게 후크를 제공해주기 때문에 이벤트는 구성 요소 개발자에게 특히 중요합니다.

생성자와 소멸자(Constructors and Destructors)

생성자와 소멸자는 메서드의 두 가지 특별한 유형입니다. 생성자는 클래스 인스턴스 (메모리 0으로 초기화 할당)를 초기화하고 객체에 대한 참조(포인터)를 반환합니다. 소멸자는 객체가 쓴 메모리 할당(그러나 객체가 만들어낸 다른 객체의 메모리는 제외)을 해제합니다.

TObject으로부터 내려온 클래스들은 static constructor, Create 및 가상 소멸자 Destroy가 있습니다.

TComponent는 새로운 public 속성과 구성 요소의 소유자를 소개하고, 이는 생성자에서 초기화 해야 합니다.

TComponent의 생성자는 virtual로 선언되어 있습니다. 즉, 그 후손 클래스에서 Override 할 수 있습니다.

이는 상속 방법에서 호출을 포함하도록 TComponent후손에 있는 가상 생성자 또는 소멸자를 Override 때 필수적입니다.


참고서적

  1. Design Patterns: Elements of Reusable Object-Oriented Software, Erich Gamma et al., Addison-Wesley, Massachusetts, 1995.
  2. Object Models: Strategies, Patterns and Applications, Peter Coad et al., Prentice-Hall, New Jersey, 1995.


Copyright © 1996, Objective Software Technology Pty Limited. This paper may be freely distributed in its entirety, providing the source is acknowledged.

Portions quoted from Design Patterns are Copyright © 1995, Addison Wesley Publishing Company.

Notes

  1. 이름이 좀 이상하게 들어가 있지만 책 이름입니다. http://ko.wikipedia.org/wiki/%EB%94%94%EC%9E%90%EC%9D%B8_%ED%8C%A8%ED%84%B4_(%EC%B1%85) 옆의 위키페이지를 보시면 아시겠지만 이미 한국어로 번역이 된 책입니다.
  2. http://www.amazon.com/Object-Models-Strategies-Patterns-Applications/dp/0138401179 이라는 책입니다. 아직 번역본은 딱히 보지 못한거같네요.
  3. 같은 인터페이스를 사용하는 경우 클래스를 내부적으로 바꾸기 위해서 또는 내부 동작을 바꾸기 위해 사용할 수 있습니다.
  4. 클래스 사이의 데이터 교환등을 의미합니다.
  5. 기본 틀만 만들고 내부 구현은 나중에 작성하는 등의 경우.
  6. 추상클래스의 반대말로 구체화된 클래스라고 생각하시면 되겠습니다.
  7. pascal 문법내의 Interface 영역
  8. pascal 문법 내의 Implememtation 영역
  9. 인터페이스만 있고 내부에 실제 로직이 구현되지 않는 경우를 의미합니다.