引言

本文以实际项目为例谈一谈我个人对于软件开发的理解,偏细节
 
软件项目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 < )
{
throw new ArgumentException("value不能为负数");
} if (length > )
{
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, , length);
}
else
{
Array.Copy(rawBytes, bytes, length);
} return bytes;
} public static int ToInt(byte[] bytes, int offset, int length, bool isLittleEndian = true)
{
if (length == )
{
return bytes[offset];
} var tempBytes = new byte[length]; Array.Copy(bytes, offset, tempBytes, , length); if (!isLittleEndian)
{
Array.Reverse(tempBytes);
} switch (length)
{
case :
// 特殊处理,转换为无符号int类型,返回时自动转换为Int32
return BitConverter.ToUInt16(tempBytes, );
case :
// 注意,这里将数据转换为有符号int类型
return BitConverter.ToInt32(tempBytes, );
default:
throw new ArgumentException("length 长度非标准值");
}
}
}

工具类型的方便之处在于其独立性,几乎无外部依赖,不需要考虑对其进行初始化,拿来就可以直接使用

[原]实例-简单设计&精简代码&复用代码的更多相关文章

  1. 3.NetDh框架之缓存操作类和二次开发模式简单设计(附源码和示例代码)

    前言 NetDh框架适用于C/S.B/S的服务端框架,可用于项目开发和学习.目前包含以下四个模块 1.数据库操作层封装Dapper,支持多种数据库类型.多库实例,简单强大: 此部分具体说明可参考博客: ...

  2. 通过游戏学python 3.6 第一季 第九章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁定账号--锁定次数--菜单功能'menufile

      通过游戏学python 3.6 第一季 第九章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁 ...

  3. 通过游戏学python 3.6 第一季 第八章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁定账号--锁定次数

    通过游戏学python 3.6 第一季 第八章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁定账 ...

  4. C# 求精简用一行代码完成的多项判断 重复赋值

    C# 求精简用一行代码完成的多项判断 重复赋值 哈哈,说实话,个人看着这么长的三元操作也麻烦,但是我也只想到了这样三元判断句中执行方法体能够写到一行,追求的终极目的是,用一行实现这个过程,而且简单,由 ...

  5. python面向对象入门(1):从代码复用开始

    本文从代码复用的角度一步一步演示如何从python普通代码进化到面向对象,并通过代码去解释一些面向对象的理论.所以,本文前面的内容都是非面向对象的语法实现方式,只有在最结尾才给出了面向对象的简单语法介 ...

  6. 函数和代码复用 --Python

    1.关于递归函数的描述,以下选项中正确的是 A.包含一个循环结构 B.函数比较复杂 C.函数内部包含对本函数的再次调用 D.函数名称作为返回值 答案:D 答案解析:递归函数是指函数内部包含对本函数的再 ...

  7. Python-函数和代码复用

    函数的定义与使用 >函数的理解与定义 函数是一段代码的表示 -函数是一段具有特定功能的.可重用的语句组 -函数是一种功能的抽象,一般函数表达特定功能 -两个作用:降低编程难度 和 代码复用 de ...

  8. 6个实例详解如何把if-else代码重构成高质量代码

    本文提纲: 为什么我们写的代码都是if-else?这样的代码有什么缺点?是否有优化的方法?如何重构?异常逻辑处理型重构方法状态处理型重构方法为什么我们写的代码都是if-else?程序员想必都经历过这样 ...

  9. Python学习笔记(五)函数和代码复用

    函数能提高应用的模块性,和代码的重复利用率.在很多高级语言中,都可以使用函数实现多种功能.在之前的学习中,相信你已经知道Python提供了许多内建函数,比如print().同样,你也可以自己创建函数, ...

随机推荐

  1. JAVA 中的集合框架

    java集合框架提供了一套性能优良.使用方便的接口和类,它们位于java.util包中 一.集合与数组 数组:(可以存储基本数据类型)是用来存现对象的一种容器,但是数组的长度固定,不适合在对象数量未知 ...

  2. IE 内使用ActiveX,写注册表被重定向到如下注册表

    IE 内使用ActiveX,写注册表被重定向到如下注册表,所以使用注册表做标记要注意下,目前还没找为什么会这样 HKEY_CURRENT_USER\Software\Microsoft\Interne ...

  3. 配置JSP模板

  4. 弹框内画echarts图dom元素无法获取的问题

    弹框内画echarts图dom元素无法获取的问题? 什么意思呢?就是当我们打开弹框之后,此时要画eachars图,可是echarts图的容器dom此时为null, 因此我们需要做的就是在dom元素获取 ...

  5. java调接口

    package util; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.InputSt ...

  6. 【Oracle】实现Oracle数据库对象的一键升级

    引言     公司内部的项目比较倾向于将业务逻辑放在oracle存储过程中实现,所以每次项目升级都涉及到很多的oracle表,存储过程等数据库对象的升级.然而采取的升级方式是比较"原始&qu ...

  7. 完成一个servlet 就要在web.xml里面配一个映射,这样就有一个路径供我们 使用????? servlet从页面接收值?

    最后,最容易忘记的是:在dao层中 调用xml里的删除sql语句 后面需要人为加上事务提交.一定要! sqlSession.commit();//jdbc是自动提交,但是mybatis中不是自动提交的 ...

  8. dedecms列表页文章有图调用缩略图 无图留空或自定义图片的方法!

    默认情况下,织梦的文章列表页会调用出当前栏目下的文章列表,并且调用出每个文章的缩略图:如果文章本身就有图,会调用出一张小图,如果没有,则会显示默认的织梦图片.这种处理方式有时候比较影响美观,其实可以修 ...

  9. HihoCoder1366 逆序单词(字典树)

    逆序单词 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 在英文中有很多逆序的单词,比如dog和god,evil和live等等. 现在给出一份包含N个单词的单词表,其中每 ...

  10. eShopOnWeb

    eShopOnWeb https://www.cnblogs.com/sheng-jie/p/9616675.html 构建现代Web应用 1.引言 eShopOnWeb是基于ASP.NET Core ...