接口设计

·  阅读 129

接口应该使用引用、常量、传递值?

image.png

!当然,应该尽量避免直接访问类成员变量,因为您无法控制这一成员的状态和其他类成员的状态之间的相互作用。

例1

看看下面这行调用,猜猜它的接口?

ps = read(filename, '\t', maxN, true, false);

可能会先想到

SomeType? read(string?, char, int?, bool, bool);

看看这个定义

vector<Point> read(
  std::string const& filename,
  char sepaeator = ' ',
  int maxN = -1,
  bool positiveXYonly = false,
  bool continueOnError = true);

如果你想要拓展这个接口,该怎么做?比如增加一个选项,只读x<=0,y<=0的Points

Attempt1:

vector<Point> read(
  std::string const& filename,
  char sepaeator = ' ',
  int maxN = -1,
  bool positiveXYonly = false,
  bool negativeXYonly = false,
  bool continueOnError = true);
//broken legacy calls:
... = read(fname, '\t', maxP, true, true);
... = read(fname, '\t', maxP, true, false);

Attempt2:

vector<Point> read(
  std::string const& filename,
  char sepaeator = ' ',
  int maxN = -1,
  bool positiveXYonly = false,
  bool continueOnError = true,
  bool negativeXYonly = false,);
//positiveXYonly AND negativeXYonly
... = read(fname, '\t', maxP, true, true, true);

this interface is fundamentally broken

  • 难以理解
  • 难以正确使用
  • 几乎不能拓展

改进

函数名

ps = read(...);
vs.
ps = readPoints(...);

-1作为infinity value很不好

vector<Point> readPoints(
  std::string const& filename,
  char sepaeator = ' ',
  int maxN = std::numeric_limits<int>::max(),
  bool positiveXYonly = false,
  bool continueOnError = true);

如果需要所有2^64的值?(有需要的话取代int)

vector<Point> readPoints(
  std::string const& filename,
  char sepaeator = ' ',
  size_t maxN = std::numeric_limits<size_t>::max(),
  bool positiveXYonly = false,
  bool continueOnError = true);

摆脱bool参数

enum class exclude {
    none, negativeXY, positiveXY
}

enum class on_error {
    continue_reading, stop_reading, empty_result
}

vector<Point> readPoints(
  std::string const& filename,
  char separator = ' ',
  size_t maxN = std::numeric_limits<size_t>::max(),
  exlude = exclude::none,
  on_error = on_error::continue_reading);

分割符的专业类型

class value_separator {
public:
    constexper explicit
    value_separator(char s = ' ') noexcept:s_{s} {}
    
    constexper char value() const noexcept {return s_;}
private:
    char s_;
}

数量限制的专业类型

class count_limit {
public:
    constexper explicit
    count_limit(size_t n) noexcept:n_{n} {}
    
    constexper int value() const noexcept {return n_;}
    
    static constexper count_limit
    none() noexpect {
        return count_limit{ std::numeric_limits<size_t>::max() }
    }
private:
    size_t n_;
}

So Far

vector<Point> readPoints(
  std::string const& filename,
  value_separator separator = ' ',
  count_limit = count_limit::none(),
  exlude = exclude::none,
  on_error = on_error::continue_reading);
...  
ps = readPoints(filename,
                value_separator{'\t'},
                count_limit{maxp},
                exlude::negativeXY,
                on_error::stop_reading);

高级参数-Points过滤器

使用std::function 或者 模板参数

vector<Point> readPoints(
  std::string const& filename,
  value_separator separator = ' ',
  count_limit = count_limit::none(),
  on_error = on_error::continue_reading,
  std::function<bool(Point const&)> filter = [](Point const&) {return true});

...  

ps = readPoints(filename,
                value_separator{'\t'},
                count_limit{maxp},
                on_error::stop_reading
                [](Point const& p) {
                    return p.x >= 0 && p.y >= 0;
                });

带特殊构造的专用SettingsType

ps = read_points(filename,
                 read_settings {
                     count_limit{maxP},
                     on_error::stop_reading,
                     [](ponut const& p) {
                        return p.x >= 0 && p.y >= 0; 
                     }
                 });

Settings和Action分离的SettingsType,

  • 设置不能独立于读取操作使用

  • 可以执行多个读取操作,而无需反复指定设置

  • 可以携带附加状态:上一个读取操作影响下一个

read_settings how;
how.max_count(maxP);
how.filter( [](ponut const& p) {
                return p.x >= 0 && p.y >= 0; 
            });
how.error_action(on_error::stop_reading);

ps = read_points(filename, how);

例2

image.png

输入: 归一化的向量(x, y, z) 和旋转的角度(a)
输出:旋转后的向量(x, y, z)

quaternion make_screw_rotation (double a, double x, double y, double z);
  • 问题1:四个参数什么是什么?角度?轴?->read docs & good luck
  • 问题2:angle是角度还是弧度? ->no way to check
  • 问题3:轴是normalized的吗? -> costly check
  • 问题4:结果是一个quaternion 四元组吗? -> costly check

编译器可检查的参数顺序

quaternion make_screw_rotation (double angle, vector3d const& axis);

输入约束类型

quaternion make_screw_rotation (radians a, direction3d const& d);

auto d = direction3d{1.0, 0.0, 1.0};
auto a = degrees{45.0};
auto q = make_screw_rotation(a, d);


template<class Trun>
class angle {...}
using degrees = angle< degrees_turn<double> >;
using radians = angle< radians_turn<double> >;
radians a1{ pi / 2 };
degrees a2 = a1;

templates<int dims, class NumT> 
class direction {...}
using directions3d = direction<3, double>;

directions3d d {1.0, 0.0, 1.0};

分类:
代码人生
标签:
分类:
代码人生
标签:
收藏成功!
已添加到「」, 点击更改