Jump to content
Registration disabled at the moment Read more... ×
PeterPanettone

Refactoring suggestion: Move current procedure to new separate unit

Recommended Posts

Moving the current procedure to a new separate unit is a common desire when a unit starts to grow too large, and you realize a specific feature set could be broken out into its own module. It would be fantastic if MMX could have a ONE-STEP refactoring to achieve this.

 

The Cumbersome, Manual, Tool-Assisted Refactoring Process: Let's assume you have UnitA.pas and you want to move ProcedureA and all its helper functions into a new UnitB.pas:

 

Step 1: Create the New Unit


First, create your new, empty unit. Go to File > New > Unit - Delphi.


Save it as UnitB.pas.

Decide on the structure. Will UnitB contain a class (TMyFeatureManager) or will it be a collection of standalone procedures? For this example, let's assume you're creating a new class, as that's the most common and robust approach.

// In UnitB.pas:

unit UnitB;

interface

type
  TMyFeatureManager = class
  private
    // Fields that were in UnitA's form will go here
  public
    procedure ProcedureA;
    // ... other public methods will go here
  end;

implementation

{ TMyFeatureManager }

procedure TMyFeatureManager.ProcedureA;
begin
  // Code will go here
end;

end.


Step 2: Use the "Find Usages" and "Go to Definition" Tools


In UnitA.pas, right-click on ProcedureA and select Search For Usages. This will show you every place where ProcedureA is called.


Inside ProcedureA, right-click on every single procedure it calls (e.g., Helper1, Helper2) and use Go to Definition (Ctrl+Click) to jump to their source. This helps you identify all the "connected procedures" you need to move.


Make a list of all the procedures, functions, and private fields/variables that are part of this feature set.


Step 3: Use the "Move" Refactoring (Safest Method)


The Move refactoring is your best friend here. It will automatically update all references to the moved member.


Move the Procedures:
In UnitA.pas, right-click on a helper procedure (e.g., Helper1) that you want to move.


Select Refactor > Move....
The "Move" dialog will appear. In the "Target Scope" or "Destination" field, select your new TMyFeatureManager class in UnitB.
Click OK.


What happens: Delphi will physically move the code for Helper1 into UnitB.pas, make it a method of TMyFeatureManager, and automatically add UnitB to the uses clause of UnitA if needed. It will also update any calls inside UnitA to correctly reference the method in the new class.


Move the Fields:
Do the same for any private fields from UnitA's form that are only used by these procedures. Right-click the field, choose Refactor > Move..., and select TMyFeatureManager as the destination.

 

Repeat this process for every single procedure and field on your list. This is a painstaking but very safe process, as the IDE is managing all the references for you.


Step 4: Handle Dependencies


After moving the code, you will likely have some compiler errors. These are usually dependency-related.


UnitA depends on UnitB: The Move refactoring should have already added UnitB to UnitA's uses clause.
UnitB depends on UnitA (or other units): Your new TMyFeatureManager class will likely need to reference components or methods from the original form in UnitA.


The Wrong Way (Causes Circular References): Do NOT add UnitA to the uses clause in the interface section of UnitB. This will almost certainly cause a circular unit reference.


The Right Way:
Add UnitA to the uses clause in the implementation section of UnitB.
Pass a reference to the form from UnitA into your new class's constructor.
Example of the Correct Dependency Handling:

// In UnitB.pas:

 

unit UnitB;

interface

uses
  Vcl.Forms; // Or whatever base class your form has

type
  // Forward declare the form class from UnitA
  TFormA = class;

  TMyFeatureManager = class
  private
    FOwnerForm: TFormA; // A reference to the form that owns this manager
  public
    constructor Create(AOwnerForm: TFormA);
    procedure ProcedureA;
  end;

implementation

uses
  UnitA; // Now we can add the full unit reference here

{ TMyFeatureManager }

constructor TMyFeatureManager.Create(AOwnerForm: TFormA);
begin
  FOwnerForm := AOwnerForm;
end;

procedure TMyFeatureManager.ProcedureA;
begin
  // Now you can safely access components on the original form
  FOwnerForm.Button1.Caption := 'Done';
end;

end.

// In UnitA.pas:
 

procedure TFormA.FormCreate(Sender: TObject);
begin
  // Create an instance of the manager and give it a reference to ourselves
  FMyFeatureManager := TMyFeatureManager.Create(Self);
end;

procedure TFormA.Button1Click(Sender: TObject);
begin
  // Call the procedure in the new, separate unit
  FMyFeatureManager.ProcedureA;
end;

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×