Break out command object
Tags: refactoring, legacycode
Published: 2021-09-07 13:04:43

---
title: Break out command object
published: true
date: 2021-09-07 12:56:29 UTC
tags: Refactoring,Legacycode
canonical_url: https://blog.snowfrog.dev/replace-method-with-command/
---

 ![Break out command object](https://blog.snowfrog.dev/content/images/2021/09/pablo--42-.png)

In many applications, long functions are very hard to work with. In a legacy codebase, they are often contained in large classes that have many dependencies. The work that it takes to bring such functions and classes into a test harness is often significant and may even be overkill for the changes you need to make.

If the function that you need to work with is large or uses instance variables and functions, consider using _Break Out Command Object_. To make a long story short, this refactoring technique gets you to move a long function to a new class. Objects created using that new class are called command objects because they are mostly built around a single function, whose request and execution is the purpose of the object. It is an object that encapsulates a request, following the command pattern in [Design Patterns](https://amzn.to/3zsLypY).

<iframe width="356" height="200" src="https://www.youtube.com/embed/QIGzlJimfVI?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

After you’ve used _Break Out Command Object_, you can often write tests for the new class easier than you could for the old function. Local variables in the old function can become instance variables in the new class. Often that makes it easier to break dependencies and improve the code.

Here's an example in Typescript (large parts of the class have been removed to preserve pixels):

```ts
class ApplicantInfoComponent {
  applicantCommonModel = new ApplicantCommonEditModel();
  isSmartMoveActive = false;
  datePickerComponents: QueryList<CustomDatePicker>;
  ...
  
  constructor(private flashMessageService: NotifyFlashMessageService, ...) {
    ...
  }
  
  validateDates() { ... }
  ...
  
  validateBirthDate(finalValidation) {
    let isValid = true;
    let tempDate = new Date(this.applicantCommonModel.DateOfBirth);

    if (
      this.isSmartMoveActive && new Date(
        tempDate.getFullYear() + 18, tempDate.getMonth() - 1, 
        tempDate.getDay()
      ) >= new Date()
    ) {
      isValid = false;
      let birthDate = this.datePickerComponents['_results']
        .find(item => item.Name == 'birthDate');
      birthDate.invalidDate = true;
      this.flashMessageService.warning('Age must not be less than 18 years.');
    }
    else if (
      this.isSmartMoveActive && new Date(
        tempDate.getFullYear() - 125, tempDate.getMonth() - 1,
        tempDate.getDay()
      ) >= new Date()) {
      isValid = false;
      let birthDate = this.datePickerComponents['_results']
        .find(item => item.Name == 'birthDate');
      birthDate.invalidDate = true;
      this.flashMessageService.warning('Age must not be more than 125 years.');
    }
    if (finalValidation)
      return isValid && this.validateDates();
    return isValid;
  }
  ...
}
```

The `ApplicantInfoComponent` class has a long function named `validateBirthDate`. We can’t easily write tests for it, and it is going to be very difficult to create an instance of `ApplicantInfoComponent` in a test harness. Let’s use _Break Out Command Object_ to move `validateBirthDate` to a new class.

The first step is to create a new class that will do the birth date validation work. We can call it `BirthDateValidator`. After we’ve created it, we give it a constructor. The arguments of the constructor should be a reference to the original class, and the argument to the original function.

```ts
class BirthDateValidator {
  constructor(component: ApplicantInfoComponent, finalValidation: boolean) {}
}
```

You might be looking at this and saying, “Wait a minute, it looks like we are going to end up in the same place. We are accepting a reference to a `ApplicantInfoComponent`, and we already decided that we don't want to instantiate one of those in our test harness. What's the point of all this?” Wait, we are going to make things better.

After we’ve made the constructor, we can add another function to the class, a function that will do the work that was done in the `validateBirthDate()` function. We can call it `validate()`.

```ts
class BirthDateValidator {
  constructor(component: ApplicantInfoComponent, finalValidation: boolean) {}
    
  validate() {}
}
```

Now we add the body of the `validateBirthDate()` function to `BirthDateValidator`. We copy the body of the old `validateBirthDate()` function into the new `validate()` function.

```ts
class BirthDateValidator {
  constructor(component: ApplicantInfoComponent, finalValidation: boolean) {}
    
  validate() {
    let isValid = true;
    let tempDate = new Date(this.applicantCommonModel.DateOfBirth);

    if (
      this.isSmartMoveActive && new Date(
        tempDate.getFullYear() + 18, tempDate.getMonth() - 1, 
        tempDate.getDay()
      ) >= new Date()
    ) {
      isValid = false;
      let birthDate = this.datePickerComponents['_results']
        .find(item => item.Name == 'birthDate');
      birthDate.invalidDate = true;
      this.flashMessageService.warning('Age must not be less than 18 years.');
    }
    else if (
      this.isSmartMoveActive && new Date(
        tempDate.getFullYear() - 125, tempDate.getMonth() - 1,
        tempDate.getDay()
      ) >= new Date()) {
      isValid = false;
      let birthDate = this.datePickerComponents['_results']
        .find(item => item.Name == 'birthDate');
      birthDate.invalidDate = true;
      this.flashMessageService.warning('Age must not be more than 125 years.');
    }
    if (finalValidation)
      return isValid && this.validateDates();
    return isValid;
  }
}
```

If the `validate()` on `BirthDateValidator` has any references to instance variables or functions from `ApplicantInfoComponent`, our compile will fail. To make it succeed, we can make getters for the variables and make the functions that it depends on public. In this case, we depend on a few variables and one `validateDates()` function, all of which are public except for the `flashMessageService` variable. After we make it public on `ApplicantInfoComponent`, we can access it from a reference to the `BirthDateValidator` class and the code compiles.

Now we can make `ApplicantInfoComponent`’s `validateBirthDate` function delegate to the new `BirthDateValidator`.

```ts
class ApplicantInfoComponent {
  ...
  
  validateBirthDate(finalValidation) {
    birthDateValidator = new BirthDateValidator(this, finalValidation);
    birthDateValidator.validate();
  }
  ...
}
```

Now back to the `ApplicantInfoComponent` dependency. The whole reason why we decided to create a new class is that `ApplicantInfoComponent` would be too hard to instantiate in a test harness. As of right now, we still have that problem because our new `BirthDateValidator` class still depends on a reference to `ApplicantInfoComponent`. What we can do is extract a new interface to break the dependency on `ApplicantInfoComponent` completely. To do that we create a new interface,  we'll call it `DateValidator`. Then we change the reference that the `BirthDateValidator` holds from `ApplicantInfoComponent` to `DateValidator`, compile, and let the compiler tell us what functions and properties have to be on the interface. Here is what the code looks like at the end:

```ts
interface DateValidator {
  applicantCommonModel: ApplicantCommonEditModel;
  isSmartMoveActive: boolean;
  datePickerComponents: QueryList<CustomDatePicker>;
  flashMessageService: NotifyFlashMessageService;
  validateDates(): boolean;
}

class ApplicantInfoComponent {
  ...
  
  validateBirthDate(finalValidation) {
    birthDateValidator = new BirthDateValidator(this, finalValidation);
    birthDateValidator.validate();
  }
  ...
}
  
class BirthDateValidator {
  constructor(component: DateValidator, finalValidation: boolean) {}
    
  validate() {
    let isValid = true;
    let tempDate = new Date(component.applicantCommonModel.DateOfBirth);

    if (
      component.isSmartMoveActive && new Date(
        tempDate.getFullYear() + 18, tempDate.getMonth() - 1, 
        tempDate.getDay()
      ) >= new Date()
    ) {
      isValid = false;
      let birthDate = component.datePickerComponents['_results']
        .find(item => item.Name == 'birthDate');
      birthDate.invalidDate = true;
      component.flashMessageService.warning('Age must not be less than 18 years.');
    }
    else if (
      component.isSmartMoveActive && new Date(
        tempDate.getFullYear() - 125, tempDate.getMonth() - 1,
        tempDate.getDay()
      ) >= new Date()) {
      isValid = false;
      let birthDate = component.datePickerComponents['_results']
        .find(item => item.Name == 'birthDate');
      birthDate.invalidDate = true;
      component.flashMessageService.warning('Age must not be more than 125 years.');
    }
    if (finalValidation)
      return isValid && component.validateDates();
    return isValid;
  }
}
```

Now that we've extracted the code that we need to modify to a new class, we're ready to write some tests, refactor and make whatever changes we came here to make. You can see me work with _Break Out Command Object_, and the following steps of writing tests and refactoring code, [this video series I made on working with legacy code](https://www.youtube.com/watch?v=QIGzlJimfVI).  
  
Suggested reading:

- [Refactoring: Improving the Design of Existing Code, by Martin Fowler](https://amzn.to/3t7y7Jv)
- [Working Effectively with Legacy Code, by Michael Feathers](https://amzn.to/3tel2hK)