首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

敏捷思维 架构设计中的方法学(10) -- 分层 -4

敏捷思维 架构设计中的方法学(10) -- 分层 -4

层内的细分分层的思路还可以适用于层的内部。层内的细分并没有固定的方式,其驱动因素往往是出于封装性和重用的考虑。例如,在EJB体系中的业务层中,实体Bean负责实现业务对象,因此一个应用往往拥有大量的实体Bean。而用户端并不需要了解每一个的实体Bean,对它们来说,只要能够完全一些业务逻辑就可以了,但完成这些业务逻辑则需要和多个实体Bean打交道。因此EJB提供了会话Bean,来负责把实体Bean封装起来,用户只知道会话Bean,不知道实体Bean的存在。这样既保证了实体Bean的重用性,又很好的实现了封装。
面向接口编程在前面的章节中,我们提到一个接口设计的例子。为什么我们提倡接口的设计呢?Martin Fowler在他的分析模式一书中指出,分析问题应该站在概念的层次上,而不是站在实现的层次上。什么叫做概念的层次呢?简单的说就是分析对象该做什么,而不是分析对象怎么做。前者属于分析的阶段,后者属于设计甚至是实现的阶段。在需求工程中有一种称为CRC卡片的玩艺儿,是用来分析类的职责和关系的,其实那种方法就是从概念层次上进行面向对象设计。因此,如果要从概念层次上进行分析,这就要求你从领域专家的角度来看待程序是如何表示现实世界中的概念的。下面的这句话有些拗口,从实现的角度上来说,概念层次对应于合同,合同的实现形式包括接口和基类。简单的说吧,在概念层次上进行分析就是设计出接口(或是基类),而不用关心具体的接口实现(实现推迟到子类再实现)。结合上面的论述,我们也可以这样推断,接口应该是要符合现实世界的观念的。
在Martin Fowler的另一篇著作中提到了这样一个例子,非常好的解释了接口编程的思路:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
interface Person {
  public String name();
  public void name(String newName);
  public Money salary ();
  public void salary (Money newSalary);
  public Money payAmount ();
  public void makeManager ();
}
interface Engineer extends Person{
  public void numberOfPatents (int value);
  public int numberOfPatents ();
}
interface Salesman extends Person{
  public void numberOfSales (int numberOfSales);
  public int numberOfSales ();
}
interface Manager extends Person{
  public void budget (Money value);
  public Money budget ();
}




可以看到,为了表示现实世界中人(这里其实指的是员工的概念)、工程师、销售员、经理的概念,代码根据人的自然观点设计了继承层次结构,并很好的实现了重用。而且,我们可以认定该接口是相对稳定的。我们再来看看实现部分:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class PersonImpFlag implements Person, Salesman, Engineer,Manager{
// Implementing Salesman
public static Salesman newSalesman (String name){
  PersonImpFlag result;
  result = new PersonImpFlag (name);
  result.makeSalesman();
  return result;
};
public void makeSalesman () {
  _jobTitle = 1;
};
public boolean isSalesman () {
  return _jobTitle == 1;
};
public void numberOfSales (int value){
  requireIsSalesman () ;
  _numberOfSales = value;
};
public int numberOfSales () {
  requireIsSalesman ();
  return _numberOfSales;
};
private void requireIsSalesman () {
  if (! isSalesman()) throw new PreconditionViolation ("Not a Salesman") ;
};
  private int _numberOfSales;
  private int _jobTitle;
}




这是其中一种被称为内部标示(Internal Flag)的实现方法。这里我们只是举出一个例子,实际上我们还有非常多的解决方法,但我们并不关心。因为只要接口足够稳定,内部实现发生再大的变化都是允许的。如果对实现的方式感兴趣,可以参考Matrin Fowler的角色建模的文章或是我在阅读这篇文章的一篇笔记。
通过上面的例子,我们可以了解到,接口和实现分离的最大好处就是能够在客户端未知的情况下修改实现代码。这个特性对于分层技术是非常适用的。一种是用在层和层之间的调用。层和层之间是最忌讳耦合度过高或是改变过于频繁的。设计优秀的接口能够解决这个问题。另一种是用在那些不稳定的部分上。如果某些需求的变化性很大,那么定义接口也是一种解决之道。举个不恰当的例子,设计良好的接口就像是我们日常使用的万用插座一样,不论插头如何变化,都可以使用。
最后强调一点,良好的接口定义一定是来自于需求的,它绝对不是程序员绞尽脑汁想出来的。
数据映射层在各个层的设计中,可能比较令人困惑的就是数据映射层了。由于篇幅的关系,我们不可能在这个问题上讨论太多,只能是抛砖引玉。如果有机会,我们还可以来谈谈这方面的话题。
面向对象技术已经成为软件开发的一种趋势,越来越多的人开始了解、学习和使用面向对象技术。而大多数的面向对象技术都只是解决了内存中的面向对象的问题。但是鲜有提到持久性的面向对象问题。
面向对象设计的机制与关系模型有很大的不同,这造成了面向对象设计与关系数据库设计之间的不匹配。面向对象设计的基本理论包括耦合、聚合、封装、继承、多态,而关系数据模型的理论则完全不同,它的基本原理是数据库的三大范式。最明显的一个例子是,Order对象包括一组的OrderItem对象,因此我们需要在Order类中设计一个容器(各个编程语言都提供了一组的容器对象及相关操作以供使用)来存储OrderItem,也就是说Order类中的指针指向OrderItem。假设Order类和OrderItem分别对应于数据库的两张表(最简单的映射情况),那么,我们要实现二者之间的关系,是通过在OrderItem表(假设名称一样)增加指向Order表的外键。这是两种完全不同的设置。数据映射层的作用就是向用户端隐藏关系数据库的存在。
自己开发一个对象/关系映射工具是非常诱人的。但是应该考虑到,开发这样一个工具并不是一件容易的事,需要付出很大的成本。尤其是手工处理数据一致性和事务处理的问题上。它比你想象的要难的多。因此,获取一个对象/关系映射工具的最好途径是购买,而不是开发。
总结分层对现代的软件开发而言是非常重要的概念。也是我们必须学习的知识。分层的总体思路并没有什么特别的地方,但是要和自己的开发环境、应用环境结合起来,你还需要付出很多的努力才行。
在完成了分层之后,软件架构其实已经清晰化了。下面的讨论将围绕着如何改进架构,如何使架构稳定方面的问题进行。
返回列表