• 为用户设计良好的接口


    前言

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

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

    思路一:导入新的类型

      下面还是先看这个例子,我定义了一个存储日期的 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的时候要调用的析构函数:

     1 std::tr1::shared_ptr<Investment> createInvestment()
     2 {
     3     // 指定智能指针类型及删除器
     4     std::tr1::shared_ptr<Investment>retVal (static_cast<Investment *>(0), getRidOfInvestment);
     5     
     6     // retVal = ...
     7     // 令 retVal 指向正确的对象
     8 
     9     return retVal;
    10 }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        将它声明为 private

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

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

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

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

    小结

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

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

  • 相关阅读:
    《数据库技术基础与应用(第2版)》学习笔记——第4章
    《数据库技术基础与应用(第2版)》学习笔记——第4章
    《数据库技术基础与应用(第2版)》学习笔记——第3章
    《数据库技术基础与应用(第2版)》学习笔记——第3章
    《数据库技术基础与应用(第2版)》学习笔记——第2章
    《数据库技术基础与应用(第2版)》学习笔记——第2章
    《数据库技术基础与应用(第2版)》学习笔记——第1章
    《数据库技术基础与应用(第2版)》学习笔记——第1章
    新近碰到的病毒(TR.Spy.Babonock.A)
    新近碰到的病毒(TR.Spy.Babonock.A)
  • 原文地址:https://www.cnblogs.com/scut-fm/p/3973217.html
Copyright © 2020-2023  润新知