前段日子忙活站点Theme的更改,到发布的时候有个问题难住了我,我要修改四十多个Theme的配置文件,这些配置问价也不在一处,整理出来打包很麻烦,而且本地环境和生产环境里面很多配置都不相同,不能通过简单把全文件粘贴过去这种方式替换生产环境的配置文件,只能去修改,后来频繁的修改Theme,终于意思到要写个工具了,之前也有一些修改文件的工具,但都是各行其是,痛定思痛决定写一个可复用的批量文件修改工具,当然本文的重点并不在于怎么查找修改文件,而在于怎么复用。
最开始变成的时候提说过设计模式,急不可耐的读了一番,说实话几乎没有收获,不明白为什么要用设计模式,工作两年后再读设计模式,不敢说收获有多大,但是重构了很多以前的代码,感觉不错,后来自己就情不自禁的在使用一些设计模式了,这这个简单的小工具,工厂模式又帮了大忙
我希望做出来的效果是这样的
1. 可以在某个文件夹下搜索文件,而不是检索整个硬盘(这个是当然)
2. 不仅仅是按照文件全名搜索,还可以使用一些通配符
3. 能够自己决定是否搜索子文件夹
这些都是框架部分,至于怎么去修改文件,肯定只有开发者自己知道,所以这个工具还得有开发者在框架下不影响其它算法,而拓展自己所需算法的功能。也就是说当我想批量修改某些文件的时候我可以仅仅写一个如何修改文件的算法,而不用关注与怎么搜索神马的,同时拓展部分还不能影响其它算法。
需求其实很简单,看看怎么实现,我做的solution目录结构是这样的
Base文件夹下的FileUtility.cs封装了搜索文件的方法,写的不太好,凑活能用
public class FileUtility { public static List<string> GetAllFiles(string path, string pattern, bool searchChildFolder) { List<string> names = new List<string>(); DirectoryInfo di = new DirectoryInfo(path); if (!searchChildFolder) { FileInfo[] fis = di.GetFiles(pattern); foreach (FileInfo fi in fis) { names.Add(fi.FullName); } } else { GetFile(path, pattern, names); } return names; } public static void GetFile(string path, string pattern, List<string> names) { DirectoryInfo di = new DirectoryInfo(path); string[] patterns=pattern.Split(new char[]{'|',','},StringSplitOptions.RemoveEmptyEntries); foreach (string p in patterns) { FileInfo[] fis = di.GetFiles(p.Trim()); foreach (FileInfo fi in fis) { names.Add(fi.FullName); } } DirectoryInfo[] dis = di.GetDirectories(); if (dis.Length != 0) { foreach (DirectoryInfo _di in dis) { GetFile(_di.FullName, pattern, names); } } } }
Algorithm 这个project Assembly name 是SSTool.Algorithm,存放所有修改文件的算法,Base文件夹下的IAlgorithm是所有算法都要实现的接口
public interface IAlgorithm { void execute(List<string> files); }
DemoAlgorithem.cs是一个简单的示例算法,王文件内写一行数据
public class DemoAlgorithem:IAlgorithm { public void execute(List<string> files) { foreach (string path in files) { using (StreamWriter sw = new StreamWriter(path, false)) { sw.WriteLine("This is a text."); } } } }
每添加一个算法就需要在AlgorithmConf.xml添加一条记录
<?xml version="1.0" encoding="utf-8" ?> <algorithms> <item key="Demo" algorithm="SSTool.Algorithm.DemoAlgorithem" /> </algorithms>
这是为后面工厂模式反射获取所有算法做准备
这个是算法的工产类,用于生成算法对象实例,也就是工厂模式中的工厂类了,IAlgorithm是产品类接口,而DemoAlgorithem是一个具体产品,看看怎么生产算法产品吧
我使用了反射的方式获取算法实例,代码很简单
public IAlgorithm CreateUpdateAlgorithm(string key) { Assembly assembly = Assembly.Load("SSTool.Algorithm"); Dictionary<string, string> map = GetConf("AlgorithmConf.xml"); IAlgorithm algorithm = (IAlgorithm)assembly.CreateInstance(map[key]); return algorithm; } public Dictionary<string, string> GetConf(string path) { XmlDocument doc = new XmlDocument(); doc.Load("AlgorithmConf.xml"); XmlNodeList nodes = doc.SelectNodes("//item"); Dictionary<string, string> map = new Dictionary<string, string>(); foreach (XmlNode node in nodes) { map.Add(node.Attributes["key"].Value, node.Attributes["algorithm"].Value); } return map; }
MainFrame.cs用于生成外观及响应事件
为类添加一个属性,用于获取工厂实例
private ArithmeticFactory factory; public ArithmeticFactory Factory { get { if (factory != null) { return factory; } else { factory = new ArithmeticFactory(); return factory; } } }
在Load事件处理程序中使用工厂的GetConf获取所有配置,把key绑定到界面选择算法的Combobox中
protected override void OnLoad(EventArgs e) { Dictionary<string, string> map = Factory.GetConf("AlgorithmConf.xml"); this.cmbArithmetic.Items.Clear(); foreach (KeyValuePair<string, string> pair in map) { this.cmbArithmetic.Items.Add(pair.Key); } base.OnLoad(e); }
1.点击 “Select”按钮的时候调出选择文件件窗口
private void btnSelect_Click(object sender, EventArgs e) { FolderBrowserDialog dialog = new FolderBrowserDialog(); dialog.Description = "Select Folder Path"; if (dialog.ShowDialog() == DialogResult.OK) { this.txtPath.Text = dialog.SelectedPath; } }
2. 点击 “Search” 按钮的时候根据文件夹及Search pattern 搜索文件列表并显示在下面列表中
dgvFiles.Columns.Clear(); List<string> names = FileUtility.GetAllFiles(this.txtPath.Text, this.txtPattern.Text.Trim(), this.ckbRecursive.Checked); DataTable dt = new DataTable(); dt.Columns.Add("File"); foreach (string name in names) { DataRow dr = dt.NewRow(); dr[0] = name; dt.Rows.Add(dr); } dgvFiles.DataSource = dt; dgvFiles.Columns[0].Width = dgvFiles.Width; for (int i = 0; i < dgvFiles.Rows.Count; i++) { dgvFiles.Rows[i].Selected = true; }
3. 在界面上筛选搜索结果,选择算法,点击“Update”按钮,做一些输入验证工作后更新文件,更新算法实例使用工厂获得
private void btnUpdate_Click(object sender, EventArgs e) { string error = ValidateInput(); if (error == null) { IAlgorithm algorithm= Factory.CreateUpdateAlgorithm(this
.cmbArithmetic.SelectedItem.ToString()); List<string> files = new List<string>(); for (int i = 0; i < dgvFiles.Rows.Count; i++) { if(dgvFiles.Rows[i].Selected ==true) { files.Add(dgvFiles.Rows[i].Cells[0].Value.ToString()); } } algorithm.execute(files); this.panDisplay.Enabled = false; this.progressUpdate.Visible = true; this.progressUpdate.Value = 0; this.progressUpdate.Visible = false; this.panDisplay.Enabled = true; MessageBox.Show("Done!", "Update"); } else { MessageBox.Show(error, "Error"); } }
这样一个简单的文件批量修改工具就完成了,点击这里下载源码,其实这个工具很简单,没任何高明之处,反而本着发布去写的小工具甚至有些粗糙,只是想借此说明几点
1. 设计模式与我们日常编程工作并不是遥不可及的,设计模式并不只是架构师的菜,只要认真发觉,我们每天使用的类库中都包含很多设计模式,有时候我们也在使用了,只是没发现
2. 设计模式的学习并不是看几本书、在网上看个大牛写的一系列教程就可以做到的,而在于我们对自己写的代码精益求精,发现违背设计原则的地方不断重构,结合理论指导,自然能够用处设计模式,一旦设计模式是自己慢慢演化去代码得来,相信大家就不在会问为什么要用这个模式、永乐有什么好处、什么场景下用这个设计模式了。
纸上得来终觉浅,绝知此事要躬行。