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

简介:

前段日子忙活站点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,如需转载请自行联系原作者

相关文章
|
Prometheus 监控 Cloud Native
在 HBase 集群中,Prometheus 通常监控哪些类型的性能指标?
在 HBase 集群中,Prometheus 通常监控哪些类型的性能指标?
319 2
|
JavaScript 前端开发 安全
TypeScript无缝衔接ArkTS:快速入门鸿蒙ArkTS基本语法
【10月更文挑战第12天】TypeScript无缝衔接ArkTS:快速入门鸿蒙ArkTS基本语法
1101 0
TypeScript无缝衔接ArkTS:快速入门鸿蒙ArkTS基本语法
|
存储 NoSQL Java
Java中使用redis的bitMap实现签到功能
这个实现示例提供了一种灵活、高效的方式,展示了如何使用Redis来解决现实中的问题。
582 2
|
机器学习/深度学习 算法 PyTorch
【深度学习】深度学习基本概念、工作原理及实际应用案例
深度学习是一种机器学习方法,它试图模拟人脑中的神经网络结构,以解决复杂的问题。深度学习的核心在于构建多层非线性处理单元(即神经元)的网络结构,这些网络可以从原始数据中自动提取特征并进行学习。
4170 1
|
消息中间件 Java 数据处理
揭秘Apache Flink的Exactly-Once神技:如何在数据流海中确保每条信息精准无误,不丢不重?
【8月更文挑战第26天】Apache Flink 是一款先进的流处理框架,其核心特性 Exactly-Once 语义保证了数据处理的精准无误。尤其在金融及电商等高要求场景下,该特性极为关键。本文深入解析 Flink 如何实现 Exactly-Once 语义:通过状态管理确保中间结果可靠存储;利用一致的检查点机制定期保存状态快照;以及通过精确的状态恢复避免数据重复处理或丢失。最后,提供一个 Java 示例,展示如何计算用户访问次数,并确保 Exactly-Once 语义的应用。
437 0
|
存储 搜索推荐 JavaScript
【毕业设计之java系列】基于springboot学生社团信息管理系统
【毕业设计之java系列】基于springboot学生社团信息管理系统
821 0
|
网络协议 Linux 网络安全
Linux 禁止某个IP地址访问的几种方法
一、概述 这两个文件是tcpd服务器的配置文件,tcpd服务器可以控制外部IP对本机服务的访问。这两个配置文件的格式如下: #服务进程名:主机列表:当规则匹配时可选的命令操作 server_name:hosts-list[:command] /etc/hosts.allow控制可以访问本机的IP地址,/etc/hosts.deny控制禁止访问本机的IP。
18270 0
|
存储 缓存 安全
数字化医学影像管理系统PACS源码
数字化医学影像管理系统PACS源码
444 0
|
消息中间件 安全 Java
Zookeeper的SASL认证最佳实践
Zookeeper的SASL认证最佳实践
5031 0
Zookeeper的SASL认证最佳实践
|
设计模式 运维 架构师
进大厂,到底需要掌握哪些核心技能?
很多小伙伴问我进大厂到底需要怎样的技术能力,经过几天的思考和总结,终于梳理出一份相对比较完整的技能清单,小伙伴们可以对照清单提前准备相关的技能,在平时的工作中注意积累和总结。只要在平时的工作中总结到位,对技术知其然,知其所以然,进大厂,是水到渠成的事情。接下来,我就简单的列举下,在我个人看来,进大厂需要的一些技能。当然,这次主要说的是Java技术栈。后面也会分享一些其他的技术栈(C++、运维、云原生已安排上了,后面发)。
492 0
进大厂,到底需要掌握哪些核心技能?