Refactoring to move a private field from one class to its helper class?
Asked Answered
A

6

10

EDIT : I submitted an Eclipse enhancement request for this refactoring.

Is there a way to move a private field from one class to its helper class? The below chicken-scratch UML shows what I'm doing manually right now. Class C1 has private field and a private final reference to a Helper object before the refactoring.

After the refactoring, all references in C1' to field are changed to helper.getField() and helper.setfield() as appropriate.

UML Diagram

class Field {}

class C1 {
   final private Field field;
   final private Helper helper;

   public Field getField() {
      return field;
   }

   public C1() {
      helper = new Helper();
      field = new Field();
   }
}

class Helper {}

class C1Prime {
   final private HelperPrime helper;

   public Field getField() {
      return helper.getField();
   }

   public C1Prime() {
      helper = new HelperPrime();
   }
}

class HelperPrime {
   final private Field field;
   public HelperPrime() {
      field = new Field();
   }
   public Field getField() {
      return field;
   }
}

I've used Eclipse's refactoring capabilities quite a bit, but I can't figure out a way to automate this.

For instance, ideally I would drag the private field/attribute/member from one class to another and hope that Eclipse asks me how I want to handle the unresolved references. It offers no suggestions and breaks all of the references.

The operation that I've been repeating is to separate knowledge and behavior that doesn't really belong in the current class. I'm moving attributes and behavior that references certain fields out of the original class into a new "helper" class.

The first step in my refactoring is to move the fields. A reference to the helper class exists as a field in the class I'm refactoring from. In order not to break C1 during the refactoring, I think it would be nice if Eclipse offered to to generate getters and setters in Helper' and update the references in C1 to use the getters/setters in the new class.

Ambit answered 4/10, 2010 at 20:42 Comment(2)
For information, which version of Eclipse are you using?Pipes
3.5, but I'd try upgrading to 3.6 if this feature were improved. However, I read the release notes and it appears little changed in 3.6 with respect to refactoring.Ambit
T
2

The thing is, your field must be private (I don't even know why the ability to create public non-final fields exists). So how could you possibly access it from another class?

If you don't wish to break it during a refactor I can give you a little trick I use that kinda helps sometimes.

When you create your class, make it either an inner class of the existing class or a second class in the same file.

If you make it an inner class you can copy the methods over first and they can refer to the member in the other class. Once you get all the functionality ported over you can move the variable. At this point none of the references to the variable should be updated since you access it the same way regardless of which class it was in.

Making a class a second class in the same file can be nice if you are splitting apart functionality as well--it lets you access everything at once without mousing between windows. When you are done just drag the new class into it's own java file, recalculate imports, reformat make it public and save it.

I nearly always use one of these methodologies when creating a new class that interacts with existing classes.

Truant answered 4/10, 2010 at 21:15 Comment(0)
Y
8

Well, it simply doesn't make sense for this to work in general. Semantically, it's a weird operation. If you move the field to a new, disjoint class (say from String to Integer) the code that referenced it wouldn't have an instance of the new class to use to get the instance field.

So there are only special cases where it makes sense: when the field is a static member, or you're moving it to a parent class.

For static members, it works fine to right-click on the field you want to move (the variable name), and click Refactor->Move. Choose the new type. References will automatically be updated (try it yourself and see)

For when you're moving it to/from a parent class, you can use Refactor->Pull Up or Push Down, but this will not automatically change references for you (only an issue with Push Down; with Pull Up polymorphism dictates that the references are all still fine).

Yelmene answered 4/10, 2010 at 20:58 Comment(2)
Of course this makes sense. The helper class is not a disjoint class; it's a helper class that is contained by the first and has the same timeline as the referencing class C1. The helper class is a final field, so is always present and accessible.Ambit
@Jeff I answered this two years ago, and before you provided the clarifications in your question that changed it significantly. My point still stands; in general moving a field to another class is not always possible.Yelmene
T
2

The thing is, your field must be private (I don't even know why the ability to create public non-final fields exists). So how could you possibly access it from another class?

If you don't wish to break it during a refactor I can give you a little trick I use that kinda helps sometimes.

When you create your class, make it either an inner class of the existing class or a second class in the same file.

If you make it an inner class you can copy the methods over first and they can refer to the member in the other class. Once you get all the functionality ported over you can move the variable. At this point none of the references to the variable should be updated since you access it the same way regardless of which class it was in.

Making a class a second class in the same file can be nice if you are splitting apart functionality as well--it lets you access everything at once without mousing between windows. When you are done just drag the new class into it's own java file, recalculate imports, reformat make it public and save it.

I nearly always use one of these methodologies when creating a new class that interacts with existing classes.

Truant answered 4/10, 2010 at 21:15 Comment(0)
C
1

Won't right click the field > Refactoring > Move do?

Yes, it does not update references, but imagine what would it have to do - in all places where your field is referenced, it will have to instantiate the new class. That's not practical.

Comity answered 4/10, 2010 at 20:46 Comment(3)
No, Eclipse's move field refactoring basically just cuts and pastes the field, which is surprising given how powerful the other refactorings in Eclipse are.Ambit
Or a little more intelligently, it would notice I already have an reference to the object in my class and offer to use that instead.Ambit
+1 This works perfectly fine for constants. Easily updates the references tooVolution
S
1

The move operation on a field won't work appropriately. You can move a field but eclipse won't move the getters and setters. It's an intresting question if this is possible i don't think it is.

Screed answered 4/10, 2010 at 20:49 Comment(1)
This is what I was afraid of. I'll have to submit an enhancement request. I can't see any reason why this shouldn't be easily automated.Ambit
P
0

For a Java function, you can simply right click on its name, then select Refactor > Move. In the wizard, select the new class that will now manage this method.

I think this can help you in your task, even if you can't choose several elements at once that must be moved...

Pipes answered 4/10, 2010 at 20:46 Comment(4)
No, Eclipse's move field refactoring basically just cuts and pastes the field, which is surprising given how powerful the other refactorings in Eclipse are.Ambit
Also, you can choose as many as you want and Eclipse will move them. It just does nothing about the unresolved references to it.Ambit
I just tried in Eclipse Helios to move one method from one class to another, and Eclipse automatically updated my JUnit tests. So for me, it's not a simple copy / paste operation here...Pipes
My question regards fields, not methods.Ambit
S
0

If the target class doesn't already exist, you can use Refactor > Extract Class. This will create a new class containing the fields you select, and add a field of this type into your original class.

I'm not sure if you can do it for a class that does exist - but you could always use Extract Class and then cut'n'paste its contents into your new class.

Surber answered 4/10, 2010 at 20:53 Comment(1)
Right, but then the references to the temporary new class are not updated to access the class that I just pasted them into. This won't be any better than just moving them directly to the desired class.Ambit

© 2022 - 2024 — McMap. All rights reserved.