开发过程随笔系列之一 ----- Url小写替换工具

Url小写替换工具

                                                                                                        --------- 开发过程随笔系列之一

项目简介

集团产品部在进行SEO的过程中,发现搜索引擎对大小写不同的相同Url认为是不同链接,所以提出建议:将url进行统一. 因为项目较多,手动替换比较费时,所以做了简单的小工具,可以批量进行替换。

开发周期2

源代码位置

$root/J架构项目/研发类/2008/ Url小写批量替换 在此工具的开发过程中,遇到一些问题,有几处值得拿出来跟大家讨论一下的场景,因此整理了一下,感兴趣的可以跟帖讨论。

简单的代码重用:     

  此工具需要两个基本功能:备份,小写替换。因此开始时直接定义了两个方法 Backup(string dir)LowercaseLink(string dir),开发完成后,查看了一下代码,发现一个问题: 即文件夹遍历,这两个方法都对文件夹进行了一次遍历,其代码是重复的(.Net虽好,但并不万能,例如文件夹的复制就没有实现。需要自己来开发。)。因此我在想有什么方法能够将遍历的代码只写一遍,后来发现BackupLowercaseLink这两个方法参数和返回值相同,因此想到了委托(好东西呀,我也不是很懂),代码如下: public delegate void HandleMethod(string filepath);

public void Backup(string fileName)

{     //此处省略若干字

}

public void LowercaseLink(string filepath)

{     //此处省略若干字

}

public void Traversal(string dirName,HandleMethod method)

{

  string[] subDirs = Directory.GetDirectories(dirName);

  foreach (string subDir in subDirs)

  {       Traversal(subDir, method);

  }   string[] files = Directory.GetFiles(dirName);   foreach (string file in files)

  {       method.Invoke(file);

  }

} 调用如下: Traversal(selectedFolder, Backup); Traversal(selectedFolder, LowercaseLink); Traversal的第二个参数为委托类型(相当于函数指针),调用时需要将实际的方法名传递过去,然后通过Invoke来触发。         .Net3.5 Linq语言和对列表的排序方法中,可以见到很多委托的影子。感兴趣的可以看下。

业务处理和表现层分离:       

  在第一版完成后,基本功能已经实现,能实现将文本中的所有链接的批量替换。此时所有的代码都放在了Form1.cs文件中,只是通过不同的方法和属性来区分。大致代码如下: public bool IsBackup;

public delegate void HandleMethod(string filepath);

public List<string> linkPatterns = new List<string>();

public string selectedFolder = string.Empty;

private void btnStart_Click(object sender, EventArgs e){}

private void btnSelectFolder_Click(object sender, EventArgs e){}

//方法代码

public void Backup(string fileName){}

public void Traversal(string dirName,HandleMethod method){} public void InitPattern(){} public void LowercaseLink(string filepath){}         在对代码进行了一些考虑以后,发现下面的四个方法和上面的两个方法是不同的。上面是界面代码的事件响应(属于表现层),下面的才是真正的业务处理(属于业务处理层)。后来又添加一次操作状态和信息的响应,此时仍然堆积在 Form1 中,代码体积开始变得庞大和混乱。   因此对代码进行了分离。         分离后代码如下: public class LowercaseManager         public ProjectStatus DoTask(LowercaseManagerStartupInfo startInfo){} public bool CheckBackup(string folder){}; public void TraversalBackup(string dirName, LowercaseManagerStartupInfo startInfo){}; public void Traversal(string dirName, HandleMethod method){};         public void LowercaseLink(string filepath){} public class LowercaseManagerStartupInfo         public bool IsBackup; public string BackupFolder; public string ProjectFolder;

public enum ProjectStatus

{     NoProject,     ProjectFolderNotExist,     NeedBackupFolder,

    Success

} public partial class Form1 : Form

public const string NEED_BACKUPFLODER = "请选择备份文件夹";

public const string MSG_TITLE = "提示";

public const string NEED_PROJECT = "请选择要替换的项目文件夹";

public const string PROJECT_FOLDER_NOT_EXIST = "项目文件夹不存在";

public const string SUCCESS = "替换成功"; private void btnStart_Click(object sender, EventArgs e) private void btnSelectFolder_Click(object sender, EventArgs e) private void btnBackup_Click(object sender, EventArgs e) private void cbIsBackup_CheckedChanged(object sender, EventArgs e) 这还有一个意外,刚开始时,并没有想到使用LowercaseManagerStartupInfo这个参数类,而是将参数放到了LowercaseManager里面,后来想到调用线程类时ThreadProcess等类都将入口参数变为了类xxxxStartInfo,因此这里简单的模仿了一下,连名字都抄了过来((_))         在进行拆分时,有一个想法:在面向过程的开发中,是以函数作为开发单元;而在面向对象的开发中,是以类作为开发单元的。

扩展与可配置化        

截止此时,代码已经开发完毕。下午再次浏览代码时,突然想到,如果要实现将匹配的字符串替换为大写或者替换为其他要求(首字母大写,统一替换为xxx等),这时应该怎么做。

        最开始的想法是直接在public void LowercaseLink(string filepath)方法中添加 switch来实现。代码可能如下:

foreach (string pattern in linkPatterns)

{

    MatchCollection urls = Regex.Matches(fileContent, pattern);

    foreach (Match url in urls)

    {

        switch(replaceType)

        {

            case "lower":

                fileBuilder.Replace(url.Value, url.Value.ToLower());

                break;

            case "upper":

                fileBuilder.Replace(url.Value, url.Value.ToUpper());

                break;

            ...

        }

    }

} 但是这样的设计感觉并不是很好,同时不利于配置,扩展起来也不是特别严谨和清晰。忘了哪位大人物曾经说过:“当你看到switch分支过多的时候,可以想想策略,命令等模式”。 呵呵,策略模式来敲门了(那些大人物如果看到实现的并不是策略模式的话,别敲我头就好了) 于是实现如下:

public interface IReplace

{

    string ToDest(string src);

}

public class ReplaceFactory

{

  public static IReplace lower = new LowerReplace();   public static IReplace CreateInstance()

  {

      //配置return (IReplace)Assembly.Load(path).CreateInstance(className);

      return lower;

  }

} public class LowerReplace : IReplace { // } public class UpperReplace : IReplace { // }         注意红色那行代码,当我们需要替换为不同的目的字符时,那么创建不同类的示例即可(path,className从配置文件加载)。如果没有你要的类,则需要继承 IReplace接口,建立自己的类。 这样便可规范类,方便管理和扩展,同时实现了可配置化         此时就会遇到一个问题:原来我们的类叫做:LowercaseManager和LowercaseManagerStartupInfo,只是为替换小写使用,现在可替换为不同的目标字符串,所以需要来类名已经不太合适。此时对类名进行了更改:ReplaceManager和ReplaceManagerStartupInfo         在配置方面新增加了一个类ConfigHelper.cs辅助读取配置文件。

 设想(以下出现的代码暂时没写,只是预想)         至此,算是做了一些代码重码。在我把工具交给王泊、应龙进行简单的测试时,出现了一些问题: 1. 有些文件不需要进行过滤,例如扩展名为exedll 2. 类似<a href="<%# Eval(Container, "Url")%>" > 类似的并没有考虑。 3. 原来的正则表达式区分了大小写。 因此对程序进行简单的更改。 针对问题1 ,直接在代码里进行了扩展名的判断。 public List<string> exts;

exts = new List<string>();

exts.Add(".cs");

exts.Add(".html");

if (exts.Contains(Path.GetExtension(filepath).ToLower())) //对指定文件进行替换

{ }

但是如果做成一个简单过滤器似乎是更好的选择 , 代码可能如下:

public interface IFilter

{

    string DoFilter(string input);

} public class FileFilter : IFilter 针对问题2 偷了个懒,直接将代码放到了LowerReplace里面,对包含eval,bind等关键字的情况进行了判断,包含此关键字的则不进行替换. 实际上不进行替换也是替换的一种,也可以做成类,叫做NoReplace,实现可能如下: public class NoReplace : IReplace 这样连同我们已经实现的替换类,现在已经有很多了: public class NoReplace : IReplace public class UpperReplace : IReplace public class LowerReplace : IReplace 以后可能更多。这时就会出现一个问题,在使用ReplaceFactory.CreateInstance()进行Replace实例创建时,到底要创建哪个类。配置文件只能解决单一类的情况,但是现在要根据字符串包含的某些字符或者其他特征判断后,动态的进行创建。 当然可以在Factory类型里面进行判断,也可以建立单独类 public class ReplaceRouter / ReplaceMap 将其路由到正确的Replace类。

(此处可能需要探讨,大家注意。) 针对问题3 这个很好解决,忽略大小写即可。 现在我们可以做个简单的设想,如果要做成通用的字符串替换工具,在现有的基础上应该如何扩展?这个问题大家可以讨论下。                 一路走来,就会发现,其实设计和架构并不是一开始就能确定的,而是随着问题的不断深入,设计的不断迭代而逐步成型的。这需要时间,精力和经验,更需要不断的自我否定,对代码艺术的不断追求,否则所谓的重构,设计只能是空谈。

(转载公司的一位架构师的,目的在于促使自己养成写开发随笔的好习惯)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值