hrmai

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  40 随笔 :: 0 文章 :: 507 评论 :: 1 引用

公告

2011年1月6日 #

其实我用过的,那一天晚上,看着摇摇欲坠的衣架,还有上次换床时留下的两根木头,突发奇想,用菜刀把木头给锯了,然后把衣架给修好了,用了两个小时。(我的菜刀上有一道锯齿状的东西)

其实很多时候我们都是在用不对的工具做对的事情,最后是搞到很辛苦,很不爽。试问,你有没有用剪刀拧过螺丝?用起子敲过钉子?是不是很辛苦。

小时侯觉得这种事情做起来很有成就感,因为确实,用了跟别人不同的方法去做成别人想去做的事情。但是现在你还在用这些方法吗?

作为一个程序员,我们用的最多的工具,应该是编程语言和方法论了(先不算IDE)。而说起方法论,我想说一下设计模式。院子里也有很多人在写设计模式的文章,也有很多人在用设计模式来完成工作,也包括我。但是一般情况下我不用设计模式,我宁愿让类看起来自然易懂一点。为什么了?因为,很多时候我们用设计模式的时候我们是分不清楚各种各样的设计模式所代表的工具类型的。比如,单例,他主要的作用是要来维持实例的状态的全局性;facase,主要是封装系统内部的实现,像windows的HAL层,让每一个做驱动开发的,直接使用函数就可以了;适配器,主要是为了让两孔的插头可以插进三孔的插孔。

其实这些设计模式他们本身被提取出来的时候所肩负的职责你懂吗?如果不懂,会发生什么事情了?曾经有人说过,单例可以节省内存,首先不说任何问题,如果你真的要节省内存,那么,我建议将所有的函数和属性静态化,我想这是最节省内存的方法了。

说起起子,其实我们可以看到各种各样的起子,但是如果就按适合的类型,来分类,那么大概可以分成,十字和一字,还有就是起子头的大小。于是就有一些很有设计模式头脑的人用了一个模板模式来把起子重构了一把,最后结果就是,一个起子柄可以安装多个标准的起子头,如图:

clip_image001

于是有人发挥想象的空间:为什么不把一个锤子附加在起子的柄上了,这样就可以两用了,一头是起子,一头是锤子(有点像单例既省内存又维护状态)。这个可以有吗?这个真的不可以有。为什么了,因为如果加了一个锤子,你会发现你用起子的时候,手柄不好用,用锤子的时候,你会发现柄太小,震的手疼。

这里就引申到了我个人觉得软件里面最重要的一个东西,职责,responsibility。为什么说这个东西重要,是因为,无论是生活中的工具还是软件开发中的设计模式与类,如果承担的职责过多且职责之间没有想象之处,那么这个东西是有问题的。就好象带锤子的起子,它是起子还是锤子了?一个类之所以不应该承担太多的职责,就是因为会变成带起子的锤子。而一个函数不应该有太多的职责,是因为你把起子铸在类锤子里面,仍希望既能当锤子用又能当起子用。类应该是一个多头的起子,他可以承担多个相同的职责,方法应该是起子中的一个头,命名空间应该是一个工具箱,既有锤子又有起子,可能还有能当菜刀用的锯子(这是扯淡的,当我没说)。

最后,如果你不知道下面的代码有什么问题,那么还是不要用设计模式了,这个时候是手柄是菜刀的起子了。

public class Test

{

private Test()

{

}

private static Test instanse;

private static List<int> status = new List<int>();

public Test Instance

{

get

{

status.Clear();

return instanse;

}

}

}

posted @ 2011-01-06 21:26 Leon Mai 阅读(277) 评论(4) 编辑

前段时间做了一个统计系统,但是现在发现统计数据和业务系统耦合度太高,以至于系统在做业务扩展的时候需要顾忌的东西太多。

其实我接触过的很多系统都是这样,那为什么大多数系统都会这样子了?是因为系统刚开始的时候架构设计有问题了?还是我们没有能力去设计一个ETL统计系统了?其实都不是这样子的。真正的问题是系统在演变过程中没有按照既有的数据库设计规则而导致了现在的问题。下面我来模拟一下一个简单系统演变过程。

1、 系统刚开始的时候

由于系统刚开始,也不一定能够得到很多的业务量,投资也不是很大,于是我们按照不要过度设计的原则,只有以下这么几个表。

clip_image002

现在系统可以运行的很好,完全没有问题。

2、 业务第一次扩展

随着业务的进一步发展,系统进行了第一的扩展,主要是要求系统可以对一次订购可以进行分状态提交,也就是说订购是多个状态的,比如:下单、确认、支付。其实就是类似于淘宝的一个购买过程。于是我们进行了第一次系统扩展过程。

clip_image004

我们在Order表中加入了一个记录订单状态的Status状态,然后用户每一次提交改变订单状态的时候我们都将status置位为对应的状态。

至今,我们的系统还可以运行下去,一切还好。

3、 业务第二次扩展

业务进一步发展,我们的系统得到了进一步的发展,现在我们需要回馈新老客户,我们将系统中的业务打包起来形成一个套餐包,然后给予一定的优惠。类似于移动的套餐,比如动感地带的音乐套餐等。现在我们给Produce表加了一个字段,如下图:

clip_image006

PDetail中存储了这个产品中包含的子产品ID。这个时候系统还可以继续运行。而且除了套餐包麻烦一点之外,其他所有的都还好。

4、 统计系统

现在管理层需要报表,而且,由于系统数据的膨胀,我们不可能只用简单的操纵原始数据来获得系统需要的统计数据。我们开始设置一个ETL的统计系统。

我们先从最简单的要求开始,提供指定日期的营业额,周报,日报等。

这个时候我们会发现,按照3的系统来说,如果要用ETL来提取这个数据,我们是没办法做得到的(假设,订购状态的改变时长可以无限),除非我们每天都对Order表进行一个全表的扫描,然后和抽取出来的数据进行一个对比。可是,我们现在已经是一个很大的数据量,我们的系统可能已经不可以在每天晚上的空闲时间完成这个数据抽取工作。

可是,我们在整个系统改造过程中都没有发现问题呀。难道这可以怪我们吗?

(有人可能会说我们其实可以在客户购买的过程中将统计数据插入统计中。这里有以下的问题,第一,如果系统有多个购买渠道,或者增加多个订购渠道,那么你将会遇到我在开篇的时候说的情况;第二,如果有多个统计要求,那么插入统计表的过程就会很有问题。)

5、 解决之道

其实这里的解决之道比较复杂。为什么了,因为系统已经积聚了很多历史数据,所以,如果从长久来看,我们应该调整系统的结构,然后解决遗留数据。我们将会看到以下的数据结构:

clip_image008

我们这样做的原因是因为我们满足第三范式,而且,我们会使统计数据的ETL来的非常简单。比如,我们只要每天在一个固定的时间将当天的数据抽取到统计表中,那么我们就可以解决ETL中的全表扫描问题了。

那么历史数据怎么办了?其实历史数据历来都是一个问题,但是我觉得,其实我们可以将历史数据简化成一个两部提交的购买过程,下单,支付;下单,否决支付。

6、 3中的设计存在的问题

其实这个设计没什么问题,真的没有。但是在一种情况下会有一个很大的问题。假设我们的系统就是一个移动的业务系统,那么这个设计就会有很大的问题,为什么了?因为产品代表的是业务,是一种长期订购的业务。这种设计在系统扣费的时候不会有任何问题,但是这种设计在系统控制用户状态的时候会有问题。比如:我订购的是音乐套餐,19元,130条短信,如果短信超出了就要扣钱。如果我每一次发短信,系统都需要关联Produce表一次,那么我想这个系统的效率就会有很大的问题。我个人觉得应该将系统调节成以下的结构:

clip_image010

(CustomerProduce表只记录没有子节点的产品)

这样子我们每次控制客户的业务状态的时候我们就可以得到很好的效率。

其实我们从这次模拟中会看到,很多系统我们参与的时候会发现很烂,然后就骂开始时的设计人员,程序员,骂之前的人,殊不知,其实我们很多时候就是在做着被自己骂的人。一个系统之所以由一个好的系统变成一个不好的系统,是由于在每一次变更中慢慢的变的不那么的规范,变的不愿意去更改之前的业务逻辑,于是我们的系统慢慢的变得由一个具有很高的可扩展性的系统,变成了一个可以满足业务且仅满足业务的系统。

今天你是不是也在做着同样的事情了?

posted @ 2011-01-06 21:12 Leon Mai 阅读(1680) 评论(20) 编辑