[原]实例-简单设计&精简代码&复用代码
引言
本文以实际项目为例谈一谈我个人对于软件开发的理解,偏细节
软件项目B
基于.net平台,使用WPF框架,c#语言,MVVM模式开发的桌面软件
该软件支持可视化的设计器功能,允许所见即所得的方式为页面添加文字、图像等元素。可对元素进行编译解析,生成对应的二进制数据下发至下位机,本文不对软件整体设计做介绍,仅列举部分设计及编码细节进行介绍
独立的Model层数据类型
Model层作为独立的数据访问层,数据类型定义保持独立,仅记录数据本身,ui无关
结构如下图
BGProject 项目类型,组合多个BGDiargam视图对象
BGDiagram视图类型,组合多个BGElement元素对象,存在多个派生元素类型
View层在使用数据时,可封装视图数据类型组合Model数据类型,以记录其他UI相关数据
适度封装以简化代码
编译过程需对文字、图片等做不同处理
初期实现时仅实现了一个TextCompiler,后续陆续实现ImageCompiler等,遂提取抽象基类CompilerBase
形成如下结构
Compile方法由各个派生类自行实现编译逻辑
上层编译逻辑的实现,简单使用多态,如下
public bool Compile(BGProject project, out String errorMessage) { TextCompiler textCompiler = new TextCompiler(project); ImageCompiler imageCompiler = new ImageCompiler(project); XxxCompiler xxxCompiler = new XxxCompiler(project); foreach (CompilerBase compiler in new CompilerBase[] {textCompiler, imageCompiler, XxxCompiler}) { compiler.Compile(); if (!compiler.Validate(out errorMessage)) { return false; } } // ... }
Don't Repeat Yourself 复用代码
每一种数据的编译逻辑中,都需要遍历相应类型的元素,因此考虑将元素遍历逻辑独立出来
为基类TravelCompilerBase添加如下方法
protected static void TravelElements<T>(BGProject project, BGElementType elementType, Action<T> action) where T : BGElement { foreach (BGDiagram diagram in project.Diagrams) { foreach (T element in diagram.Elements.Where(e => e.ElementType == elementType)) { action(element); } } }
处理逻辑通过action参数传递进来
TextCompiler中编译文字元素时,调用上述方法,通过lambda表达式生成匿名方法完成处理逻辑
TravelElements<BGTextElement>(Project, BGElementType.Text, element => { //.... 针对目标元素做相应处理 });
处理该特定问题,这里使用委托的方式,泛型化使其易于使用,你也可以使用TemplateMethod模式
合宜地使用静态类型封装基本工具类型
静态类型是一种良好组织独立工具method的方式
许多不专业的程序员常将静态类型作为存储全局对象的容器,这其实是在破坏软件结构。应尽一切可能避免使用静态类型变量。
序列化工具类
项目中涉及数据序列化到本地文件,直接使用如下工具类型
public static class SerializeUtility { public static void BinarySave<T>(String filePath, T obj) { using (Stream stream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None)) { IFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream, obj); } } public static T BinaryLoad<T>(String filePath) { return BinaryLoad<T>(filePath, null); } public static T BinaryLoad<T>(String filePath, SerializationBinder serializationBinder) { if (!File.Exists(filePath)) { return default(T); } using (Stream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None)) { IFormatter formatter = new BinaryFormatter(); if (serializationBinder != null) { formatter.Binder = serializationBinder; } return (T)formatter.Deserialize(stream); } } }
int与byte数组转换工具类
涉及到传输数据至下位机,考虑数据存储格式,编写如下工具类,支持littleEnding与bigEnding
// Created by Ant 2014-4-30 public static class BytesConverterUtility { public static byte[] GetBytes(int value, int length, bool isLittleEndian = true) { if (value < 0) { throw new ArgumentException("value不能为负数"); } if (length > 4) { throw new ArgumentException("length不能>4"); } var rawBytes = BitConverter.GetBytes(value); if (rawBytes.Length < length) { throw new ApplicationException( String.Format("BitConverter.GetBytes返回的字符数{0}小于目标字符数{1}", rawBytes.Length, length)); } var bytes = new byte[length]; if (BitConverter.IsLittleEndian != isLittleEndian) { Array.Reverse(rawBytes); Array.Copy(rawBytes, rawBytes.Length - length, bytes, 0, length); } else { Array.Copy(rawBytes, bytes, length); } return bytes; } public static int ToInt(byte[] bytes, int offset, int length, bool isLittleEndian = true) { if (length == 1) { return bytes[offset]; } var tempBytes = new byte[length]; Array.Copy(bytes, offset, tempBytes, 0, length); if (!isLittleEndian) { Array.Reverse(tempBytes); } switch (length) { case 2: // 特殊处理,转换为无符号int类型,返回时自动转换为Int32 return BitConverter.ToUInt16(tempBytes, 0); case 4: // 注意,这里将数据转换为有符号int类型 return BitConverter.ToInt32(tempBytes, 0); default: throw new ArgumentException("length 长度非标准值"); } } }
工具类型的方便之处在于其独立性,几乎无外部依赖,不需要考虑对其进行初始化,拿来就可以直接使用
本站内容均为原创,转载请注明出处
作者:Gods_巨蚁 QQ:517377100
出处:http://www.cnblogs.com/gods/
多编码 多总结 厚积薄发
Github博客 hungryant.github.io
作者:Gods_巨蚁 QQ:517377100
出处:http://www.cnblogs.com/gods/
多编码 多总结 厚积薄发
Github博客 hungryant.github.io