我们先来看一小段程序:
const char *GetString(int key) { if (key == 1) return "first"; else if (key == 2) return "second"; else if (key == 3) return "third"; else if (key == 4) return "forth"; //... return "none"; ]
这是我们经常会看到的一段程序,设计模式中著名的工厂方法。它封装了将“材料”生产成“产品”的过程,使程序更加容易读懂,并方便后期修改(易读性与可维护性)。当然,在“产品”很少的时候,这样的程序又简明又易写,很是适合。但当我们的工厂逐渐变大,产品的种类越来越多的时候,你就不得不写上一堆又一堆的 else if 了,这可是非常麻烦和烦人的一件事。后期修改的时候,读程序的人也会不禁咒骂之前的作者——这样的意大利面条可不是谁都愿意吃的。
没错,当面对这种情况时,你马上会想到用 switch 来代替。你很聪明,switch 从形式上,确实要比 else if 明了得多了,但是它也有一个缺点:如果 key 变为了非整型或 char 型,又该怎么办?
这个时候,我们会想到,如果有一种方法,能将数据与程序分离开,每次出现新产品,只需要将产品的样式填到菜单里就可以,那多好。啊,数据库,它可以很方便地管理这些字段。这是个好的建议,当数据项和数据量多到一定程度时,使用数据库几乎成为必然手段。然而,我们面对的却是一种尴尬的情况:问题的规模不适合用 else if 和 switch 处理,如果使用数据库,且不说运行效率,仅从操作的麻烦程序上来说,还不如直接写 else if 了。。。所以在这里,我们需要的不是强大却麻烦的大型数据库,我们需要的,仅仅是编程上的一点小技巧。说到这里,也许您已经明白了,没错,就是在程序中实现一个数据表与查询方法。查询方法只按固定的方式去数据表中查询对应项的数据,然后返回。这样,既不必连接消耗资源的数据库,提高程序运行效率,又可以明了简便地完成我们想要的模式。查询方法很简单,只要给一个数据结构,遍历就是了,那么表呢?用什么来实现?对,在C中,数组是一个必然的选择。表是二维的,那么用二维数据吗?显然不行,二维数据只能维护一堆数据类型相同的数据,这显然不符合我们的需要。我们是要将int型数据对应为string型的。于是我们想到了一个利器:结构体数组。
那么让我们来改写一下之前的代码:
#define END 9999 typedef struct { int key; char *str; }ITEM; const ITEM table[] = { {1, "first"}, {2, "second"}, {3, "third"}, {4, "forth"}, //... {END, NULL} }; const char *GetString(int key) { for (int i = 0; table[i].key != END; i++) { if (table[i].key == key) return table[i].str; } }
table维护了数据的对应,而GetString只是遍历table去查找相应项。每当产品增加时,我们只需要在table中添加一个对应项即可。这样就解决了我们所面对的用 else if 和 switch 不足,用数据库又有余的数据对应问题。
同样的方法也适用于Java。Java中使用枚举来替代结构体数组。