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

델파이 강좌/문서
Delphi Programming Tutorial&Documents
[110] 델파이 코드 리팩토링 문제
주정섭 [jjsverylong] 6630 읽음    2007-01-09 14:00
델파이 리팩토링 연습 문제

(이글은 자게란에 올린 글이지만, 강좌란에 두는 것이 좋다고 생각되어 옮깁니다.)

밑의 함수는 지인으로 부터 받은 소스에 포함되어 있던 함수중 하나이다.

이 함수의 역할은 컴에 설치된 문서(워드, 엑셀, 아래아한글 등등) 타입들을 알아내고, 이 정보들을 바탕으로 FileOpen 다이얼로그의 filter 속성으로 사용할 문자열을 얻어 내는 것이다. 아래는 그 소스인데, 소스 구조는 매우 간단하다.

function GetFileFilter:String;
var
    checkVal:OleVariant;
begin
    Result := 'Image All(*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.pcx;*.tif;*.tiff;*.psd;*.pdd)|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.pcx;*.tif;*.tiff;*.psd;*.pdd|';
   
    try
        checkVal := CreateComObject(CLASS_HwpCtrl);
        checkVal := Unassigned;
        Result := Result + 'HWP Document(*.hwp)|*.hwp|';
    except
        RemoveFileDocKind(dkHwp);
    end;

    try
        checkVal := CreateOleObject('WORD.Application');
        checkVal := Unassigned;
        Result := Result + 'MS-Word(*.doc)|*.doc|';
    except
        RemoveFileDocKind(dkMsWord);
    end;

    try
        checkVal := CreateOleObject('Excel.Application');
        checkVal := Unassigned;
        Result := Result + 'MS-Excel(*.xls)|*.xls|';
    except
        RemoveFileDocKind(dkMsExcel);
    end;

    try
        checkVal := CreateOleObject('PowerPoint.Application');
        checkVal := Unassigned;
        Result := Result + 'PowerPoint(*.ppt)|*.ppt|';
    except
        RemoveFileDocKind(dkMsPPt);
    end;

    try
        checkVal := CreateComObject(CLASS_IF_);
        checkVal := Unassigned;
        Result := Result + 'HunminWord Document(*.gul)|*.gul|';
    except
        RemoveFileDocKind(dkHun);
    end;

    Result := Result + 'Adobo PDF File(*.pdf)|*.pdf|PostScript(*.ps)|*.ps|Encapsulated PostScript(*.eps)|*.eps|';
end;


그런데 이 함수는 몇가지 문제점이 있다.

첫번째로 모양이 매우 비슷한 try...except 구문이 여러번 반복된다는 것이다. 반복되는 구조의 코드는 제거함이 옳을 것이다.

두번째로 후일 새로운 문서 타입을 추가해야할 때, 이 try except 구문이 또 반복 추가되고, 새 문서 타입 추가하기가 그리 쉽지 않다.

이 두문제를 해결할 수 있도록 이 코드를 정리해 보자. 리팩토링이란 기존 코드의 기능은 그대로 유지하면서도, 더욱 깔끔하고 정갈한 구조를 가지는 코드로 변경하는 작업이다. 이 코드를 깔끔하게 정리해 주시라.

-------------------------------------------------------------------------------------------------------
아래는 이 글에 대한 해답으로 올려준 댓글들입니다.
참여해 주신 분들께 대단히 감사드립니다.

--------------------------------------------------------------------------------
윤승환 님의 리팩토링
--------------------------------------------------------------------------------

function GetFileFilter():String;
  function  AddWordFiler(classid:TGUID;filter:string;docktype:TDockType;var filterlist:string) :string;  overload;
  var
    checkVal  :OleVariant;
  begin
    try
        checkVal := CreateComObject(classid);
        checkVal := Unassigned;
        Result  := filter;
    except
        RemoveFileDocKind(docktype);
        Result  :='';
    end;
  end;
  function  AddWordFiler(classname:string;filter:string;docktype:TDockType;var filterlist:string)  :string;  overload;
  begin
    result  :=AddWordFiler(ProgIDToClassID(classname),filter,docktype,filterlist);
  end;
begin
  result  :='Image All(*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.pcx;*.tif;*.tiff;*.psd;*.pdd)|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.pcx;*.tif;*.tiff;*.psd;*.pdd|';

  AddWordFiler(CLASS_HwpCtrl,'HWP Document(*.hwp)|*.hwp|',dkHwp,Result);
  AddWordFiler('WORD.Application','MS-Word(*.doc)|*.doc|',dkMsWord,Result);
  AddWordFiler('Excel.Application','MS-Excel(*.xls)|*.xls|',dkMsExcel,Result);
  AddWordFiler('PowerPoint.Application','PowerPoint(*.ppt)|*.ppt|',dkMsPPt,Result);
  AddWordFiler(CLASS_IF_,'HunminWord Document(*.gul)|*.gul|',dkHun,Result);

  result  :=result + 'Adobo PDF File(*.pdf)|*.pdf|PostScript(*.ps)|*.ps|Encapsulated PostScript(*.eps)|*.eps|';
end;

---------------------------------------------------------------------------------
이경환.단디
---------------------------------------------------------------------------------
function GetFileFilter:String;
type
  TDocType = Record
    docId : String;
    filter : string;
    dockKind : integer;
    IsOle: Boolean;
  end;
const
  docTypes : array[0..2] of TDocType = (
    (docId:'WORD.Application'; filter:'MS-Word(*.doc)|*.doc|'; dockKind:dkWord; IsOle:true),
    (docId:'Excel.Application'; filter:'MS-Excel(*.xls)|*.xls|'; dockKind:dkExcel; IsOle:true),
    (docId:'{26319A34-0ADB-4F04-8EFD-4E6679644879}'; filter:'HWP Document(*.hwp)|*.hwp|'; dockKind:dkHwp; IsOle:false)
  );
  function checkFileServer(docId: string; docKind: integer; IsOle: Boolean): boolean;
  var
    checkVal: OleVariant;
  begin
    result:= false;
    try
      try
        if IsOle then   
          checkVal:= CreateComObject(docId)
        else
          checkVal:= CreateOleObject(StringToGuid(docId));
        result:= true;
      except
        RemoveFileDocKind(docKind);
      end;   
    finally
      checkVal:= Unassigned;
    end;   
  end;

begin

    Result := 'Image All(*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.pcx;*.tif;*.tiff;*.psd;*.pdd)|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.pcx;*.tif;*.tiff;*.psd;*.pdd|';

    for I := Low(docTypes) to High(docTypes) do
    begin
      if checkFileServer(docTypes[i].oleType, docTypes[i].docKind, docTypes[i].IsOle) then
        begin
          Result := Result + docTypes[i].filter;
        end;
    end;
   
    Result := Result + 'Adobo PDF File(*.pdf)|*.pdf|PostScript(*.ps)|*.ps|Encapsulated PostScript(*.eps)|*.eps|';
end;


---------------------------------------------------------------------------------
미노님의 방법
---------------------------------------------------------------------------------
type
  TDocumentType = (dtHWP, dtWord, dtPowerpoint, dtExcel, dtHunminWord);
  TDocumentFilter = class
  private
    FDocumentType: TDocumentType;
  public
    class function DocumentFilter(Filter: TDocumentType): string;
    function GetDocumentFilter: string;
    property DocumentType:TDocumentType read FDocumentType write FDocumentType;
  end;

const
  DocumentType: array [1..5] of TDocumentType = (dtHWP, dtWord, dtPowerpoint, dtExcel, dtHunminWord);

implementation

{ TDocumentFilter }

class function TDocumentFilter.DocumentFilter(Filter: TDocumentType): string;
begin
  case Filter of
    dtHWP: Result:= 'HWP Document(*.hwp)|*.hwp|';
    dtWord: Result:= 'MS-Word(*.doc)|*.doc|';
    dtExcel: Result:= 'MS-Excel(*.xls)|*.xls|';
    dtPowerpoint: Result:= 'PowerPoint(*.ppt)|*.ppt|';
    dtHunminWord: Result:= 'HunminWord Document(*.gul)|*.gul|';
  else result:= '';
  end;
end;

function TDocumentFilter.GetDocumentFilter: string;
begin
  Result:= DocumentFilter(FDocumentType);
end;


function GetFileFilter:String;
var
  I: Integer;
begin
  Result:= 'Image All(*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.pcx;*.tif;*.tiff;*.psd;*.pdd)|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.pcx;*.tif;*.tiff;*.psd;*.pdd|';
  for I := Low(DocumentType) to High(DocumentType) do
    Result:= Result + TDocumentFilter.DocumentFilter(DocumentType[I]);
  Result:= Result + 'Adobo PDF File(*.pdf)|*.pdf|PostScript(*.ps)|*.ps|Encapsulated PostScript(*.eps)|*.eps|';
end;

---------------------------------------------------------------------------------
주정섭의 방법
---------------------------------------------------------------------------------
// 컴파일 되게 하기 위해서...다음 함수를 구라로 정의
procedure RemoveFileDocKind(aValue: Integer);
begin
end;

// 컴파일 되게 하기 위해서 다음 상수들을 구라로 정의
const dkHwp = 100;
const dkWord = 101;
const dkExcel = 102;

// 리팩토링한 GetFileFilter 함수
function GetFileFilter:String;
type
  TDocType = Record
    comObject : TGUID;
    className : String;
    filter : string;
    dockKind : integer;
  end;
const
  noGuid = '{00000000-0000-0000-0000-000000000000}';
  docTypes : array[0..2] of TDocType = (
    (comObject:noguid; className:'WORD.Application'; filter:'MS-Word(*.doc)|*.doc|'; dockKind:dkWord),
    (comObject:noguid; className:'Excel.Application'; filter:'MS-Excel(*.xls)|*.xls|'; dockKind:dkExcel),
    (comObject:'{26319A34-0ADB-4F04-8EFD-4E6679644879}'; filter:'HWP Document(*.hwp)|*.hwp|'; dockKind:dkHwp )
  );
var
  checkVal:OleVariant;
  I: Integer;

begin
  Result := 'Image All(*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.pcx;*.tif;*.tiff;*.psd;*.pdd)|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.pcx;*.tif;*.tiff;*.psd;*.pdd|';

  for I := Low(docTypes) to High(docTypes) do
  begin
    try
      if GuidToString(docTypes[i].comObject) = noGuid then
        checkVal := CreateOleObject(docTypes[i].ClassName)
      else
        checkVal := CreateComObject(docTypes[i].comObject);

        checkVal := Unassigned;
        Result := Result + docTypes[i].filter;
    except
      RemoveFileDocKind(docTypes[i].dockKind);
    end;
  end;

  Result := Result + 'Adobo PDF File(*.pdf)|*.pdf|PostScript(*.ps)|*.ps|Encapsulated PostScript(*.eps)|*.eps|';
end;

-------------------------------------------------------------------------------------
사악신 님이 올린 리팩토링
-------------------------------------------------------------------------------------

1. 사용하고자 하는 유닛에서 다음처럼 호출
:: 초기화시 사용해도 되고 필요시 동적으로 달고 싶은 놈만 다는 형태로 사용...

procedure TFormFileFilter.ButtonOpenClick(Sender: TObject);
var
  Filters : TStringList;
begin
  Filters := TStringList.Create;
  try
    Filters := TDecoratorImageAll.Create( Filters );
    Filters := TDecoratorHWP.Create( Filters );
    Filters := TDecoratorExcel.Create( Filters );
    Filters := TDecoratorPDF.Create( Filters );

    OpenDialog.Filter := Filters.Text;
    if OpenDialog.Execute then
    begin
      //
    end;
  finally
    Filters.Free;
  end;
end;

2. 데코레이터 소스
:: BDS 에서 지원하는 투게더가 좀 미흡한 관계로(오류가 많습니다. ㅡㅡ) 생산성은 조금 떨어지지만,
잘 활용하면 코드량에 비해서 그렇게 많은 시간이 들어가지 않습니다. ^^
델파이 하위 버전과의 호환성을 위하여 추가된 문법들은 정리하여 제거하였습니다.
해당 문서의 검사루틴은 생략했습니다. 각자 적당한 방법으로 채우거나 구조를 활용하면 되지 않을까 싶어서...
아무튼 구조만 잡아봤습니다.

unit FileFilter.Classes.Documents; // BDS 이전 Delphi 7 이하 버전에서는 네임스페이스를 지원하지 않습니다.

interface

uses
  Classes;

type
  TDecoratorDoc = class(TStringList)
  protected
    FComponent: TStringList;

    function GetDoc: String; virtual; abstract;
  public
    constructor Create(ADecorateMe: TStringList);
    destructor Destroy; override;

    function GetTextStr: String; override;
  end;

  TDecoratorImageAll = class(TDecoratorDoc)
  protected
    function GetDoc: String; override;
  end;

  TDecoratorHWP = class(TDecoratorDoc)
  protected
    function GetDoc: String; override;
  end;

  TDecoratorExcel = class(TDecoratorDoc)
  protected
    function GetDoc: String; override;
  end;

  TDecoratorWord = class(TDecoratorDoc)
  protected
    function GetDoc: String; override;
  end;

  TDecoratorPPT = class(TDecoratorDoc)
  protected
    function GetDoc: String; override;
  end;

  TDecoratorPDF = class(TDecoratorDoc)
  protected
    function GetDoc: String; override;
  end;

  TDecoratorPS = class(TDecoratorDoc)
  protected
    function GetDoc: String; override;
  end;

  TDecoratorEPS = class(TDecoratorDoc)
  protected
    function GetDoc: String; override;
  end;

implementation


{ TDecoratorDoc }

constructor TDecoratorDoc.Create(ADecorateMe: TStringList);
begin
  inherited Create;

  FComponent := ADecorateMe;
end;


destructor TDecoratorDoc.Destroy;
begin
  FComponent.Free;
  FComponent := nil;

  inherited;
end;

function TDecoratorDoc.GetTextStr: String;
var
  Temp: String;
begin
  Temp := inherited GetTextStr;

  Temp := Temp + FComponent.Text + GetDoc;
  Result := Temp;
end;

{ TDecoratorHWP }

function TDecoratorHWP.GetDoc: String;
begin
  Result := 'HWP Document(*.hwp)|*.hwp|';
  try
    // 검사루틴...
  except
    Result := '';
  end;
end;


{ TDecoratorExcel }

function TDecoratorExcel.GetDoc: String;
begin
  Result := 'MS-Excel(*.xls)|*.xls|';
  try
    // 검사루틴...
  except
    Result := '';
  end;
end;


{ TDecoratorImageAll }

function TDecoratorImageAll.GetDoc: String;
begin
  Result := 'Image All(*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.pcx;*.tif;*.tiff;*.psd;*.pdd)|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.pcx;*.tif;*.tiff;*.psd;*.pdd|';
end;


{ TDecoratorPPT }

function TDecoratorPPT.GetDoc: String;
begin
  Result := 'PowerPoint(*.ppt)|*.ppt|';
  try
    // 검사루틴...
  except
    Result := '';
  end;
end;


{ TDecoratorWord }

function TDecoratorWord.GetDoc: String;
begin
  Result := 'MS-Word(*.doc)|*.doc|';
  try
    // 검사루틴...
  except
    Result := '';
  end;
end;


{ TDecoratorPDF }

function TDecoratorPDF.GetDoc: String;
begin
  Result := 'Adobo PDF File(*.pdf)|*.pdf|';
  try
    // 검사루틴...
  except
    Result := '';
  end;
end;


{ TDecoratorPS }

function TDecoratorPS.GetDoc: String;
begin
  Result := 'PostScript(*.ps)|*.ps|';
  try
    // 검사루틴...
  except
    Result := '';
  end;

end;


{ TDecoratorEPS }

function TDecoratorEPS.GetDoc: String;
begin
  Result := 'Encapsulated PostScript(*.eps)|*.eps|';
  try
    // 검사루틴...
  except
    Result := '';
  end; 
end;

end.

+ -

관련 글 리스트
110 델파이 코드 리팩토링 문제 주정섭 6630 2007/01/09
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.