第十二篇:为用户设计良好的接口

前言

作为一名优秀的程序员,必须保证自己的代码能提供正确的,完善的接口,如此方能和同事,甲方更好的沟通合作,也让自己的代码更加地容易维护。

本文将介绍一些设计优秀接口的思路。

思路一:导入新的类型

下面还是先看这个例子,我定义了一个存储日期的 Date 类:

1 class Date
2 {
3 public:
4     Date(int month, int day, int year);
5     // ......
6 };

可用以下方法定义一个 Date 对象:

1 Date d(30, 3, 1995);

可有些用户会犯很蠢的错误,比如:

1 Date d(30, 3, 1995);

显然,他用户将接口的参数输错位了。然而,优秀的接口应当能够友好反馈错误信息给用户,这种情况下,最好的策略就是定义新的类型,请参考下面这个 Date 类的设计:

 1 class Day
 2 {
 3 public:
 4     explicit Day(int d)
 5         :val(d) {}
 6     // ......
 7 private:
 8     int val;
 9 };
10
11 class Month
12 {
13 public:
14     static Month Jan() {
15         return Month(1);
16     }
17     static Month Feb() {
18         return Month(2);
19     }
20     // ......
21
22 private:
23     explicit Month(int m) {
24         val = m;
25     }
26     // ......
27
28     int val;
29     // ......
30 };
31
32 class Year
33 {
34 public:
35     explicit Year(int y)
36         :val(y) {}
37 private:
38     int val;
39 };

而定义一个 Date 对象,可采用如下方式:

1 Date d(Month::Feb(), Day(30), Year(1995));

在日,月,年各个类中,还可以实现更高级的封装。

思路二:引导用户进行正确编码

这里继续上一篇文章中提到的智能指针的一个例子,这里要说明的是,当时给出的那个工厂函数:

1 class Investment
2 {
3     // ......
4 };
5
6 Investment * createInvestment();

并不是很好的一种设计。

为啥?因为用户可能忘了使用智能指针把 Investment * 接过去。而使用下面的工厂函数接口设计可以有效的避免这个问题:

1 std::tr1::shared_ptr<Investment> createInvestment();

这样就让用户你不用智能指针都不行了,哈哈。

甚至你还可以更过分,指定智能指针在资源被指数为0的时候要调用的析构函数:

std::tr1::shared_ptr<Investment> createInvestment()
{
    // 指定智能指针类型及删除器
    std::tr1::shared_ptr<Investment>retVal (static_cast<Investment *>(0), getRidOfInvestment);

    // retVal = ...
    // 令 retVal 指向正确的对象

    return retVal;
}

上段代码中的getRidOfInvestment是你自己指定的删除器。

思路三:限制类型什么事情可以做什么事情不能做

使用 const,explicit等限制性关键字,屏蔽无用的拷贝构造函数等可以做到这点。

这些在以前的文章中均有讲解。

思路四:使你的类尽量表现得像内置类型

要做到这点可不简单,你需要以"当初语言设计者设计语言内置类型时"那般谨慎的思考class的设计,对设计出的class,我们需要问自己以下几个问题:

1. 新的对象资源在何时创建? 何时销毁?

这部分同样涉及到构造函数,析构函数的编写。

2. 对象的初始化和赋值应该有什么样的差别?

这部分涉及到构造函数,拷贝构造函数,赋值运算符的编写。不要混淆这两个概念。

3. 如果对象发生了值传递,意味着什么?

你得仔细考虑这期间发生的资源相关的一些问题。

4. 哪些对象是合法范畴?

对象的成员是不是合法,这点很重要。它影响到了你诸多成员函数的错误检查工作,也影响到了抛出的异常。

5. 新的类需不需要配合某个继承图系?

如果这个类的子类要实现多态,那么成员函数就得声明为虚函数;如果这个类继承自其它类,那么当你自定义拷贝构造函数或者重载赋值运算符的时候,也得对父类部分做出处理。

6. 什么样的操作符和函数对这个新的类型来说是合理的?

需要考虑这个类型应该对哪些运算符重载,还有哪些函数被当做成员函数,哪些用非成员函数实现。

具体的选取规则,以后会有篇文章专门讲。

7. 什么样的标准函数应当驳回?

将它声明为 private

8. 新的类型成员将被哪些对象取用?

这个涉及到变量private,protected,以及友元相关机制。

9. 新的类型是否应当满足一般化的要求?

如果你要定义的是一个类家族,那么你需要的不止是一个类,而是一个类模板

小结

1. 类的设计不要贪快。要尽量满足,实现这些规则,贪快会导致开发后期事倍功半。

2. 本文应当在实际项目中进行类设计的时候边设计边看,如此,方能有显著的提高。

时间: 2024-10-07 16:38:22

第十二篇:为用户设计良好的接口的相关文章

Python开发【第二十二篇】:Web框架之Django【进阶】

Python开发[第二十二篇]:Web框架之Django[进阶] 猛击这里:http://www.cnblogs.com/wupeiqi/articles/5246483.html 博客园 首页 新随笔 联系 订阅 管理 随笔-124  文章-127  评论-205 Python之路[第十七篇]:Django[进阶篇 ] Model 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计表结构和字段 使用 MySQLdb 来连接数据库,并编写数据访问层代码 业务逻

SaltStack 学习笔记 - 第十二篇: SaltStack Web 界面

SaltStack 有自身的用python开发的web界面halite,好处是基于python,可以跟salt的api无缝配合,确定就比较明显,需要个性化对web界面进行定制的会比较麻烦,如果喜欢体验该界面的可以参考下面的文章  http://rfyiamcool.blog.51cto.com/1030776/1275443/ 我是运用另一个python+php来进行web开发,具体需要的工具有在我的另一篇文章里面介绍过,这里再重新进行整个开发介绍 首先介绍php 跟python通信的工具 pp

第二十二篇:再写Windows驱动,再玩Windbg---NET

2011年到现在,就没再怎么搞过Windows驱动了. 最近, 由于项目需要, 试着改一改一个显卡驱动(KMDOD), 从实践上证明, 我在理论上对一个驱动的架构的正确与否.(USB Display = KMDOD + AVStream). 其中, KMDOD是完成显示的部分功能, 完成其中的VidPN(Video present network), 将驱动中原来的POST物理设备转变为USB物理设备. 而AVStream之所以这样提出, 完成是由于USB Video class的启发, 要不然

跟我学SpringCloud | 第十二篇:Spring Cloud Gateway初探

SpringCloud系列教程 | 第十二篇:Spring Cloud Gateway初探 Springboot: 2.1.6.RELEASE SpringCloud: Greenwich.SR1 如无特殊说明,本系列教程全采用以上版本 前面我们在聊服务网关Zuul的时候提到了Gateway,那么Zuul和Gateway都是服务网关,这两个有什么区别呢? 1. Zuul和Gateway的恩怨情仇 1.1 背景 Zuul是Netflix开源的一个项目,Spring只是将Zuul集成在了Spring

Mysql优化(出自官方文档) - 第十二篇(优化锁操作篇)

目录 Mysql优化(出自官方文档) - 第十二篇(优化锁操作篇) 1 Internal Locking Methods 2 Metadata Locking 3 External Locking Mysql优化(出自官方文档) - 第十二篇(优化锁操作篇) 1 Internal Locking Methods 这里介绍Mysql的几种锁,该锁由Mysql自行进行管理,用户不需要处理该锁. Row-Level Locking 对于InnoDB,行锁可以通过SELECT ... FOR UPDAT

Android之十二微信UI界面设计

Android之十二微信UI界面设计 corners_bg.xml <span style="font-size:14px;"><?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color=&qu

shell编程(十二)--- 添加用户示例

[[email protected] Learn]# cat useradd-final.sh  #!/bin/bash # DEBUG=0 ADD=0 DEL=0 help() { echo "Usage: $(basename $0) -v | --verbose | --add user1,user2,... | --del user1,user2,... | -h | --help" } while [ $# -ne 0 ] do case $1 in -h | --help 

SQL Server 索引的图形界面操作 &lt;第十二篇&gt;

一.索引的图形界面操作 SQL Server非常强大的就是图形界面操作.关于索引方面也一样那么强大,很多操作比如说重建索引啊,查看各种统计信息啊,都能够通过图形界面快速查看和操作,下面来看看SQL Server索引方面的GUI操作. 二.索引统计信息的图形界面操作 SQL Server 索引的图形界面操作 <第十二篇>

第二十二篇 信念

第二十二篇  信念 "信念"能带给一个人无穷的力量,这些力量可以支撑自己走过漫长的人生.一个人如果没有信念,就很难找到自己的人生方向,所以"信念"也可以理解为希望. 信念可以给到我们希望,也可以给到我们力量,所以一个人的信念会影响到自己的整个人生.当然信念也有好坏之分,好的信念能让自己积极向上.不畏艰难:坏的信念会让我们不思进取.随波逐流.这两种不同的信念会给到我们两种完全不同的人生,就看亲人们如何作出正确的选择. 一个人活在世上,可以选择走正确的人生道路,依靠好的

解剖SQLSERVER 第十二篇 OrcaMDF 行压缩支持(译)

解剖SQLSERVER 第十二篇   OrcaMDF 行压缩支持(译) http://improve.dk/orcamdf-row-compression-support/ 在这两个月的断断续续的开发工作中,我终于将OrcaMDF 压缩功能分支合并到主分支这意味着OrcaMDF 现在正式支持数据行压缩功能 支持的数据类型实现行压缩需要我修改几乎所有已实现的数据类型以将他们作为压缩存储.integer类型被压缩了,decimal类型变成可变长度,而可变长度类型基本上都被截断了进而用0来填补.所有先