C++设计:接口与实现分离

本文翻译自C++之父的CppCoreGuidelines部分章节:C.129

C++中的接口

C++中的接口一般设计为不包含成员变量的抽象类,这样就只有接口的功能。

如果接口类中包含了成员变量,那么势必会给派生带来麻烦。

不好的设计:接口与实现混合

 

class Shape {   // BAD, mixed interface and implementation
public:
    Shape();
    Shape(Point ce = {0, 0}, Color co = none): cent{ce}, col {co} { /* ... */}

    Point center() const { return cent; }
    Color color() const { return col; }

    virtual void rotate(int) = 0;
    virtual void move(Point p) { cent = p; redraw(); }

    virtual void redraw();

    // ...
public:
    Point cent;
    Color col;
};

class Circle : public Shape {
public:
    Circle(Point c, int r) :Shape{c}, rad{r} { /* ... */ }

    // ...
private:
    int rad;
};

class Triangle : public Shape {
public:
    Triangle(Point p1, Point p2, Point p3); // calculate center
    // ...
};

缺点:

 

1 给Shape增加数据成员,所有派生类都要多出这个成员,不管是否需要。而且都要重新编译。

2 有的派生类并不需要Shape中的某些数据成员,但也不得不接受。
 

接口示例:纯接口类的Shape

 

class Shape {  // pure interface
public:
    virtual Point center() const = 0;
    virtual Color color() const = 0;

    virtual void rotate(int) = 0;
    virtual void move(Point p) = 0;

    virtual void redraw() = 0;

    // ...
};

 

无须构造,因为没有数据成员

简单实现类的示例:

 

class Circle : public Shape {
public:
    Circle(Point c, int r, Color c) :cent{c}, rad{r}, col{c} { /* ... */ }

    Point center() const override { return cent; }
    Color color() const override { return col; }

    // ...
private:
    Point cent;
    int rad;
    Color col;
};

 

但是,这样以来,实现类的成员函数就变得繁琐,因为每个实现类都要实现一边接口的成员函数。

将接口和实现分离:

1 接口继承:

 

class Shape {   // pure interface
public:
    virtual Point center() const = 0;
    virtual Color color() const = 0;

    virtual void rotate(int) = 0;
    virtual void move(Point p) = 0;

    virtual void redraw() = 0;

    // ...
};

class Circle : public Shape {   // pure interface
public:
    int radius() = 0;
    // ...
};

2 实现继承:实现放入名称空间Impl中

下面是实现(因为每个函数后面都有大括号),实现放到了Impl这个名称空间中了,这样使用Shape的客户端代码就不会随着Impl中Shape类的成员变量的变化而重新编译,因为客户端代码是看不到这些内容的,编译的时候也没有绑定这些信息。

class Impl::Shape : public Shape { // implementation
public:
    // constructors, destructor
    // ...
    virtual Point center() const { /* ... */ }
    virtual Color color() const { /* ... */ }

    virtual void rotate(int) { /* ... */ }
    virtual void move(Point p) { /* ... */ }

    virtual void redraw() { /* ... */ }

    // ...
};

这样一来接口变化只是成员函数的变化,成员变量的增减就看不到了,因为成员变量在Impl::Shape中

考虑派生类的写法:派生类也和上面一样提供了实现类,用户只使用接口类和包含实现类的实现的DLL,从而不受实现类变换的影响,不需要重新编译。可以直接替换DLL

class Impl::Circle : public Circle, public Impl::Shape {   // implementation
public:
    // constructors, destructor

    int radius() { /* ... */ }
    // ...
};

考虑新增派生类的写法:

class Smiley : public Circle { // pure interface
public:
    // ...
};

class Impl::Smiley : Public Smiley, public Impl::Circle {   // implementation
public:
    // constructors, destructor
    // ...
}

两条派生路线:

Smiley     ->         Circle     ->  Shape
  ^                     ^               ^
  |                     |               |
Impl::Smiley -> Impl::Circle -> Impl::Shape
 

参考:https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md
 

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

C++程序员Carea

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值