C++学习笔记----5、重用之设计(三)---- 设计可用的接口(2)

3、设计易用的接口

        接口应用易用。这并不意味着接口一定要琐碎,但应该在功能允许的情况下尽可能地简单直观。满足KISS原则:保持简单,傻瓜(Keep it simple,stupid.)。你不能要求你的库的客户要学习好多页的源代码或者文档,只是为了使用一个简单的数据结构,或者为了得到他们所需的功能而在他们的代码中绕来绕去。这一部分我们就讨论一下设计易用的接口的四个策略。

3.1、遵循做事情熟悉的方式

        开发易用的接口最好的策略就是遵循标准而又熟悉的做事情的方式。当人们碰到一个与他们过去曾经使用过的相类似的接口时,他们就会更好地理解,更快地适应,也不会不正确地使用。

        例如,假设你要设计一辆小汽车的导向系统。有几种可能:用操纵杆,两个按钮,一个左移,一个右移,一个水平转向架,或者是一个不错的老式方向盘。你认为哪个接口更易用?哪种接口会卖掉更多的小汽车?消费者对方向盘会更熟悉,对这两个问题的答案显然都是方向盘。即使你开发了新的能够提供更高性能与安全性的技术,也要有一段儿困难的销售时期,来让人们学习怎么使用。当你在跟随标准操模型与开辟一个新方向之间进行选择时,保持人们习惯的方式的接口通常是一个更好的选择。

        创新确实很重要,但是应该着重在内部实现的创新,而不是接口。例如,消费者会对于纯电动汽车的创新感到兴奋。这些汽车卖得好部分原因就是使用它们的接口与标准油车一样。

        对于C++来讲,这种策略就意味着你应该开发接口,满足C++程序员习惯了的接口。例如,C++程序员期待一个类的构造函数和析构函数来各自初始化与消除一个对象。如果你需要“重新初始化”一个既存的对象,标准方式就是给它赋一个新的构造对象。在设计类时,要遵循这些原则。如果你要求程序员去调用initialize()与cleanup()成员函数来初始化与消除对象,而不是把这些功能放到构造函数与析构函数 中,你就会把那些想要使用你的类的所有人搞晕。因为你的类与其他C++类不一样,程序员要花费更多的时间去学习怎么使用,也很有可能因为忘记调用initialize()或者cleanup()而导致使用错误。

        总要从使用者的角度想一想你的接口。这么设计有道理吗?这是你想要的吗?

        C++提供了一个语言特性叫做操作符重载,可以帮助你为你的对象开发易用的接口。操作符重载允许你写出像内置的int或double这样的类型的操作符一样的标准操作符。例如,你可以写出一个Fraction类,允许加、减、打印等,如下:

Fraction f1{ 3, 4 };
Fraction f2{ 1, 2 };
Fraction sum{ f1 + f2 };
Fraction diff{ f1 – f2 };
println("{} {}", f1, f2);

        与使用成员函数调用同样的行为来对比一下:

Fraction f1 { 3, 4 };
Fraction f2 { 1, 2 };
Fraction sum { f1.add(f2) };
Fraction diff { f1.subtract(f2) };
f1.print();
print(" ");
f2.print();
println("");

        可以看出来,操作符重载允许你为类提供易用的接口。然而,注意不要滥用操作符重载。将+重载为减,将-重载为乘是可以的。但这种实现是反直觉的。这并不意味着每个操作符问题实现了同样的行为。例如,string类实现了+操作符是将strings进行连接,对于string连接来说是自然的接口。

3.2、不要省略必须的功能

        在设计接口的时候,要记住未来要什么。这是一个绑定在多年之后的设计吗?如果是的话,你要为扩展留下空间,用外挂的架构。是否有证据证明人们会为特定目的来使用你的接口,不管最初设计的目的是什么?与他们沟通,获得他们使用情况的更好的理解。否则的话就要以后重写,更糟的是,随意添加新功能,导致接口混乱。要小心啦,特别通用也是一个陷阱。不要设计包含全部的日志类,如果以后的使用不清晰的话。因为可能会有没有必要的复杂设计、实现以及公共接口。

        这个策略也是具有两面性的。首先,要包含客户可能需要的所有行为的接口。一开始听起来很明显。回到刚才小汽车的类比案例中,你永远不会建造一个没有让司机查看速度的速度表的小汽车!同样的,你也永远不会设计一个无法访问分子与分母的Fraction类。

        然而,其他的可能的行为就比较隐蔽了。这个策略要求你考虑到所有的用户可能放到你的代码中的使用情况。如果你用一种特别的方式思考接口,就有可能缺失用户使用方式不同的必须功能。例如,假设你要设计一个游戏板类。可能只是考虑了典型游戏,类似于棋牌类,决定板上最大一个点只支持一个棋子。然而,后来你决定写一个西洋双陆棋游戏,该游戏允许一个点上有多个棋子?排除了这种可能性,你的游戏板就违反了西洋双陆棋的游戏规则。

        很显然,考虑到每一种库的可能的使用情况是非常困难的,如果不是不可能的话。不要为了设计出完美的接口而对未来可能的使用情况而感到焦虑。只要多考虑一下,尽最大努力去做就好了。

        该策略的第二部分就是尽可能在实现中包含更多的功能。不要要求用户给出你在实现中早就知道的代码信息,或者知道你的设计之不同。例如,如果你的类需要一个临时文件,不要让客户给出其路径。他们不关心你使用什么文件;用其他方式来给出合适的临时文件路径吧。

        还有,不要要求库使用者执行不必要的操作来合并结果。如果你的随机数库使用了随机数算法来单独计算随机数的低阶与高阶位,在给到用户之前要把这些位数进行合并。

3.3、提供整洁的接口

        为了在接口中避免缺失功能,有些程序员走向了事情的另一个极端:他们包含了能想到的每一个可能的功能。使用这些接口的程序员不完成任务誓不罢休。可不幸的是,接口因此而变得杂乱而无法使用!这样的接口被叫做胖接口。

        不要在你的接口中提供不必要的功能;保持整洁和简单。这可能一开始看起来与前面所说的不要缺失掉必要的功能直接冲突。虽然避免缺失功能的策略就是包含每一个想象到的接口,这并不是一个合理的策略。你应该包含必要的功能,而去掉无用或者反生产的接口。

        还是考虑小汽车的案例。驾驶小汽车只是通过几个部件与小汽车进行交互:方向盘,刹车以及加速踏板(油门),变速排档、后视镜、速度计,以及仪表板的其他控件。现在,把小汽车的仪表板想象成与飞机驾驶舱类似的,带有上百的拨号、杠杆、监控、以及按钮。这是没用的!驾驶小汽车要比开飞机要简单得多,而接口也要简单得多:不需要查看高度,与控制台通信,或者控制类似于机翼、引擎以及起落架等飞机上的无数的部件。

        胖接口可以通过将接口划分成几个小的接口来避免。要不就用外观设计模式来提供更容易的接口或者胖接口中的接口集。例如,一个胖的小汽车接口会包含从简单动作,比如加速、刹车、转向等,到更高级的功能,像调节引擎的性能的多种选项,还有更多。更好的设计是提供多个更容易使用的接口:一个是像加速、刹车和转向等的基础操作;另一个提供访问引擎调优选项;还有更多。

        另外,从库开发的角度,小的库更容易维护。如果要使大家都开心,你就有了犯错误的更大空间,如果你的实现复杂到所有东西都搅和在一起,即使是一个小错误也会使整个库无法使用。

        不幸的是,设计整洁的接口在本本上看起来很美,但是实践上很难。判断规则也是主观的:你来决定什么是必须的,什么不是。当然了,你的用户会明确地告诉你什么时候是错的,那也是事后的诸葛亮。

3.4、提供文档

        不管你的接口有多易用,也要提供使用文档。在没有告诉他们怎么做之前,你无法期待程序员正确地使用你的库。把你的库或者代码想象成其他程序员消费的产品。你的产品应该有文档解释其正确使用方法。

        有两种方法提供接口的文档:在接口中加上注释或者额外提供文档。尽量两者都提供。大部分公共的API只提供额外的文档:在许多标准Unix和Window头文件中并没有提供详尽的注释。在Unix中,文档通常以man pages的方式提供。在Windows中,文档通常与集成开发环境一起提供或者放到互联网上。

        虽然大部分API和库省略了接口本身的注释,文档的形式依然很重要。你不可能只提供只有代码的模块或头文件。即使注释就是外部文档的简单重复,浏览带有友好注释的模块或者头文件也比只有代码要好得多。即使是最棒的程序员也喜欢看有注释的代码!怎么写注释我们前面花了大量的笔墨,在这儿就不多讨论了。

  • 18
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王俊山IT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值