1

I currently have a class Foo, i've decided I need a second type of Foo where I want to test out a significantly different implementation. It will no doubt share some functionality with Foo so I'll need to create a Abstract Class parent for both of them.

At the end of the process I want to have:

  • AbstractFoo: an abstract class
  • FooBoo: functionaly identical to the original Foo
  • FooBar: the new subclass of AbstractFoo I've created.

There are two ways I could go about this:

Copy Paste, then Refactor

  1. Create a copy of Foo, and name it FooBar and rename Foo to FooBoo
  2. Edit FooBar with the new implementation
  3. Inspect, FooBar and FooBoo, move methods that are the same between them into a abstract class AbstractFoo, which both inherit from

Refactor, then Implement

  1. Rename Foo, FooBoo
  2. Consider what methods are not related to the part of the implementation that FooBoo and FooBaz are going to be different in. Move these to AbstractFoo
  3. Create FooBaz and reimplement all the methods that remain within FooBoo

Which is to be preferred? *Is there another method?*

Last time I had to do this, it was because the I decided that Foo had 2 different modes, that was determined by a boolean passed into the constructor.

This time (on a different class), it is because I want to try out using a different engine underneath that might be faster. I want to keep FooBoo around because it has a subclass that I think will be much harder to implement for FooBaz, and I also want to be able to check that in the end they behave the same.

  • 1
    How about using an interface, if supported by the language? – MikeSW Feb 8 '14 at 11:33
  • I don't think it is, and there are almost definitely large common functionalities. – Lyndon White Feb 8 '14 at 12:33
1

I would do it this way:

  1. Rename Foo to AbstractFoo and make it abstract
  2. Create FooBoo that derives from AbstractFoo
  3. Push down all methods from AbstractFoo to FooBoo leaving behind only abstract methods
  4. Implement FooBar

I like this because most of it can be achieved using refactor functions of IDE. This makes sure that you don't have to manually change code where Foo was used. Also, compiler gives you errors in places where you used Foo's constructor, because it cannot instantiate abstract class.

  • Did you mean to say push down All methods? – Lyndon White Feb 8 '14 at 12:34
  • @Oxinabox: I understood him to mean "all methods that will be specific to FooBoo". – jmoreno Feb 8 '14 at 17:01
1

Hard to say, it depends on the case, what approach makes the most sense.

If most of the stuff is going into the base class, I'd either use your "Refactor, then Implement" approach, or follow a slightly changed variant of @Euphorics' approach: I would make sure everything works as before after his Step 3 (completed AbstractFoo and FooFoo) and before moving on to his Step 4 (creating FooBar).

In some cases, it is the other way round. You decide for a common base class, which actually will have only a few methods from old Foo. In that case, rename Foo to FooFoo, create an empty AbstractFoo and move just the few methods into the new base (don't forget to change FooFoos inheritance).

I have also used the "copy + paste + refactor" approach. It can be useful, when the methods to move to the base compared to the methods that need to stay in FooBoo are around 50:50 distribution. Again, it depends, and sometimes you'll find during refactoring, that you need another class to extract things from the Foo class family.

1

I would go with the copy+paste approach as a starter, that gives you flexibility to do whatever you want with the new class without bringing any unintended side effects into existing code. This would sort of fall under the "tracer application" pattern since your main intent is to try out a completely new engine.

When you have your successful result(s) from your copy+pasted code you will follow the test driven pattern and refactor out the common bits into your abstract base class etc. This would come close to what is discussed in Kent Becks Test-Driven Development by example as well as the common work patterns discussed in the pragmatic programmer.

IMHO the above is how I would approach this problem most of the times since it leaves room for wild crazy experimentation without affecting the already working and tested code with the least amount of effort (eg. no current tests will be modified or broken etc and logic stays intact). The big effort comes when you have your working tracer to either throw it away and write a clean implementation based on your tracer code or to start integrate it with your actual code base.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.