博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
软件开发中的矛盾——一个简单的例子
阅读量:2340 次
发布时间:2019-05-10

本文共 1894 字,大约阅读时间需要 6 分钟。

声明】如需复制、传播,请附上本声明,谢谢。原文出处:http://morningspace.51.net/,moyingzz@etang.com

 

 

 

 

 

 

 

  在以前的文章中,我曾经提到过软件开发中充满了矛盾,一些原则本身就是彼此矛盾的,需要不断在这些矛盾中寻求折中、平衡。这里给出一个源自实际的简单例子,希望能给大家一点启示,只是不知道是否贴切(说明:程序用C++语言描述,T为某个数据类型)。

 

  在定义某个类的接口时,需要定义两个相关变量a和b的getter/setter函数。为了使接口尽量精简,我们采用第一种方法,用一对getter/setter来处理:

 
getAB(T* pa, T* pb);setAB(T a, T b);
 
 

  但是,如果有时只想对其中一个变量进行存取时,调用getAB/setAB就需要额外的工作,client方的代码就会很累赘。

 

  为了获取a的值,不得不定义一个额外的变量b:

 
T a, b;getAB(&a, &b);cout << a;	// only get a
 
 

  为了设置a的值而保留b的值,不得不额外调用getAB:

 
T a, b;getAB(&a, &b);a = 10;setAB(a, b);	// only set a
 
 

  为此,我们采用第二种方法,将之拆成两对函数:

 
T getA();T getB();void setA(T a);void setB(T b);
 
 

  这样,接口就一下扩展了一倍。但是,事情并未就此结束。有时,像getAB/setAB这样的方式并非只起到了简化接口的作用。在调用setAB的时候,我们可以从a和b相关的角度来考察a和b的合法性,比如:a,b代表某个值域的上下限,那么假定如果a > b时,设置就不合理,就应该拒绝。而这种合法性检查用第二种方法实现的时候就不是那么顺利了,粗看起来代码应该如下:

 
int setA(T a){  if (a > m_b)    return -1;	// error  else    m_a = a;}int setB(T b){  if (b < m_a)    return -1;	// error  else    m_b = b;}
 
 

  如果只是设置a、b中的一个值,倒不会有任何麻烦,这种方法完全胜任。但是,如果同时设置呢?暂且不考虑a、b的初值应该如何取,比如某次对a、b的设置使a、b分别等于5、10,而再次试图重设a、b为15、20时,问题就产生了:

 
setA(15);setB(20);
 
 

  结果变成了a = 5, b = 20(completely error)。

 

  如果要得到正确结果,则需要颠倒调用setA和setB的次序。但是,如果a、b要分别设成1、6呢?亦即,为了保证成功设置,client代码需要十分小心,不同情况,采用不同的调用约定。对于上述情况,用第二种方法很难做到正确的合法性检查,因为函数的signature决定了它无法得知相关的另一个值,从而不能做出正确判断。

 

  所以,事情的演变过程就是:

  • 为了使接口精简,我们选择方法一;
  • 为了不增加额外的客户代码,我们选择方法二;
  • 为了进行合法性检查,我们又不得不选择方法一。
这里总共出现了两种方法,三个原则(“为了……”)。

 

 

  而当我们最终决定选择方法二时,还是有可能背负着“增加额外的客户代码”这样的罪名。而如果你确实不想如此,或许你会将两种方法结合使用,即把getA/setA,getB/setB,getAB/setAB统统定义为接口。可是,你又可能会被人指责为“接口混乱”,而且对于setA/setB而言,合法性检查仍然是个问题。

 

  结论:在有多种方法可供选择时,存在不同的原则(选择依据),针对实际的情况,我们需要作出决定。这种决定往往不会做到满足所有原则,但一般它应该是最大限度的适合大多数情形。如果,实际的情况不能足以使你作出很肯定的判断,那么,恐怕只有习惯和直觉可以影响你的决定了。只是,或许以后你还会修改你的决定。

 

   ps:为了说明方便,所以这里选用了一个极为简单的例子。坦白讲,可能有夸大之嫌。实际情况下,对于这样的“getter/setter”问题,你多半不会像文中说得那样处于如此为难的境地,除非你是个完美主义者。你可以很快作出决定,因为即使无法满足某条原则,其代价也不会很高。否则,那些软件开发人员,每天就不用写几行代码了,而且会深陷于矛盾的痛苦之中。但是,在这样细微的地方都会存在矛盾,可以想见,用“矛盾重重”来形容软件开发过程,可能是不算夸张的。

 

转载地址:http://ujzvb.baihongyu.com/

你可能感兴趣的文章
C/C++中变量的存储位置
查看>>
C++中四种强制类型转换区别详解
查看>>
linux gdb的详细用法 运行与断点
查看>>
删除vector中重复元素
查看>>
和为s的连续正数序列
查看>>
什么是Redis?什么是nosql?NoSQL数据库的四大分类
查看>>
为什么说Redis是单线程的以及Redis为什么这么快!
查看>>
redis的过期健删除策略以及内存淘汰机制
查看>>
redis 双写一致性问题
查看>>
map 如何使用结构体作为自定义键值
查看>>
Mysql几种索引类型的区别及适用情况
查看>>
Redis缓存穿透、缓存雪崩、redis并发问题分析
查看>>
Redis持久化的两种方式
查看>>
判断一个数组,是否可以分成两个数组之和相等的数组
查看>>
背包问题
查看>>
结构体变量之间的比较和赋值原理
查看>>
C++ const修饰函数、函数参数、函数返回值
查看>>
将单链表的每k个节点之间逆序
查看>>
删除链表中重复的节点——重复节点不保留
查看>>
2018腾讯校招编程题——最重要的城市
查看>>