前段日子忙活站點Theme的更改,到發布的時候有個問題難住了我,我要修改四十多個Theme的配置文件,這些配置文件也不在一處,整理出來打包很麻煩,而且本地環境和生產環境里面很多配置都不相同,不能通過簡單把全部文件粘貼過去這種方式替換生產環境的配置文件,只能去修改,后來頻繁的修改Theme,終于意識到要寫個工具了,之前也有一些修改文件的工具,但都是各行其是,痛定思痛決定寫一個可復用的批量文件修改工具,當然本文的重點并不在于怎么查找、修改文件,而在于怎么復用。
最開始編程的時候聽說過設計模式,急不可耐的讀了一番,說實話幾乎沒有收獲,不明白為什么要用設計模式,工作兩年后再讀設計模式,不敢說收獲有多大,但是重構了很多以前的代碼,感覺不錯,后來自己就情不自禁的在使用一些設計模式了,這個簡單的小工具,工廠模式又幫了大忙
需求
我希望做出來的效果是這樣的
1. 可以在某個文件夾下搜索文件,而不是檢索整個硬盤(這個是當然)
2. 不僅僅是按照文件全名搜索,還可以使用一些通配符
3. 能夠自己決定是否搜索子文件夾
這些都是框架部分,至于怎么去修改文件,肯定只有開發者自己知道,所以這個工具還得有開發者在框架下不影響其它算法,而拓展自己所需算法的功能。也就是說當我想批量修改某些文件的時候我可以僅僅寫一個如何修改文件的算法,而不用關注與怎么搜索神馬的,同時拓展部分還不能影響其它算法。
基礎框架
需求其實很簡單,看看怎么實現,我做的solution目錄結構是這樣的
FileUtility.cs
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
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>
這是為后面工廠模式反射獲取所有算法做準備
ArithmeticFactory.cs
這個是算法的工產類,用于生成算法對象實例,也就是工廠模式中的工廠類了,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
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. 設計模式的學習并不是看幾本書、在網上看個大牛寫的一系列教程就可以做到的,而在于我們對自己寫的代碼精益求精,發現違背設計原則的地方不斷重構,結合理論指導,自然能夠用出設計模式,一旦設計模式是自己慢慢演化去代碼得來,相信大家就不在會問為什么要用這個模式、用了有什么好處、什么場景下用這個設計模式了。
紙上得來終覺淺,絕知此事要躬行。
文章列表