リファクタリング (Delphi 2005 以降)

 リファクタリングについては Delphi 2005 の頃からあるのですが、正直全然使っていませんでした。

 昔からの Delphi ユーザには賛同して頂けると思うのですが...

 という事が過去にありまして。そして再現方法がハッキリしないのでフラストレーションが溜まって次第に使わなくなっていました。ちゃんと動作してるヒトはちゃんと動作してたんでしょうけれど...。時は流れて Delphi 2009 の頃。リファクタリングを何気に試してみたら、「アレ?普通に使えるみたいだけど?」という感触を得ました。ちゃんと動作するのなら、当然使わないテはないでしょう。

 "リファクタリング" という言葉に囚われずに、"コードエディタの機能" として覚えると生産効率が向上します。とりあえず、キーボードショートカットが用意されているものだけでも覚えておくといいと思います。



名前の変更

 ショートカット: Shift + Ctrl + E
 メニュー: [リファクタリング | 名前の変更]

 以下のようなコードがあったとします。

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
begin
  for i:=0 to 9 do
    ShowMessage('テストです' + IntToStr(i));
end;

end.

 ボタンが一つだけあるフォームです。

 ここで 変数 i を選択状態にして "Shift + Ctrl + E" を押すと以下のようなダイアログが表示されます。

 新しい名前を "iLoop" としてみましょう。"リファクタリングの前に名前を参照" のチェックは外しておきます。そのまま Enter キーを押すと (もちろん、マウスで OK ボタンを押下してもいいです) ...

procedure TForm1.Button1Click(Sender: TObject);
var
  iLoop: Integer;
begin
  for iLoop:=0 to 9 do
    ShowMessage('テストです' + IntToStr(iLoop));
end;

 置換ではないので IntToStr の I が iLoop にはなっていません。今回は変数 i が一文字だったために選択状態にする必要がありましたが、2 文字またはそれ以上の名前の変数を変更する場合には "キャレットが変数名の内側" にあればいいです。

 次にボタンの名前を変更してみます。先程と同様にコントーロール名を探します...フォームのクラスメンバとして Button1 がありますね。ここにカーソルを置きます。

 そして "Shift + Ctrl + E" を押すと

 新しい名前を "btnTEST" としてみます。

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    btnTest: TButton;
    procedure btnTestClick(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnTestClick(Sender: TObject);
var
  iLoop: Integer;
begin
  for iLoop:=0 to 9 do
    ShowMessage('テストです' + IntToStr(iLoop));
end;

end.

  

 すべてが変更されている事が判ります。今回はコントロール名が他に使われていなかったのでクラス定義部からコントロール名を探しましたが、実装部にコントロール名があれば、そちらからでもリファクタリングは可能です。

See Also:


同期編集

 ショートカット: Shift + Ctrl + J
 メニュー: (なし)

 コレは "リファクタリングパッケージの機能" なんですかね? シンボルの名前変更 と類似の機能があります。

 今度も変数名を変更してみましょう。先程の "iLoop" を "i" に戻してみます。変数が使われている箇所を適当に選択状態にします。

 左の赤丸で囲まれた場所にあるボタンを押下するか、"Shift + Ctrl + J" で同期編集モードに入ります。

 水色の部分が同期編集の範囲です。青い枠は同期編集可能な識別子です。"iLoop" を "i" にしてみます。

 リアルタイムで名前が変更されます。同期編集モードを抜けるには Esc キーを押すか、同期編集範囲外にキャレットを移動させます。

 リファクタリングの シンボルの名前変更 と違い、こちらはあくまでも "ソースコード上での安全な置換" といった認識をすべきでしょう。例えば同期編集ではコントロールの名前を変更しても、フォームのコントロール名までは変更されません。

See Also:


フィールドの宣言

 ショートカット: Shift + Ctrl + D
 メニュー: [リファクタリング | フィールドの宣言]

 クラスメンバ変数を途中で定義したくなったとします。

 えぇ、ループ変数はループを抜けたら変数の値は保証されないのは解っております。意味のないコードですがご容赦下さいね。

 ここで "FIndex" にキャレットを置いて "Shift + Ctrl + D" を押します。

 今回、変数 i は Integer 型という事がハッキリしていますので、FIndex の "種類" は "Integer" と類推されています。間違っていたら修正する事も可能です。クラスメンバ変数は private な "アクセス制御" にしてみます。

 コメントが残念な位置になっていますが、目的は果たせています。

See Also:


変数の宣言

 ショートカット: Shift + Ctrl + V
 メニュー: [リファクタリング | 変数の宣言]

 ローカル変数を途中で定義したくなったとします。

 TBitmap 型の変数が定義したくなりました。"bmpTMP" にキャレットを置いて "Shift + Ctrl + V" を押します。

 変数 bmpTMP の "種類" は "TBitmap" と類推されています。間違っていたら修正する事も可能です。今回はこれで OK なのでそのまま Enter キーを押下します。

 正しく変数 "bmpTMP" が追加されました。

See Also:


コードテンプレートによる変数の宣言

 ショートカット: var と入力後に Ctrl + J
 メニュー: (なし)

 リファクタリングの機能ではありませんが、変数の宣言 と類似の機能がコードテンプレートの機能として存在します。

 ローカル変数を途中で定義したくなったとします。とりあえず "var" と入力します。

 続けて "Ctrl + J" を押します。

 String 型の "strTemp" 変数を定義したいので、まず "LVar" になっている箇所を "strTmp" にします。

 変数名を変更したら Tab キーを押して型の入力を行います。

 型がデフォルトで "Integer" になっていますので "string" に変更します。

 再度 Tab キーを押下すると...

 見事にローカル変数が定義されました。

 この方法では、"結局、変数名と型をタイプしなければならない" のであまりメリットが感じられないかもしれませんが、この機能を使えば "キャレット移動を伴わずに var 部に変数を宣言する事が可能" です。長くなってしまった関数の最下部であってもキャレット移動を伴わずに変数が定義できるので、コードを書く流れを阻害しません。

See Also:


メソッドの抽出

 ショートカット: Shift + Ctrl + M
 メニュー: [リファクタリング | メソッドの抽出]

 失敗したら厄介な事になりそうなので、現在でも利用するのを躊躇しているリファクタリングです。

 以下のようなコードがある場合、

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    btnTest: TButton;
    procedure btnTestClick(Sender: TObject);
  private
    FIndex: Integer;
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnTestClick(Sender: TObject);
var
  i: Integer;
begin
  for i:=0 to 9 do
    ShowMessage('テストです' + IntToStr(i));
  FIndex := i;
end;

end.

 メソッド化したい箇所を範囲選択する...或いは抽出したい箇所が 1 行ならそこへキャレットを置きます。続けて "Shift + Ctrl + M" を押します。

 手続きが抽出できたようです。

 デフォルトの名前が付いていますので、"ShowLoop" という手続き名に変更してみます。

 そのまま Enter キーを押下すると以下のようなコードになります。

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    btnTest: TButton;
    procedure btnTestClick(Sender: TObject);
  private
    FIndex: Integer;
    procedure ShowLoop(i: Integer);
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnTestClick(Sender: TObject);
var
  i: Integer;
begin
  for i:=0 to 9 do
  ShowLoop(i);
  FIndex := i;
end;

procedure TForm1.ShowLoop(i: Integer);
begin
  ShowMessage('テストです' + IntToStr(i));
end;

end.

 「あー、この部分は別にしとけば使いまわせるのに!」 という時に便利そうです。ただ、見ての通り引数名がローカル変数名とカブったりしますので、何らかの修正は必要だと思います。

 むしろ、変数の宣言 と組み合わせたコーディングの方が使い勝手がいいかもしれません。まず、以下のような意味のないコードを記述します。

 "aBMP" と "Count" の上でそれぞれ "Shift + Ctrl + V" を押してローカル変数を作ります。そして以下のように選択し...

 "Shift + Ctrl + M" を押してメソッドを抽出します。

 Bitmap と Integer を引数に持つ手続きが作れました...いや、自分で書いた方が早いな (^^;A

See Also:


リソース文字列の抽出

 ショートカット: Shift + Ctrl + L
 メニュー: [リファクタリング | リソース文字列の抽出]

 これは比較的よく使います。

 多言語対応アプリを作りたいとします。そうすると文字列をどこか一か所に集めてリソース文字列にしなければなりません...が、最初から MessageBox() 等の文字列を別ユニットにイチイチ分けて書いていたら面倒で仕方ありません。そこで...

 リソース文字列化したい文字列の上にキャレットを置き、"Shift + Ctrl + L" を押します。

 ダイアログが表示されました。

 定数の識別子のデフォルトは "Str + 定数" になっています。日本語名の識別子はキモいので、"StrTEST" に変更します。

 そしてそのまま Enter キーを押下します。

 resourcestring 部に定数が追加されました。後はこの部分を別ユニットに集約すれば多言語化が比較的簡単に行えます。

See Also:


パラメータの変更

 ショートカット: Shift + Ctrl + X
 メニュー: [リファクタリング | パラメータの変更]

 「思うようには行かないよね┐(´ー`)┌」 第一弾です。以下のようなコードがあった場合に、

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    function SumValue(Value1, Value2, Value3: Integer): Integer;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function TForm1.SumValue(Value1, Value2, Value3: Integer): Integer;
begin
  result := Value1 + Value2 + Value3;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  a,b,c: Integer;
begin
  a := 10;
  b := 20;
  c := 30;
  ShowMessage(SumValue(a, b, c));
end;

end.

 SumValue() の引数は 2 つでよかったとします。宣言部でも実装部でも利用している箇所でもいいのでキャレットをメソッド (関数 / 手続き) 名の上に置き、"Shift + Ctrl + X" を押します。

 今回は引数の削除なので "Value3" を選択し、[削除] ボタンを押して [OK] ボタンを押下します。

 ところが、このままでは何も起きません。リファクタリングのキューに溜められているだけ (名前の変更 にあったようなチェックボックスが何故か存在しない) なので、[表示 | リファクタリング] でリファクタリングウィンドウを表示します。

 赤丸で囲まれたボタンを押し、リファクタリングを確定させます。余談ですが、このボタンのショートカットは "Ctrl+R" なので、コードエディタとドッキングさせていると "置換ダイアログ" が表示されてしまいます...orz

 さて、結果や如何に?

 ...とまぁ、残念な事になる訳です。このような結果になるのなら、最初から手で修正した方がいいように思えます。

See Also:


ユニットの検索

 ショートカット: Shift + Ctrl + A
 メニュー: [リファクタリング | ユニットの検索]

 「思うようには行かないよね┐(´ー`)┌」 第二弾です。

 単純に定義されてるユニットが判らない時に、その識別子にキャレットを乗せて "Shift + Ctrl + A" を押下するだけです。例えば...

procedure TForm1.Button1Click(Sender: TObject);
var
  Dir: TDirectory;
begin

end;

 このようなコードの場合、"TDirectory" にキャレットを置いて "Shift + Ctrl + A" を押すと、

 ダイアログが現れ、ユニットを追加したい場所を "インターフェイス (interfaceuses)" か "実装部 (implementationuses)" かを選ぶ事ができます。追加先を選んで Enter を押すと uses に "IOUtils" が追加されてメデタシメデタシ...のハズなんですが。ちょっと欠点がありまして。

 ...前者はともかくとして、"関数や手続き" のユニットの検索ができないのは片手落ちのような気がします。uses に既に含まれているユニットの "関数 / 手続き" は何故か検索対象なのですが...意味ないですよね。「最初からユニット名が判るのならユニット検索なんかしない」のだし。

 自分で作った 「Unit2 にある TForm2 を Unit1 で使いたい」という用途にはいいかもしれませんが、そもそもそういう場合には「追加しますか?」ダイアログが出るので、この機能の出る幕はなかったりします...orz。

See Also:


 BACK