使用简单工厂写一个可复用的批量文件修改工具

简介:

前段日子忙活站点Theme的更改,到发布的时候有个问题难住了我,我要修改四十多个Theme的配置文件,这些配置文件也不在一处,整理出来打包很麻烦,而且本地环境和生产环境里面很多配置都不相同,不能通过简单把全部文件粘贴过去这种方式替换生产环境的配置文件,只能去修改,后来频繁的修改Theme,终于意识到要写个工具了,之前也有一些修改文件的工具,但都是各行其是,痛定思痛决定写一个可复用的批量文件修改工具,当然本文的重点并不在于怎么查找、修改文件,而在于怎么复用。

最开始编程的时候听说过设计模式,急不可耐的读了一番,说实话几乎没有收获,不明白为什么要用设计模式,工作两年后再读设计模式,不敢说收获有多大,但是重构了很多以前的代码,感觉不错,后来自己就情不自禁的在使用一些设计模式了,这个简单的小工具,工厂模式又帮了大忙

需求

我希望做出来的效果是这样的

image

1. 可以在某个文件夹下搜索文件,而不是检索整个硬盘(这个是当然)

2. 不仅仅是按照文件全名搜索,还可以使用一些通配符

3. 能够自己决定是否搜索子文件夹

这些都是框架部分,至于怎么去修改文件,肯定只有开发者自己知道,所以这个工具还得有开发者在框架下不影响其它算法,而拓展自己所需算法的功能。也就是说当我想批量修改某些文件的时候我可以仅仅写一个如何修改文件的算法,而不用关注与怎么搜索神马的,同时拓展部分还不能影响其它算法。

基础框架

需求其实很简单,看看怎么实现,我做的solution目录结构是这样的

image

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. 设计模式的学习并不是看几本书、在网上看个大牛写的一系列教程就可以做到的,而在于我们对自己写的代码精益求精,发现违背设计原则的地方不断重构,结合理论指导,自然能够用出设计模式,一旦设计模式是自己慢慢演化去代码得来,相信大家就不在会问为什么要用这个模式、用了有什么好处、什么场景下用这个设计模式了。

纸上得来终觉浅,绝知此事要躬行。


    本文转自魏琼东博客园博客,原文链接:http://www.cnblogs.com/dolphinX/p/3320792.html,如需转载请自行联系原作者

相关文章
|
7月前
|
JSON 算法 前端开发
2705. 精简对象
2705. 精简对象
53 0
|
3月前
|
Shell Linux Python
你知道创建模块都有哪些方式吗?
你知道创建模块都有哪些方式吗?
30 0
|
5月前
|
JSON 测试技术 数据格式
软件复用问题之如果待复用的组件是需要新建的,应该如何解决
软件复用问题之如果待复用的组件是需要新建的,应该如何解决
|
存储 JSON 前端开发
Android数据库存储模块封装,让操作记录更好用可复用
Android数据库存储模块封装,让操作记录更好用可复用
|
Scala 开发者
包对象注意事项和细节说明|学习笔记
快速学习包对象注意事项和细节说明。
包对象注意事项和细节说明|学习笔记
|
XML Java Maven
一个封装好的dialog工具类,减少重复的代码,简洁又方便使用!
一个封装好的dialog工具类,减少重复的代码,简洁又方便使用!
一个封装好的dialog工具类,减少重复的代码,简洁又方便使用!
|
存储 SQL 测试技术
手把手带你设计接口自动化测试用例(四):建立配置信息表,执行结果记录表...
手把手带你设计接口自动化测试用例(四):建立配置信息表,执行结果记录表...
179 0
手把手带你设计接口自动化测试用例(四):建立配置信息表,执行结果记录表...
|
前端开发 API PHP
Laravel 模型工厂类 批量生成数据
Laravel Jetstream 是为 Laravel 设计的精美的应用程序脚手架。
185 0
|
JavaScript 开发者
改造原有代码-使用封装的函数实现|学习笔记
快速学习改造原有代码-使用封装的函数实现
|
缓存
读源码长知识 | 动态扩展类并绑定生命周期的新方式
在阅读viewModelScope源码时,发现了一种新的方式。 协程需隶属于某 CoroutineScope ,以实现structured-concurrency,而 CoroutineScope 应
175 0