我们这里实现一个在线markdown编辑器,可以把一些特定的基础markdown语法映射为对应的HTML标签。仓库代码地址在这里
下面谈谈具体代码
责任链决定用哪个标签
这种模式适合回答这样一个问题:我应该处理这个标签吗?如果是否定的,就把这个往下传递。像链表一样将一系列处理流程链接在一起。
具体来说,责任链的代码可以构造成这样:
abstract class Handler<T> {
protected next : Handler<T> | null = null;
// 指定类链中下一个类
public SetNext(next : Handler<T>) : void {
this.next = next;
}
public HandleRequest(request : T) : void {
if (!this.CanHandle(request)) {
if (this.next != null) {
this.next.HandleRequest(request);
}
return;
}
}
protected abstract CanHandle(request : T) : boolean;
}
我们可以使用一个类来实现基类CanHandle的抽象方法:
class ParseChainHandler extends Handler<ParseElement> {
private readonly visitable : IVisitable = new Visitable();
protected CanHandle(request: ParseElement): boolean {
let split = new LineParser().Parse(request.CurrentLine, this.tagType);
if (split[0]){
request.CurrentLine = split[1];
this.visitable.Accept(this.visitor, request, this.document);
}
return split[0];
}
constructor(private readonly document : IMarkdownDocument,
private readonly tagType : string,
private readonly visitor : IVisitor) {
super();
}
}
在这个类上可以派生出很多不同的具体功能的类出来,当然也可以使用工厂模式来去生成不同功能的类:
class Header1ChainHandler extends ParseChainHandler {
constructor(document : IMarkdownDocument) {
super(document, "# ", new Header1Visitor());
}
}
class Header2ChainHandler extends ParseChainHandler {
constructor(document : IMarkdownDocument) {
super(document, "## ", new Header2Visitor());
}
}
最后我们再把这些派生类拼装一下,做出一条这样的职责链出来:
class ChainOfResponsibilityFactory {
Build(document : IMarkdownDocument) : ParseChainHandler {
let header1 : Header1ChainHandler = new Header1ChainHandler(document);
let header2 : Header2ChainHandler = new Header2ChainHandler(document);
let header3 : Header3ChainHandler = new Header3ChainHandler(document);
let horizontalRule : HorizontalRuleHandler = new HorizontalRuleHandler(document);
const quoteRuleHandler: QuoteRuleHandler = new QuoteRuleHandler(document)
let paragraph : ParagraphHandler = new ParagraphHandler(document);
header1.SetNext(header2);
header2.SetNext(header3);
header3.SetNext(horizontalRule);
horizontalRule.SetNext(quoteRuleHandler);
quoteRuleHandler.SetNext(paragraph)
return header1;
}
}
访问者模式
访问者模式建议将新行为放入一个名为访问者的独立类中, 而不是试图将其整合到已有类中。 现在, 需要执行操作的原始对象将作为参数被传递给访问者中的方法, 让方法能访问对象所包含的一切必要数据。
也就是说我们不需要修改原有的代码,而是重新构造一个新的类称为访问者,新功能都在访问者里面,由他去拜访原有的功能,实现灵活扩展。
举个现实中的例子,比如说你会接到卖保险的电话,他根据你自身特点向你推销保险产品,也可能会接到卖理财产品的电话,他根据你自身特点,向你推销理财产品。我们本身没有变化,但是通过访问者的不同,实际上对我们自身能力实现了灵活的扩展。
对于我们的markdown编辑器,访问者可以这样设计,当发生访问行为的时候,从被访问者身上获取一些数据Visit,操作一些行为:
interface IVisitor {
Visit(token : ParseElement, markdownDocument : IMarkdownDocument) : void;
}
abstract class VisitorBase implements IVisitor {
constructor (private readonly tagType : TagType, private readonly TagTypeToHtml : TagTypeToHtml) {}
Visit(token: ParseElement, markdownDocument: IMarkdownDocument): void {
// 添加一段html
markdownDocument.Add(this.TagTypeToHtml.OpeningTag(this.tagType), token.CurrentLine, this.TagTypeToHtml.ClosingTag(this.tagType));
}
}
class Header1Visitor extends VisitorBase {
constructor() {
super(TagType.Header1, new TagTypeToHtml());
}
}
被访问者可以这样设计,里面有一个Accept方法,把对应数据或者信息交给访问者:
interface IVisitable {
Accept(visitor : IVisitor, token : ParseElement, markdownDocument : IMarkdownDocument) : void;
}
class Visitable implements IVisitable {
Accept(visitor: IVisitor, token: ParseElement, markdownDocument: IMarkdownDocument): void {
visitor.Visit(token, markdownDocument);
}
}