Aimin Han

SharePoint Server、Office、Silverlight、Flash、GIS、AVEVA NET & solutions 培训 咨询 设计
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

谈谈代码设计

Posted on 2009-09-05 20:32  aimin  阅读(2525)  评论(3编辑  收藏  举报

为什么要重视代码设计?

在绝大多数项目中,尤其是在大型、资源缺少(这是软件项目的典型现象)的项目中,正规的架构可能只是解决系统级的事项,而特意把大部分的设计工作留待代码构建阶段去做。这可能会引发两个问题:

1)       代码构建阶段的设计分散保存在开发人员的头脑中,得不到有效的验证

2)       由于开发人员各有所长,也各有所短,往往在进行了部分或完成代码构建后,问题才能被明确。最终,不合理的设计导致大量的重构工作,延误项目工期。



设计工作千头万绪,然而软件设计的首要使命是“管理复杂度”;重点和难点是“管理变化”。虽然“管理变化”是“管理复杂度”的内容之一,但由于“管理变化”具有突出的重要性,因此单独进行介绍。在代码构建整个过程中,所有设计工作必须充分考虑这两个方面,从而实现高质量代码。

1.  管理复杂度

复杂度来源:

1)       用复杂的方法解决简单的问题

这个现象通常出现在过度设计上:首先是简单的问题复杂化,然后对复杂化的问题使用复杂的方法。

如在10个有序数字中检索某个数字。按照目前的CPU运算速度,直接采用顺序检索简单有效。如果盲目考虑CPU运算的因素,采用二分法检索,反而导致逻辑冗余。但是,如果对位数过万的数组进行检索,则应采用二分法或其它更有效的算法。

2)       用简单但错误的方法解决复杂的问题

由于设计是一个由浅入深,不断尝试,不断失败,最终得到正确结果的过程,因此,在代码构建之初,当设计者未能正确考虑复杂问题的各个方面,将复杂问题简单化后,将会导致“用简单但错误的方法解决复杂的问题”。

对此,我说一下自己的一个切身体会,在SharePoint开发中,我们一个常见的问题是“内容数据库非关系型数据库”,虽然Moss提供了Lookup类型的字段来建立这种关系,但是存在两个问题,首先是不能跨站点,更不能查询多个列表的内容,也不能综合显示多个字段的内容。这些缺陷对于很多应用来说,似乎是致命的。为此,我曾经设计出同时支持跨网站、跨列表、多字段的自定义Field。但是设计中欠缺考虑,以致完成该Field的开发后,一个显著的缺陷让我头疼不已:将关联关系存储与字段值中,虽然在显示、编辑和存储上简化了编码工作,但是却未能解决关联关系本质上的问题,从而导致了一系列更为复杂的问题的出现,如反向关联查询,关联同步,以致于花费了大量的精力来维护这种关系。

在该字段新版本的设计中,我将关联关系单独存储在一个列表中,同时扩展基内容类型的New & Edit Form。嵌入维护不同列表间项与项关系的可视化展示与编辑部件(基于Silverlight实现)。顺便提一下,使用了多年的Flex,发现Silverlight除了在基础组件库上略逊色外,性能方面还是很不错的,尤其是继承自NET的垃圾回收机制。

但是无论是Flex和Silverlight,都要注意它在图形绘制方面的局限性,当需要绘制的组件和图表过多时,尽量只绘制ViewPort中的部分,否则性能问题将导致整个程序的崩溃。

总结一下,如果使用简单但错误的方法解决复杂问题,那么代价会在后面成几何级数回馈给你。

套用一句话:代码无小事!

3)       用不恰当的复杂方法解决复杂问题

在旧有经验的指导下,某些复杂问题的表象会误导设计者,将之当作另外一个曾经发生过的复杂问题,而问题本身的复杂程度也令人望而却步,不愿做进一步的分析,直接套用已有的解决方法虽然省事,却有可能“用不恰当的复杂方法解决复杂问题”。

         管理复杂度的方法:

1)       任何人在同一时间只处理必不可少的复杂度

a)         分类汇总

从问题的领域着手,而不是从底层实现细节入手去编写程序,在最抽象的层次上工作,也能减少人的脑力负担。

问题的领域在不同抽象程度上,可以划分为不同的级别:业务,子系统,功能模块,类、子程序。通常,从较高的领域分解下来,会有效的降低问题的复杂程度。如在子系统级别确定好每个功能模块接口,那么在进入功能模块领域后,你只需要关心少量的接口,而把更多的精力放在该功能的数据、逻辑和UI设计上。

C#等面向对象语言可以轻易实现这种抽象,通过定义基类或抽象类(C#中的abstract 关键字)

b)         一致的抽象

定义基类或抽象类时,必须保持同级别抽象的一致性,摒除不必要的细节。

Asp.Net编程中的PageControl就属于同一级别的抽象。 Page类的实现中,它无需知道具体是什么类型的Control,是DataGrid还是TextBox,它根本不关心,它只关心Control类中的Render()可以提供什么样的Html。而这个Control是红的还是绿的,是方的还是圆的,根本不需要关心。如果编写Page类时调用了Control的一个子类,那抽象就不一致了,Page将自动降级,Page对于Control基类毫无意义。当然,这样的错误谁也不会去犯,仅仅用来说明这个道理而已。

在SharePoint中,通常我们也会实现一些基础类以提高团队的开发效率,如BaseAjaxWebPart、BaseAjaxField等等。这些Base类中一定不要使用某些实现特定细节的类或组件。

2)       不要让偶然性的复杂度无谓地快速增长

偶然性的复杂度,即潜在的变化,包括:已知的未知、未知的未知。

对于未知的未知这种情况,我们可以不过多考虑,只需要根据经验在任务工期上预留一定资源:时间、人力等。

对于已知的不确定的变化,在下面《管理变化》中简要介绍。

 

2.  管理变化

没有变化的程序是不可能存在的。但不能让变化像病毒一样蔓延到整个程序。

管理变化的手段比较多,如24种设计模式,大部分都是解决这个问题的。但万变不离其宗,主要还是以下两个方面:

1)       封装

通过有效的继承和一致的抽象,可以确保发生变化时,最小的代码改动量。

2)       模块化

通过对系统模块化及划分界面,确定接口,可以有效地将变化控制在模块内部。

使用封装和模块化来管理变化时,必须注意隐藏信息:该使用private之处,绝不要使用protected。该使用protected之处,绝不要使用publicInternal也不要多用,它的作用域是程序集内部,在编写代码时,往往跟public一样混淆视听,降低了代码的可读性。

太多的public会让使用这些class的二次开发的人员无所适从。充斥着非必要public成员的class,不符合管理复杂度的原则。有可能是设计原因,也有可能是未按照设计严格编码。