Refactoring with Interface

I like refactoring

Refactoring code involves extracting an interface from a complex, large class.

class LanguageProcessor:
    def concat(self, prev_text, new_text):
        if self.language == 'en':
            ... # Concatenate logic for English
        elif self.language == 'ja':
            ...

In my understanding, the interface should be:

  • Clear input/output design
  • Reduced code
  • Simple, easy to understand (Single responsibility principle)

In this case, I would do something like this:

class BaseConcatter:
    # Define interface
    @abstractmethod
    def concat(self, prev_text: str, new_text: str):
        ...

class CustomConcatException(Exception):
    pass

class EnglishConcatter(BaseConcatter):
    ...

class JapaneseConcatter(BaseConcatter):
    ...

class Language(Enum):
    ENGLISH = auto()
    JAPANESE = auto()
    ...

concatters = {
    Language.ENGLISH: EnglishConcatter(),
    Language.JAPANESE: JapaneseConcatter(),
}

Then, the original code becomes:

class LanguageProcessor:
    def __init__(self):
        self.concatters = concatters

    def concat(self, prev_text, new_text):
        if self.language not in self.concatters:
            raise SomeException('Unsupported language to concat.')
        result = self.concatters[self.language].concat(prev_text, new_text)
        ...

Design the interface, implement them, create factories, and reduce unnecessary if/else blocks.

So, it seemed good to me, but actually, it wasn't.

Stupid Mistake

There is some code like this:

try:
    language_processor.concat(prev_text, new_text)
except ExistingException:
    # Some handling
except Exception:
    # Cancel process

If you add a new exception in your concatter, like CustomConcatException, the code will break. In the previous code, the existing exception was handled, but no new custom exception was accounted for.

It would always handle it as an exception and cancel the process. This can be problematic because not every concat error should lead to cancellation (this is what I actually experienced).

Why did it happen?

My understanding of the interface was not complete. It's not just about input/output — you also need to consider everything that can be observed from the outside of the process.

In this case, the exception handling is crucial. My refactoring missed that point.

Lesson Learned

The interface should:

  • Have clear input/output and exception handling design
  • Reduce code
  • Be simple, easy to understand (Single responsibility principle)

Especially when refactoring existing code, exception handling might be even more important.