条款18:让接口容易被正确使用,不易被误用

简介: 条款18:让接口容易被正确使用,不易被误用
程序设计中经常遇到的便是如何设计接口,让用户能够直观的、快速的了解并上手使用接口。让接口容易被正确使用,不易被误用便是本条款所强调的。
理想上,如果客户企图使用某个接口而却没有获得他所预期的行为,这个代码不应该通过编译;如果代码通过了编译,他的作为就该是客户所想要的。

比如这样一个表示时间的类:

class Date {
public:
    Date(int month, int day, int year);
};

如果我们不对接口做一些“强制性“的约束,该接口就可能被误用:

Date date(2022, 2, 25); //传参顺序不对
Date date(31, -1, 0); //参数非法

虽然上述调用可以通过编译,但却可能与用户意图违背。为此我们可以采用类型系统:

class Month {
public:
    explicit Month(int m) : month(m) {
    }
private:
    int month;
};
class Day {
public:
    explicit Day(int d) : day(d) {
    }
private:
    int day;
};
class Year {
public:
    explicit Year(int y) : year(y) {
    }
private:
    int year;
};
class Date {
public:
    Date(const Month &m, const Day &d, const Year &y);
};

这时,我们对用户使用该类的方式做了一些约束,比如:

Date date(2022, 2, 25);       //报错,explicit禁止隐式类型转换
Date date(Day(25), Month(2), Year(2022));//报错,传参顺序不对
Date date(Month(2), Day(25), Year(2022));//正确

虽然我们对传参的顺序,方式做了一定的约束,但是还是避免不了传入非法值,比如:

Date date(Month(22), Day(25), Year(2022));

月份为22显然不合法。为此,我们可以预先定义所有有效的值(即告知用户,只能使用这几个值哦):

class Month {
public:
    static Month Jan() { return Month(1); }
    static Month Feb() { return Month(2); }
    //...
private:
    explicit Month(int m);
};

再使用时,只能这样,这样总不会写错了吧?:

Date date(Month::Jan(), Day(25), Year(2022));

预防用户错误的另一个做法是,限制类型内什么事可做,什么事不可做。常见的限制是加上const。

比如:我们重载了*并且返回值被const限定,当用户一不小心将==写成=时,程序可以告知用户,唉?你这里是不是要做比较?而不是赋值??

if (a * b = c) // ?

后面部分理解不是很透彻,再续。

关于shared_ptr删除器的使用:

void del(int *p) {
  cout << "del" << endl;
}
void func() {
  shared_ptr<int> p(new int(3), del);
  cout << p << endl;
  shared_ptr<int> p1(new int(4), [](int *p) { cout << p << endl; cout << "lambda" << endl; });
  cout << p1 << endl;
}
相关文章
|
监控 安全 数据安全/隐私保护
在开源代码的时候该如何避免安全风险的发生?
作为开发者来讲,不管是在实际开发中使用开源项目,还是直接投身于开源的贡献中,关于开源相关的内容想必都有自己独到的见解。开源与开发者息息相关,可能有的开发者会觉得不使用开源项目,自己就与开源无关了?这种想法是片面的,因为就算没有在实际开发中使用开源项目,但是在实际开发中肯定会用到一些第三方的插件,那么能保证这些插件没有用到开源的内容么?所以,开源与每一位开发者都有联系。
313 2
在开源代码的时候该如何避免安全风险的发生?
|
数据采集 算法
拒绝想当然,不看文档导致GNE 的隐秘 bug
拒绝想当然,不看文档导致GNE 的隐秘 bug
122 0
|
Java 容器 Spring
521我发誓读完本文,再也不会担心Spring配置类问题了(下)
521我发誓读完本文,再也不会担心Spring配置类问题了(下)
521我发誓读完本文,再也不会担心Spring配置类问题了(下)
|
IDE Java 中间件
521我发誓读完本文,再也不会担心Spring配置类问题了(上)
521我发誓读完本文,再也不会担心Spring配置类问题了(上)
521我发誓读完本文,再也不会担心Spring配置类问题了(上)
|
Java 容器 Spring
521我发誓读完本文,再也不会担心Spring配置类问题了(中)
521我发誓读完本文,再也不会担心Spring配置类问题了(中)
521我发誓读完本文,再也不会担心Spring配置类问题了(中)
|
Java 程序员
用了这么久的equals,你知道还要遵守约定么(上)
重写equals 方法看起来很简单,但是还会有多种方式导致出错,后果可能是严重的。最简单,最容易避免出错的方式是 避免重写equals方法 ,采用这种方式的每个类只需要和自己对比即可,这样永远不会出错。如果满足了以下任何一个约定,也能产生正确的结果:
用了这么久的equals,你知道还要遵守约定么(下)
重写equals 方法看起来很简单,但是还会有多种方式导致出错,后果可能是严重的。最简单,最容易避免出错的方式是 避免重写equals方法 ,采用这种方式的每个类只需要和自己对比即可,这样永远不会出错。如果满足了以下任何一个约定,也能产生正确的结果:
|
设计模式 Java 编译器
恕我直言,我怀疑你没怎么用过枚举
我们是否一样? 估计很多小伙伴(也包括我自己)都有这种情况,在自学Java语言看书时,关于枚举enum这一块的知识点可能都有点 “轻敌” ,觉得这块内容非常简单,一带而过,而且在实际写代码过程中也不注意运用。 是的,我也是这样!直到有一天我提的代码审核没过,被技术总监一顿批,我才重新拿起了《Java编程思想》,把枚举这块的知识点重新又审视了一遍。 为什么需要枚举 常量定义它不香吗?为啥非得用枚举? 举个栗子,就以B站上传视频为例,视频一般有三个状态:草稿、审核和发布,我们可以将其定义为静态常量: public class VideoStatus { public st
145 0
|
IDE Java 中间件
521我发誓读完本文,再也不会担心Spring配置类问题了
Spring配置类专栏研究得最深的一篇,不为别的,只为涨薪
606 0
521我发誓读完本文,再也不会担心Spring配置类问题了
如果你有医生朋友,请务必转发给他们!
今天,阿里云联合钉钉上线国际医生交流平台。
如果你有医生朋友,请务必转发给他们!

热门文章

最新文章