□第一节 什么是接口?
什么是接口?
百度百科的解释:两个不同系统(或子程序)交接并通过它彼此作用的部分。
接口的概念贯穿于整个软件开发过程中,本文讨论的接口概念仅局限于以C++实现的class,function等。

class接口
以OOP的思想,现实的问题总能抽象成一个个class,这些class包含哪些属性,方法其实就是class接口要考虑的问题。
function接口
class接口的操作就是通过一个个function来实现的,function的参数传递,返回值是function接口要考虑的问题。
本条款“让接口容易被正确使用,不易被误用” 是设计优良接口重要准则之一,并在开始的时候就需要考虑的。

“定义输入”
要开发一个“让接口容易被正确使用,不易被误用”的接口,首先要考虑使用该接口可能产生的错误。考虑用来表示时间的Date类。

class Date {
private:
int year; //年份
short month; //月份
short day; //天数
public:
Date(int year, short month, short day) {
this->year = year;
this->month = month;
this->day = day;
}
};

今天是2013/02/28,今天的日期可以表示为

Date d(,,);

那明天的日期可以表示为

Date d(,,);

问题来了,刚才手抖一下,把“03”敲成“13”。
还有一个问题,假如不小心也可能出现情况:

Date d(,,); //天数与月份反了

上面的两个问题系统都不会被系统检测,也就是我们的接口接收到错误输入而没有做出任何阻止。

“使用新类型”
导入新类型来覆盖已有的数据类型是一个不错的方案。

struct Year {
explicit Year(int y):val(y) { }
int val;
};
struct Month {
explicit Month(int m):val(m) { }
int val;
}; struct Day {
explicit Day(int d):val(d) { }
int val;
};

Date的构造函数可以是下面的样子:

Date(const Year& y, const Month& m, const Day& d);

Date d(Year(), Month(), Day()); // OK
Date d(Year(), Day(), Month()); // NG,类型错误

另外一个问题的解决很容易想到的方法是使用枚举:

关于年份,似乎输入任何一个数值都是合法的,所以不需要枚举。
关于月份,一年中只有12个月,所以可以枚举1~12的值。
比如月份的枚举如下:

enum Month {
month1= ,
month2,
...
month12,
};

那么修改后的Date类应该是下面的样子

Date(Year year, Month month, Day day);

这样做法可以解决一部分问题,至少不会出现月份越界的情况。
于是明天的日期可以这么写:

Date d(Year(),month(3) , Day());

但很不幸C++中的枚举只是被拿来当作int值来用,例如下面的情况是可以通过编译的:

Date d(Year(), (Month), Day());

幸好,还有更厉害的一招:预定义所有有效的Months

class Month {
public:
static Month Jan() { return Month(); }
static Month Feb() { return Month(); }
...
static Month Dec() { return Month(); }
private:
explicit Month(int m);
}
Date d(Year(), Month::Feb(),Day());

但这种做法的看起来很诡异,而且当要枚举的集合中有很多元素时,势必影响到代码生产效率和系统性能。

总结
在接口设计的时候引入新类型,限定边界值是保证接口的正确性的有效措施之一。
但过分追求接口的正确性往往会带来系统逻辑复杂度的增加。

□第二节 让接口更容易些
“望文生义”
每个人都有名字,名字的作用就是用来区分个体的。名字往往能给我们带来很多信息。比如我的名字叫“小明”,很容易知道
我名字的读音“xiaoming”,是个男的。假如有个人叫“李芳”我说他是个男的,你信吗?

函数也有名字,这个名字是给人读的。所以当给接口定义名称的时候最好通俗易懂,"见名知意"。

“多重性”
用过.NET的都知道,Arrays有个属性名为Length用于求数组中元素个数,ArrayLists中有个属性为Count,也是干这事的。
相信肯定有过这样的经历:当使用元素个数的时候总是先尝试用Length,发现没有再换Count。是不是有点郁闷?

“自己动手”
有些事情自己能做,那就不要依赖别人。当接口的某些行为依赖于调用者来完成,那就犯了大忌。
比如我们的ConductorFactory,创建完Conductor就不管了。比如下面的代码:

cgi_CallStatus cgi_ConductorFactory::cgi_CreateSetSyslogConductor(cgi_SettingConductor *&conductor,
cgi_Status &status)
{
status.okNg = CALL_OK; conductor = MMI_NEW cgi_SettingConductor(
cgi_Translate807a::Request,
cgi_Translate807a::Response,
cgi_Translate807a::GetSize,
NULL,
NULL,
NULL); if (conductor == NULL) {
cgi_TranslatorCommon::SetErrorStatus(CGI_ERROR_NEW, CGI_CONDUCTOR_FACTORY,
CGI_FACTORY_SETSYSLOGCONDUCTOR, , status, NULL);
return CALL_ERROR;
} return CALL_OK;
}

conductor的销毁显然不是由ConductorFactory来负责,假如使用完没有delete,很显然内存泄漏就产生了。
我们可以使用智能指针来解决:

cgi_ConductorFactory::cgi_CreateSetSyslogConductor(std::tr1::shared_ptr<cgi_SettingConductor> conductor,  cgi_Status &status) {
...
}

什么是接口应该做的,什么是调用者要做的,在设计时一定要多加思考。

总结
容易理解的名字,一致的行为表现会让接口更容易使用,并且不会发生错误。接口尽量不要试图让调用者做些事情,因为它可能不听话。

■总结■
1.合理考虑接口的输入,并尽可能的阻止错误数据的输入
2.阻止错误的方法包括建立新类型,限制类型上的操作,束缚对象值,以及消除客户的资源管理责任
3.给接口一个容易理解的名字,告诉别人接口是干什么用的
4.让接口的功能与其行为保持一致
5.在系统中如果是做同一件事的,尽可能的保持接口名字的一致
6.尽可能少的期待调用者要做的事情,因为不能保证调用者会按照要求去做

[Effective C++ --018]让接口容易被正确使用,不易被误用的更多相关文章

  1. Effective C++(18) 让接口更容易被正确使用,不易被误用

    问题聚焦:     从这个条款开始,我们把注意力转移到软件设计和声明上来,具体的说就是,C++接口的设计和声明.     所谓软件设计,就是以一般习惯的构想开始,演变成细节的实现,最终开发针对性的特殊 ...

  2. 读书笔记 effective c++ Item 18 使接口容易被正确使用,不容易被误用

    1. 什么样的接口才是好的接口 C++中充斥着接口:函数接口,类接口,模板接口.每个接口都是客户同你的代码进行交互的一种方法.假设你正在面对的是一些“讲道理”的人员,这些客户尝试把工作做好,他们希望能 ...

  3. EC笔记:第4部分:18、接口正确使用,不易被误用

    好的接口容易被正确使用,不易被误用 考虑以下函数: void func(int year,int month,int day){ //一些操作 } 这个函数看似合理,因为参数的名字已经暴露了它的用途. ...

  4. 条款18:让接口容易被正确使用,不易被误用(Make interface easy to use correctly and hard to use incorrectly)

    NOTE : 1.好的接口容易被正确使用,不容易被误用.应该让所有接口努力达成这些性质. 2.“促进正确使用”的办法包括接口的一致性,以及内置类型的行为兼容. 3.“阻止误用”的办法包括建立新类型/限 ...

  5. Effective C++ -----条款18:让接口容易被正确使用,不易被误用

    好的接口很容易被正确使用,不容易被误用.你应该在你IDE所有接口中努力达成这些性质. “促进正确使用”的办法包括接口的一致性,以及与内置类型的行为兼容. “阻止误用"的办法包括建立新类型.限 ...

  6. C++设计与声明——让接口容易被正确使用

    一个简答易错的例子 class Date { public Date(int month,int day,int year); } 一年后使用这个接口的时候,写了Date d(15,10,2015)或 ...

  7. Effective C++ 34 区分接口继承和实现继承

    public继承从根本上讲,有两部分:接口继承和实现继承.两者之前的区别很像函数声明与函数定义. 具体设计中,会呈现三种形式:derived class只继承成员函数的接口(纯虚函数):derived ...

  8. effectivec++条款18,让接口容易被正确使用,不宜被吴勇

    test* create_test(); shared_ptr<test> create_test();//下面的接口比上面的接口要好 auto_ptr<test> creat ...

  9. 和我一起学Effective Java之类和接口

    类和接口 使类和成员的可访问性最小 信息隐藏(information hiding)/封装(encapsulation):隐藏模块内部数据和其他实现细节,通过API和其他模块通信,不知道其他模块的内部 ...

随机推荐

  1. shell 全局和局部变量

    /******************************************************************** * shell 全局和局部变量 * 声明: * 到目前为止, ...

  2. c语言编程风格

    关于c语言的编程风格,不同的书上有不同的推荐,不同的公司有不同的要求.这里,做了一下基本规范说明. 1.变量定义 在定义变量时,前缀使用变量的类型,之后使用表现变量用途的英文单词或单词缩写,且每个单词 ...

  3. oracle静态与动态监听

    在运行lsnrctl命令的status时,常会看到如下返回值: 服务“test”包含1个例程.    例程"mydata",状态 UNKOWN,包含此服务的一个处理程序... 服务 ...

  4. Fine Uploader文件上传组件

    最近在处理后台数据时需要实现文件上传.考虑到对浏览器适配上采用Fine Uploader. Fine Uploader 采用ajax方式实现对文件上传.同时在浏览器中直接支持文件拖拽[对浏览器版本有要 ...

  5. 《深入Java虚拟机学习笔记》- 第3章 安全

    3.1为什么需要安全性 Java的安全模型是其多个重要结构特点之一,它使Java成为适于网络环境的技术.因为网络提供了一条攻击连人的计算机的潜在途径,因此安全性是非常重要的.Java安全模型侧重于保护 ...

  6. HDU4614 Vases and Flowers 二分+线段树

    分析:感觉一看就是二分+线段树,没啥好想的,唯一注意,当开始摆花时,注意和最多能放的比大小 #include<iostream> #include<cmath> #includ ...

  7. 如何学习ios开发

    著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处.作者:Franz Fang链接:http://www.zhihu.com/question/20264108/answer/3026 ...

  8. 【译】 AWK教程指南 9读取命令行上的参数

    大部分的应用程序都允许使用者在命令之后增加一些选择性的参数.执行awk时这些参数大部分用于指定数据文件文件名,有时希望在程序中能从命令行上得到一些其它用途的数据.本小节中将叙述如何在awk程序中取用这 ...

  9. 导入showb时候出错--2015-12-4

    [root@cache-02 ~]# /opt/coreseek/csftweb-bash: /opt/coreseek/csftweb: is a directory[root@cache-02 ~ ...

  10. STL(pair map set vector priority_queue) poj 3297

    POJ 3297 算法竞赛初级杂烩包 题意:学生选课,没个学生只能选一门课.大写字符是课的名字,小写是人名.如果课程后面有多个相同名字算一个,如果一个人选多门课,则他选不上课,输出课和每门课选课人数 ...