重构uMarkdown

代码及setup请见github: js-refactor

代码说明

uMarkdown是一个用于将Markdown转化为HTML的库。代码看上去就像一个很典型的过程代码:

  1. /* code */
  2. while ((stra = micromarkdown.regexobject.code.exec(str)) !== null) {
  3. str = str.replace(stra[0], '<code>\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '<br/>').replace(/\ /gm, '&nbsp;') + '</code>\n');
  4. }
  5. /* headlines */
  6. while ((stra = micromarkdown.regexobject.headline.exec(str)) !== null) {
  7. count = stra[1].length;
  8. str = str.replace(stra[0], '<h' + count + '>' + stra[2] + '</h' + count + '>' + '\n');
  9. }
  10. /* mail */
  11. while ((stra = micromarkdown.regexobject.mail.exec(str)) !== null) {
  12. str = str.replace(stra[0], '<a href="mailto:' + stra[1] + '">' + stra[1] + '</a>');
  13. }

选这个做重构的开始,不仅仅是因为之前在写EchoesWorks的时候进行了很多的重构。而且它更适合于,重构到设计模式的理论。让我们在重构完之后,给作者进行pull request吧。

Markdown的解析过程,有点类似于Pipe and Filters模式(架构模式)。

Filter即我们在代码中看到的正规表达式集:

  1. regexobject: {
  2. headline: /^(\#{1,6})([^\#\n]+)$/m,
  3. code: /\s\`\`\`\n?([^`]+)\`\`\`/g

他会匹配对应的Markdown类型,随后进行替换和处理。而``str```,就是管理口的输入和输出。

接着,我们就可以对其进行简单的重构。

(ps: 推荐用WebStrom来做重构,自带重构功能)

作为一个示例,我们先提出codeHandler方法,即将上面的

  1. /* code */
  2. while ((stra = micromarkdown.regexobject.code.exec(str)) !== null) {
  3. str = str.replace(stra[0], '<code>\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '<br/>').replace(/\ /gm, '&nbsp;') + '</code>\n');
  4. }

提取方法成

  1. codeFilter: function (str, stra) {
  2. return str.replace(stra[0], '<code>\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '<br/>').replace(/\ /gm, '&nbsp;') + '</code>\n');
  3. },

while语句就成了

  1. while ((stra = regexobject.code.exec(str)) !== null) {
  2. str = this.codeFilter(str, stra);
  3. }

然后,运行所有的测试。

  1. grunt test

同理我们就可以mailheadline等方法进行重构。接着就会变成类似于下面的代码,

  1. /* code */
  2. while ((execStr = regExpObject.code.exec(str)) !== null) {
  3. str = codeHandler(str, execStr);
  4. }
  5. /* headlines */
  6. while ((execStr = regExpObject.headline.exec(str)) !== null) {
  7. str = headlineHandler(str, execStr);
  8. }
  9. /* lists */
  10. while ((execStr = regExpObject.lists.exec(str)) !== null) {
  11. str = listHandler(str, execStr);
  12. }
  13. /* tables */
  14. while ((execStr = regExpObject.tables.exec(str)) !== null) {
  15. str = tableHandler(str, execStr, strict);
  16. }

然后你也看到了,上面有一堆重复的代码,接着让我们用JavaScript的奇技浮巧,即apply方法,把上面的重复代码变成。

  1. ['code', 'headline', 'lists', 'tables', 'links', 'mail', 'url', 'smlinks', 'hr'].forEach(function (type) {
  2. while ((stra = regexobject[type].exec(str)) !== null) {
  3. str = that[(type + 'Handler')].apply(that, [stra, str, strict]);
  4. }
  5. });

进行测试,blabla,都是过的。

  1. Markdown
  2. should parse h1~h3
  3. should parse link
  4. should special link
  5. should parse font style
  6. should parse code
  7. should parse ul list
  8. should parse ul table
  9. should return correctly class name

快来试试吧, https://github.com/artisanstack/js-refactor

是时候讨论这个Refactor利器了,最初看到这个重构的过程是从ThoughtWorks郑大晔校开始的,只是之前对于Java的另外一个编辑器Eclipse的坏感。。这些在目前已经不是很重要了,试试这个公司里面应用广泛的编辑器。