Filter mode and responsibility chain mode

Posted by artist-ink on Tue, 23 Jun 2020 05:41:30 +0200

The filter pattern is also called the standard pattern, which mainly uses different standards to filter a group of objects. The process of filtering is a process of layer by layer filtering, so the filter pattern is a kind of structural design pattern.

In the actual development, the use of filter pattern is often combined with responsibility chain pattern. So here also need to introduce the responsibility chain mode.

As the name implies, the responsibility chain mode is to create a chain. All objects and data processed by the chain are processed in turn. Each link is responsible for processing different businesses. Each link is decoupled from each other independently and can be reused at the same time.

Students who have used gulp should have a good understanding of the responsibility chain, because the file processing mechanism of gulp itself is designed based on the responsibility chain pattern, using chain call, and different loaders are responsible for different processing of files. The actual webback is also processed based on this mode, only omitting the calling and instantiation process, and only keeping the configuration of loader and plugin.

Scene simulation

Suppose we edit news or forum content through the rich text editor of the client, but the background needs to filter the submitted content according to the following rules:

  1. Automatically label articles
  2. Special characters that may contain need to be masked or replaced
  3. Replace sensitive words such as pornography, violence, crime, etc
  4. Add article source and time description at the end of all submissions

Code example

In order to better simulate object-oriented writing, the sample code uses TypeScript

class Msg {
    msg: string;
    setMsg(msg): void {
        this.msg = msg;
    }
    getMsg(): string {
        return this.msg;
    }
}

interface Filter {
    doFilter(m: Msg): boolean;
}

// Filter danger html tags
class HtmlFilter implements Filter {
    doFilter(m: Msg) {
        let r: string = m.getMsg();
        r = r.replace('<', '[');
        r = r.replace('>', ']');
        m.setMsg(r);
        return true;
    }
}
// Add top Tags
class HotFilter implements Filter {
    doFilter(m: Msg) {
        let r: string = m.getMsg();
        r = "\n Hot News:\n \n" + r;
        m.setMsg(r);
        return true;
    }
}
// Time and source of supplementary information
class SourceWhenFilter implements Filter {
    doFilter(m: Msg) {
        let r: string = m.getMsg();
        r = r + "\n\nSource from CNN, ";
        r = r + this.getDateStr();
        m.setMsg(r);
        return true;
    }
    getDateStr(): string {
        let d: Date = new Date(),
            YYYY = d.getFullYear(),
            MM = d.getMonth() + 1,
            dd = d.getDate(),
            hh = d.getHours(),
            mm = d.getMinutes();
        str = `${YYYY}-${MM >= 10 ? MM : '0' + MM}-${dd >= 10 ? dd : '0' + dd} ${hh >= 10 ? hh : '0' + hh}:${mm >= 10 ? mm : '0' + mm}`;
        return str;
    }
}
// Filter sensitive words
class SensitiveFilter implements Filter {
    doFilter(m: Msg) {
        let r: string = m.getMsg();
        r = r.replace("kill", "murder");
        r = r.replace("riots", "parade");
        m.setMsg(r);
        return true;
    }
}

class FilterChain implements Filter {
    filters: Array<Filter> = [];
    doFilter(m: Msg) {
        for (let f of this.filters) {
            if (!f.doFilter(m)) return false;
        }
        return true;
    }
    add(f: Filter): FilterChain {
        this.filters.push(f);
        return this;
    }
}




let msgObj: Msg = new Msg();
msgObj.setMsg("<Police kneel down to kill black people in America which have Leading to continued riots in the United States.>");

let fc: FilterChain = new FilterChain();
fc.add(new HtmlFilter())
    .add(new SensitiveFilter());

let fc2: FilterChain = new FilterChain();
fc2.add(new HotFilter())
    .add(new SourceWhenFilter());

fc.add(fc2);

fc.doFilter(msgObj);

let str: string = msgObj.getMsg();
console.log(str);

The cleverness of this design lies in that it can be called in chain, and different filtering methods can be flexibly sorted and combined. You can use a single Filter for processing, or you can add a responsibility chain directly. As mentioned above, HtmlFilter can be added by fc, and fc2 can be added directly by fc. Because they all inherit the Filter class, the returned data types are the same, which is difficult to reflect in JavaScript. This is also the reason why we use TypeScript as the example code.

Compared with filters, responsibility chain is a very common and important design pattern, because it provides an extensible data or object processing scheme, you can write your own plug-in based on any rules, just follow the definition of responsibility chain. If you want to try to write your own plug-in, you can implement a simple gulp plug-in first. For specific implementation, you can view related blogs Introduction to automatic build tool gulp (3)

Topics: TypeScript Javascript