Answer Ron's questions about C# Refactorings.

Published 25 July 04 12:19 PM

On the Yahoo! Groups Refactoring DL, Ron Jeffries asked some questions about Visual C# 2005 Refactorings. 

 

"Can you say how you expect the Microsoft refactorings to stack up against the existing ones that are out there?"

 

I've personally spent very little time exploring the C# and Java Refactoring tools out there.  Most of what I know is from the competitive analysis efforts of the PMs on the team, and what I read on this group.  I keep meaning to get some first-hand experience with them, but never find the time.  (Too busy reading Ron's book!)

 

The most obvious difference will be that the Refactoring menu in Visual Studio will be quite a bit shorter than the others.  We decided to focus on making a smaller set of higher quality Refactorings.

 

The other big difference is that our Refactorings are very good at giving you reliably correct results.  It is our goal that when applied to a legal C# program, you can be sure that your code will maintain its behavior.   

 

The build on this laptop has 7 Refactorings:

 

  • Rename
  • Extract method

 

These two are by far the most important that we built.  They rank very high on “difficulty to do by hand” and “value to your code”. 

 

  • Reorder parameters
  • Remove parameters
  • Promote Local Variable to parameter

 

These three together are “change method signature”.  These aren’t nearly as important for your code as Rename & Extract Method, but they can be quite hard to do by hand.  Note that Add Parameter is missing.  Promote Local takes its place, providing a more code-focused experience.

 

  • Encapsulate Field

 

This was easy to build on top of the technology that provides Rename, and it leverages C#’s properties so we can say we’re cooler than that other language.  You can get by without it in a few steps including a Rename, so it doesn’t rank so well on the “hard to do by hand” scale.

 

  • Extract Interface

 

As we read through the Fowler list, this one seemed simple to do, so we did it.  However, it’s not something you need to use very often, so I’m not sure I’d choose if I had to do it over.  Perhaps I would have selected a streamlined Push Up Method instead, which could fill the same needs and others. 

 

One of the themes we picked up on part-way through is that there’s value in building smaller, simpler Refactorings, and letting users compose them at will. 

 

We can build them with a simpler UI.  Instead of a bunch of options, you get to select which Refactorings to apply when.  For example, Promote Local doesn’t provide any flexibility about where in the list to insert the new parameter; it always inserts at the start of the list.  We expect that you’ll use Reorder Parameters to get the results you want.

 

Is there any plan to make refactoring support pluggable, or otherwise to improve the ability of normal humans to generate code?

 

We don’t intend to build Refactoring extensibility into this product.  Bummer, huh?  I hope we can get to it soon, though; it's clearly important.

 

One of the features we did build is “Generate Method Stub”, where we will take a call to a method that doesn’t exist & generate a stub that matches.  It’s great for TDD & Programming by Intention.

 

Comments

# Nicholas Allen said on July 25, 2004 1:03 PM:
I'm a big fan of the fewer choices/higher quality strategy (I think I've said this before). I use Rename more than everything else put together so having a great implementation is better than a few extra refactorings. The only refactorings not on that list I've used more than a few times are Push Method Up/Down and Use Supertype.

I don't ever see them in books, but I'm surprised security oriented refactorings aren't talked about more. I'd think things like Lock Down Class, Minimum Grant Assembly, or Enumerate Permissions would be wildly popular at least internally. Maybe there's just not a lot of customer demand for things like that.
# jaybaz [MS] said on July 25, 2004 3:29 PM:
Nicholas: Thanks for the feedback. The more I think about the fewer features/higher quality tradeoff, the more I'm glad we took the path we did.

Regarding Push Up/Down, it always seemed like there wasn't a lot of value for us to add here.

If all you're doing is taking a method in a derived class and pushing it to a base so that other derived types can see it, then cut/paste seems to work very well.

If the other derived types have a similar method that you want to merge (the scenario described by Fowler, IIRC) then you'll need to do a bunch of pre-work to get both methods to have the same name, signature, and behavior. To say "first go do 2 hours of Refactoring by hand, and then click this button for the easy part" seems almost insulting!

Basically, it seems like all the hard work has to be done by hand anyway.

Am I way off base here? Maybe I really don't understand the way you would use these Refactorings if we automated them. Maybe there is really more value we could provide than I realize. Fill me in!

# Nicholas Allen said on July 25, 2004 5:21 PM:
Here are my core scenarios for Use Supertype (all the value from Push Up was really coming from here) and Push Down. I've got some class SubFoo : Foo that I'm working with and I don't like how the methods are distributed between them.

Use Supertype: I want to hoist a method from SubFoo to Foo. This may take a lot of pre-work as you mentioned. I subscribe to the OO principle that you should use the most general type that can work. So now I want any SubFoo (or Foo2 or XYZFoo or other implementation) that can be replaced with a Foo as a result of this change to get updated.

I also get a lot of code where people demand an ArrayList when any old list will do. It drives me nuts cause I never seem to have the correct type. Give me a one-click way to get their code into shape. I don't want to run change parameter type 100 times.

Push Down: I want to drop a method from Foo to SubFoo. Things are very likely to fail when I do this. I've got another refactoring program that uses a chatty, prompt the user for everything philosophy. I don't know if this scenario can be converted to the Whidbey philosophy.

The refactoring won't work without a little help. I get a big dialog box that reads like:

Hey! That's not going to work! But if I changed these 12 uses of Foo to SubFoo I think we'd be okay.

[X] public void fooIt (/Foo/ foo)
...
...

Why don't you look over these and get back to me if you're still up for this?

[Ok] [Cancel]

To fit in with the other refactorings you've got, I think you have to be confident enough to do it without prompting.
# jaybaz [MS] said on July 25, 2004 7:37 PM:
Very interesting. We'll talk about it some more, to see if there's anything we can do to help.
# Jim Arnold said on July 26, 2004 3:32 AM:
'One of the features we did build is “Generate Method Stub”'

Two things - can you change the smart tag thingy to automatically select the first option in the list, or at least auto-select the *only* option in the list?

Second, would it be easy enough to add a "Generate Type" feature too? Just automating the process of going to the project menu, clicking Add Class, selecting the template (yawn), and giving it a name you've already defined would be a huge time-saver.

Jim
# Dr. Robert J. Pefferly Jr. said on July 26, 2004 5:34 AM:
Dear Nicholas,

I am quite interested in your text (see below). I have done a quick lit review and not found much... can you provide more information?

My contact info is at www.pefferly.com - thanks for your time.

Yours, Rob

----------------
I don't ever see them in books, but I'm surprised security oriented refactorings aren't talked about more. I'd think things like Lock Down Class, Minimum Grant Assembly, or Enumerate Permissions would be wildly popular at least internally. Maybe there's just not a lot of customer demand for things like that.
# Nicholas Allen said on July 27, 2004 1:48 PM:
Rob-

Generally, a program that runs in a restricted environment will run equivalently in a less restrictive environment. Increasing the visibility of a method or property from private to public should not break any callers. Granting permissions that aren't asked for shouldn't alter the external behavior. If you allow too much access, you are unlikely to notice the problem unless you specifically look for security problems.

Finding the most restrictive environment in which your program will continue to function is difficult. If you want to decrease visibility, you must examine all your callers to make sure they still see the correct program elements. If you want to refuse permissions, you must examine all your callees to make sure they don't require that permission.

These seem like excellent candidates for computer assistance in refactoring.

So, I was considering program transformations that preserve your intended external behaviors while eliminating some unintended behaviors.

Moreover, good security design demands certain programming patterns. For instance, you want security violations to fail quickly. An example is with file access. If you test for file existence (reporting an error if the file is not found) before you test for file access permissions, there is a vulnerability. Hostile code can acquire information about your system by seeing which attempts are "not found" and which are "not permitted". Again though, preventing this requires checking up front for all of the permissions that will be needed later. Something a lot of people would need automated assistance for.
New Comments to this post are disabled

This Blog

Syndication

Page view tracker