Pulpcode

捕获,搅碎,拼接,吞咽

0%

2014年终技术总结之常见的编程模型

对于一个要解决的问题,总会提供一种解决思路。这种思路如果是成熟的,不一定处处都是部分最优,但是能够保证全局最优。

所以我在这里总结这种编程中常用的成熟的设计思路,避免自己再走弯路,提高自己在开发和设计时的效率。

只做加法,不做减法

数据库与orm同步

这一年主要使用c#开发系统,我们使用的orm是XAF下的XPO,XPO是一种强大的ORM工具,能够通过数据库表生成业务模型,也可以通过写好的业务模型直接去生成数据库表。

而且你在使用XPO的时候,使用什么类型的数据库对于orm而言是完全透明的,根本不用关心,只要配置一下连接字符串就行了。

但问题是如果我们通过业务类来直接生成表结构,而在开发的过程中,我们又修改了业务类,那么orm会如何处理?保证业务类和表结构同步?

比如我们现在的业务类是这样写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Student : XPObject
{
private string _name;
public string Name
{
get { return _name; }
set { SetPropertyValue("Name", ref _name, value); }
}

private int _age;
public int Age
{
get { return _age; }
set { SetPropertyValue("Age", ref _age, value); }
}
}

那么数据库的表结构是和这个模型一样没错啦。

但是如果我们添加了一个新字段呢?

1
2
3
4
5
6
private string _class;
public string Class
{
get { return _class; }
set { SetPropertyValue("Class", ref _age, value); }
}

运行系统后,表结构也会添加一个新的字段。

那么如果我们将这个class字段删除呢?运行系统后,数据库的表结构中并不会删除这个字段。

那么如果我们在业务模型中将这个Age重新命名为AgeNew呢?运行系统后,数据库的表结构并不会修改这个字段名,而是新建了一个AgeNew。

那么如果我们在这个业务模型中将class的类型变为int型的呢?运行系统后,数据库并没有发生任何变化,而且程序也会进行下去,只有在写入的那一瞬间,会报错。

之所以这么设计,就是因为,只做加法,不做减法,如果你要”牵强的“维护一致性,会让数据库的数据发生丢失,而这带来的后果很严重,因为用户程序员可能在无意识的情况下丢失了数据。

还有你会发现业务模型和数据库表之间的关系是很”清淡的“,也就在运行的那一瞬间,检查,生成,修改一下而已。写过类似维护两个模型同步关系程序的童鞋,如果设计出那种”必须一致“的程序,会对复杂性深有体会。

版本管理器的退回到某个版本

之前在使用版本管理器的时候,我一直好奇,版本管理器是如何退回到某一个版本的。

比如说有这样一个版本树:

1 -> 2 -> 3 -> 4 -> 5

现在的最新版本是5,那么如果我想使代码退回到版本3,那么如果我在版本3上进行开发,那么新的版本又算什么呢?版本4和版本5又到哪里去了呢?

其实版本管理器的真实情况是,没有真正的回到过去

如果说你想回到版本3,真实的情况是:

版本控制器将版本3的代码拿出来放到你的当前目录下,将来作为版本6

1 -> 2 -> 3 -> 4 -> 5 -> 6

其实理解这个问题的关键是,我们的当前的工作代码环境,和版本控制器版本库是三个概念,不要搞混。

我们在当前工作目录,编写,修改代码,然后通过版本控制器,存储,查询,取出,管理版本库中存储的代码。这才是理解版本控制器的关键。

而且这个版本控制器的例子也是为了说明,只做加法,不做减法这种思路,回退版本并不是真正的回退。

线性关系

维护模型之间关系,我一直觉得是个很麻烦的事情,比如你要保证它们之间的一致性,比如A变化,B是不是也要跟着变化,如何变化?

再比如它们之间的关系图应该是一条线,是一棵树,还是一张图。

这里我先提出这一节的思路:

  1. 我们做线性关系,不做图。
  2. 我们做生成操作,不做同步操作。

比如在第一节只做加法,不做减法中我们提到的,xaf的orm就是会设计成A变化,影响到B,但是不会说B变化影响到A(A指ORM模型,B指表结构),因为维护这样的环形关系代价太高了。

还有我之前工作中我写过这样一个程序,将数据库中的整型取出,然后通过位掩码解析成多个enum值,再将这些值转换为英文,最后转化为中文显示。作为一个编辑器的数据。

之后我又对这个程序写了一个逆向操作,将用户选中的中文,一步步的转化回去。项目经理对这个设计很不满意,他说如果是一个图像,你还去解析图像在转换回去吗?正确的操作是中文直接映射数字,加完之后直接写入数据库就行了。

再回到同步问题上,之前写过一个程序,OA中创建的项目会产生一个项目编号,是由项目类型缩写,科室缩写,年份,流水号四部分组成的。

关键是流水号是跟年份和项目类型科室相关的,所以在项目填写的那个界面,我们做了许多实时判断,改变项目类型,重新生成,改变年份,重新生成。最后为了这些同步,又带来许多新问题,比如如果这个项目删除了怎么办?如何回收流水号?有的项目并没有录入怎么办?如何跳开这些流水号?自动生成,同步流水号带来太多麻烦的问题。

最后的解决办法很简单,就在初始化的时候生成一个流水号,用户可以直接用,也可以自己改,再就不管了,没有任何同步关系需要维护,这样就简约多了。

不做多余操作

最近总会听到这个词,就是纯粹的做好一件事。但是我们在做事的时候总会想“打草搂兔子,顺便做某事。”

程序中好的设计,往往都会只做好一件事。类似于真正锻炼身体的人就会去健身房,而不是去扛大米跳水,既挣钱又锻炼身体。

我们在大学时写c++程序的时候,总会带这么一句using namespace std

这就是一种不好的习惯,因为它暴露了命名空间,那么那些使用你的库的用户程序员根本没有选择的余地,他们逼不得已,也获得了这些命名空间。

而正确的写法是不要暴露命名空间,直接从命名空间引用那些变量,函数,类:std::cout<<"Hello,World!":

记得前段时间,我在vs上开发程序,vs会把xml文件转化成一个点击可以展开关闭节点的视图,我在关闭父节点再打开的时候,它的子节点并没有发生任何变化。

但我的主观意识总是认为,子节点会跟随父节点变化,这就是我思维混乱的地方。想象出这些多余的操作。

这其实也算程序设计里面的”正交性“,就是指功能和功能之间尽可能的不互相干扰,同时也强调我们不要做一些自作聪明的事情。

好的设计就是这样,只做好一件事,不要顺便做了其他事。