0
点赞
收藏
分享

微信扫一扫

《敏捷软件开发》— 敏捷设计

高子歌 2022-02-08 阅读 78

《敏捷软件开发》— 敏捷设计

一、设计的臭味

1、僵化性

僵化性是指难以对软件进行改动,即使是简单的改动。如果单一的改动会导致有依赖关系的模块中的连锁改动,那么设计就是僵化的。必须要改动的模块越多,设计就越僵化。

很多开发人员在工作中都遇到过这样的情况。看着很简单的一个问题,只改了一行代码,却影响了整个程序。这就是所谓牵一发而动全身。这种情况的出现往往意味着程序的僵化。

2、脆弱性

脆弱性是指,在进行一个改动时,程序的许多地方就可能出现问题。常常是,出现新问题的地方与改动的地方没有概念上的关联。要修正这些问题就又会引出更多的问题,从而使开发团队就像一只不停追逐自己尾巴的狗一样(忙得团团转)。

3、牢固性

牢固性是指,设计中包含了对其他系统有用的部分,但是要把这些部分从系统中分离出来所需要的努力和风险是巨大的

4、粘滞性

粘滞性有两种表现形式:软件的粘滞性和环境的粘滞性。

当面临一个改动时,我们会发现有许多改动方法。其中,一些方法会保持设计;而另外一些会破坏设计。当那些可以保持系统设计的方法比那些生硬手法更难应用时,就表明设计具有高的粘滞性。做错误的事情是容易的,但是做正确的事情却很难。

当开发环境迟钝、低效时,就会产生环境的粘滞性。例如,如果编译所花费的时间很长,那么开发人员就会被引诱去做不会导致大规模重编译的改动,即使那些改动不再保持设计。如果源代码控制系统需要几个小时去 check in 仅仅几个文件,那么开发人员就会被引诱去做那些需要尽可能少拆入的改动,而不管改动是否会保持设计。

无论项目具有哪种粘滞性,都很难保持项目中的软件设计。我们希望创建易于保持设计的系统和项目环境。

5、不必要的复杂性

如果设计中包含有当前没有用的组成部分,它就含有不必要的复杂性。当开发人员预测需求的变化,并在软件中放置了处理那些潜在变化的代码时,常常会出现这种情况。这样做的初衷是为将来的变化早做准备,以保持代码的灵活性并且避免以后再进行痛苦的改动。然而多数情况下,过多的提前准备会导致设计中含有绝不会用到的结构,从而变得混乱。

6、不必要的重复

代码的复制和粘贴。

7、晦涩性

晦涩性是指代码难以理解。代码可以用清晰、富有表现力的方式编写,或者可以用晦涩、费解的方式编写。为了防止晦涩或含义模糊的代码出现,开发人员必须要站在代码阅读者的位置,共同努力对他们的代码进行重构。

二、copy 程序

这是书中的一个例子,我们抛去故事的脉络,只呈现需求的变化和代码的维护过程。实现语言为 C++。

1、初始需求

从键盘读取输入字符,并输出到打印机上

void Copy()
{
	int c;
	while (c = readKeyboard() != EOF) {
		writePrinter(c);
	}
}

程序很简洁直观。

2、增加输入设备类型

输入设备不仅仅可能是键盘,也有可能是语音设备。

需要注意,由于第一版程序已经发布,因此 copy 接口没法修改。一个直观的改造方式就是通过全局变量控制是否为键盘输入:

bool isKeyboard = true;
// reset isKeyboard before and after using
void Copy()
{
	int c;
	while ((c = isKeyboard ? readKeyboard() : readAudio()) != EOF) {
		writePrinter(c);
	}
}

程序目前还可以,使用了一个直观的全局变量。只要用户使用之前看源码和注释就可以。

3、增加输出设备类型

输出设备不仅仅可能是键盘,也有可能是麦克风。

bool isKeyboard = true;
bool isPrinter = true;
// reset isKeyboard before and after using
void Copy()
{
	int c;
	while ((c = isKeyboard ? readKeyboard() : readAudio()) != EOF) {
		isPrinter ? writePrinter(c) : writeMicrophone(c);
	}
}

不难看出,这样改下去,程序中会充满各种条件判断,导致变成一个大泥团。

4、copy程序的敏捷设计

使用敏捷开发方法时,在初始阶段的编码和上述一样。然而,在响应第一个变化时,开发人员会修改设计并使修改后的设计对于那一类需求的变化具有弹性:

class Reader
{
	public:
		virtual int read() = 0;
};

class KeyboardReader : public Reader
{
	public:
		virtual int read() override {
			return readKeyboard();
		}
};

class AudioReader : public Reader
{
	public:
		virtual int read() override {
			return readAudio();
		}
};

KeyboardReader defaultReader;

void Copy(Reader& reader = defaultReader)
{
	int c;
	while ((c = reader.read()) != EOF) {
		writePrinter(c);
	}
}

在要实现新需求时,团队抓住这次机会去改进设计,以便设计对于将来的同类变化具有弹性,而不是设法去给设计打补丁。从现在开始,无论要求增加多少种设备,团队都可以方便地进行扩展。

需要注意,正如我们前面所说,我们并不需要在最初就预测可能引发程序变化的变量,然后有针对性地进行设计;而是在需求真正发生变化时,才改造我们的设计,使其变得富有弹性。

举报

相关推荐

0 条评论