4

Our organization codebase is used by various teams. Over the years, the code has evolved and grown without much usage of interfaces. We would like to change that, to reduce chance of breaking changes.

i.e. change accessing of common components from

Foo foo = new Foo();

to

Ifoo foo = SomeFactory.GetFoo();

My question is - is there a design pattern \ refactoring pattern \ methodology to introduce interfaces to existing legacy code?

It's worth mentioning that we don't have a 'list' of the most important classes we want to protect by converting their usage to interface. Is there a recommended way to obtain such a list?

5

For each class you want to hide behind an interface, do the following:

  1. Use the refactoring tool of your favorite IDE to change the name of the class from Foo to IFoo. Your whole codebase should now still work as before.
  2. Rename the class back to Foo but without using refactoring. Your code should now be full of errors stating that IFoo is unknown.
  3. Create a new interface IFoo and have your class implement it. Many errors should now disappear. But you will still have an error in each line which says new IFoo, stating that IFoo is an interface and thus can not be instanced.
  4. Implement your preferred creational pattern (builder, factory, static factory method, whatever) and use it to fix all these new IFoo errors.
  5. Run your test suit.

But please also take the advise from Doc Brown's answer into account. An Interface only makes sense when you have at least two classes which implement it. Until you do, that interface is just unnecessary boilerplate code which does nothing except adding more complexity. A mocking class in your test suit counts, though.

  • In a way, YAGNI is what led our code to be needing this refactoring. We have separation between core-code to customization-code, and we wish to avoid cases (already happening) of core-code changing in a breaking way. Hence we want our customization code to instantiate IFoo, not Foo. And at same time, 'educate' developers that changing IFoo needs to be done more carefully than changing Foo. Would you suggest other\additional action in this scenario? – Ayal Mar 8 '17 at 9:25
  • @Ayal Educated developers should know that changing the public-facing methods of a class is something you should only do when you know what you are doing. The unnecessary complexity by adding a superfluous interface is of course also a deterrent against people who don't understand more complex software architectures, so whatever works for you. – Philipp Mar 8 '17 at 9:32
  • Side note: interfaces are the first-class citizens of an API, and client code may never even refer to the actual class name of the implementation. Therefore, interfaces preferentially get the "clean" names (Foo) and implementations get the warts (FooImpl). In the OP's case, the interfaces will have to get the warts since the API is already in use by multiple projects. – Kevin Krumwiede Mar 8 '17 at 19:08
6

The best methodology for doing this is YAGNI, or in other words, don't do it beforehand or "just in case".

Do it whereever you really need to change something at the system which adds actual business value. When you are in a situation where you

  • you want to add or improve a feature
  • you want to fix a bug
  • you want to optimize something

you know you need to change something somewhere in your program. And when you notice that introducing an interface makes sense in that context (maybe because of easier unit testing, maybe because of reuse, maybe because of reducing duplicate code), then is the right time to start with an interface.

  • Sometimes one writes interfaces beforehand as a design excercise so junior programmers write implementations. Nobody can think everything beforehand, but a little bit beforehand designing doesn't harm. E.g., I can write a business class that gets injected with a mocked helper that implements an interface and that, for example, returns OK without really doing anything, but then I ask a junior programmer to implement the real helper to complete the functionality. There I designed an interface as a design aid for someone else to code a part that fits nicely into place with an specific contract. – Tulains Córdova Mar 7 '17 at 12:01
  • @TulainsCórdova: sure, but that is IMHO a situation very different from the case described by the OP. And, when you write an interface with the intend someone else will implement it soon, you are not doing this "just in case". – Doc Brown Mar 7 '17 at 12:30

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.