引言

本文以实际项目为例谈一谈我个人对于软件开发的理解,偏细节
 
软件项目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. C# 反射通过GetCustomAttributes方法,获得自定义特性

    http://blog.csdn.net/litao2/article/details/17633107 使用反射访问: 自定义属性的信息和对其进行操作的方法. 一.实例1 1.代码: 如:Syste ...

  2. python 2 到 3 的新手坑

    print 和 input print 我们在课程最开始的时候就讲过 print,在版本2的使用方法是: print 'this is version 2' 也可以是 print('this is v ...

  3. PowerDesigner15生成数据库 同时自动生成字段说明(备注)信息

    1.打开Database->Generate Database 2.切换到Format标签页,选中Generate name in empty comment即可生成每个字段的说明(备注)信息 ...

  4. The tag handler class for "home.jsp" (org.apache.taglibs.standard.tag.rt.core.ForEachTag) was not found on the Java Build Path

    web.xml中 listener,filter,servlet需按顺序. <listener> <listener-class>listener.VisitCountList ...

  5. IDEA 新建.vue格式的文件

    1.Ctrl+Alt+S 2. <template> <div> {{msg}} </div> </template> <style> bo ...

  6. Express+Mongoose(MongoDB)+Vue2全栈微信商城项目全记录(二)

    用mogoose搭建restful测试接口 接着上一篇(Express+Mongoose(MongoDB)+Vue2全栈微信商城项目全记录(一))记录,今天单独搭建一个restful测试接口,和项目前 ...

  7. linux下的制作静态库并使用它

    静态库所要用的源文件 文件 fred.c   #include <stdio.h>   void fred(int argc) {     printf("void fred(i ...

  8. burpsuite使用以及repeater模块实现重放攻击

    第一.burp suit是什么? Burp Suite 包含了一系列burp 工具,这些工具之间有大量接口可以互相通信,之所以这样设计的目的是为了促进和提高 整个攻击的效率.平台中所有工具共享同一ro ...

  9. 如何用nodejs启一个前端服务

    1.新建文件夹,如 notice 2.新建页面和js文件,如 index.html server.js 3.index.html页面内容随你写,如: <!DOCTYPE html> < ...

  10. 预备架构的工具ADMEMS矩阵

    矩阵,是很多著名方法的核心.例如,制定公司层战略的方法之一是"波士顿矩阵","波士顿矩阵"又称"市场增长率-相对市场份额矩阵". " ...