Chapter 6: I Don't Have Much Time, and I Have to Change It (2)

Chapter 6: I Don't Have Much Time, and I Have to Change It (2)

Note of Working Effectively with Legacy Code

Sprout Method

See the previous chapter, please.

Sprout Class

See the last chapter, please.

Wrap Method

See the previous chapter, please.

Wrap Class

Here We go. I think the most crucial concept is the decorator pattern. So let's see this pattern carefully.

class ToolController {
    public raise() {
        console.log('basic raise')
    }
    public lower() {
        console.log('basic lower')
    }
    public step() {
        console.log('basic step')
    }
    public on() {
        console.log('basic on')
    }
    public off() {
        console.log('basic off')
    }
}
abstract class ToolControllerDecorator extends ToolController {
    protected controller: ToolController
    constructor(controller: ToolController) {
        super()
        this.controller = controller
    }
    public raise() {
        console.log('wrapped raise')
        this.controller.raise()
    }
    public lower() {
        console.log('wrapped lower')
        this.controller.lower()
    }
    public step() {
        console.log('wrapped step')
        this.controller.step()
    }
    public on() {
        console.log('wrapped on')
        this.controller.on()
    }
    public off() {
        console.log('wrapped off')
        this.controller.off()
    }
}

class StepNotifyingController extends ToolControllerDecorator {
    private notifyees: String[]
    constructor(controller: ToolController, notifyees: String[]){
        super(controller)
        this.notifyees = notifyees
    }
    public step() {
        this.notifyees.forEach(n => {
            console.log('notifying ' + n)
        })
        this.controller.step()
    }
}
class AlarmingController extends ToolControllerDecorator {
    constructor(controller: ToolController){
        super(controller)
    }
    public on() {
        console.log('fire alarm')
        this.controller.on()
    }
}

class WhatTheHellController extends ToolController {
    constructor() {
        super()
    }
    public on() {
        console.log('do what the hell you want)
        super.on()
    }
}



const controller: ToolController = new StepNotifyingController(
    new AlarmingController(new WhatTheHellController()),
    ['No1', 'No2', 'No3']
)

controller.step()
// [LOG]: "notifying No1" 
// [LOG]: "notifying No2" 
// [LOG]: "notifying No3" 
// [LOG]: "wrapped step" 
// [LOG]: "basic step" 

controller.on()
// [LOG]: "wrapped on" 
// [LOG]: "fire alarm" 
// [LOG]: "do what the hell you want." 
// [LOG]: "basic on"

I know it is weird that I wrote typescript code this way, but it worked, and this code snippet can show us how to decorate our class.

The key to Wrap Class is that you can add new behavior into a system without adding it to an existing class

Summary

Ok, If you want to rename an existed method and replace it with a new one, use Sprout Method.

If your existed method works just fine and you don't want to break its algorithm, use Wrap Method.

The last one, Wrap Class, is a little bit complicated. As I showed you, if the feature you are adding is to log something or do some low-level operation without influencing the main business logic, it's ok to use the Wrap Class.

But that is a rare situation. The everyday use case is that you face an enormous class and don't want to touch it. So you only wrap it.

Sounds silly, right? We want to escape from the enormous work dealing with that existing old class, so we create another class that expended from it?

Here is the theory: At least you should start with classes and methods sprouting around the carcasses of the big ugly classes. It will make you life happier. Once you did it, you will begin to consider how to refactor those old classes.That is the magic of the first step.