乌托邦式接口和实现分离技术

《Imperfect C++》中展示了一种叫“螺栓”的技术,然而,这本书中的讨论并不足够深入。当然,我也相信Matthew是故意的,从而让我们这些“三道贩子”(Matthew自称是二道贩子)也能够获得一点点成就感。

考虑这样一个接口设计:

struct IRefCount;
struct IReader : public IRefCount;


在Reader中实现接口:

<!--[if !supportEmptyParas]--> class Reader : public IReader;

在上述的继承结构中,IRefCount是一个结构性的类,用来实现引用计数,实际上它和领域逻辑部分IReader没有什么关系。我们打算在IRefCount的基础上,建立了一套工具来管理对象生命周期和帮助实现异常安全的代码 (例如,smart pointer) 。现在来考虑Reader的实现,Reader除了需要实现IReader的接口,还必须实现IRefCount的接口。这一切看起来似乎顺理成章,让我们继续看下面的设计<!--[if !supportEmptyParas]-->:

struct IWriter : public IRefCount;
<!--[if !supportEmptyParas]--> class Writer : public IWriter;


现在来考虑Writer的实现,和Reader一样,Writer除了要实现IWriter的接口外,同时还需要实现IRefCount的接口。现在,我们来看看IRefCount是如何定义的:

struct IRefCount {
virtual void add() = 0;
virtual void release() = 0;
virtual int count() const = 0;
virtual void dispose() = 0;
virtual ~IRefCount(){}
};


在Reader中的IRefCount的实现:

virtual void add() { ++m_ref_count;}
virtual void release() {--m_ref_count;}
virtual int count() const{return m_ref_count;}
virtual void dispose() { delete this;}

int m_ref_count;


同样,在Writer的实现中,也包含了一模一样的代码,这违背了DRY原则(Don’t Repeat Yourself)。况且,随着系统中的类增加,大家都意识到,需要将这部分代码复用。一个能够工作的做法是把IRefCount的实现代码直接放到IRefCount中去实现,通过继承,派生类就不必再次实现IRefCount了。我们来看一下dispose的实现:

virtual void dispose() { delete this;}

这里,采用了delete来销毁对象,这就意味着Reader必须在堆上分配,才可能透过IRefCount正确管理对象的生命周期,没关系,我们还可以override dispose方法,在Reader如下实现dispose:

virtual void dispose() { }

但是,这样又带来一个问题,Reader不能被分配在堆上了!如果你够狠,当然,你也可以这么解决问题:

class HeapReader : IReader;
class StackReader : HeapReader{ virtual void dispose() { } };


问题是,StackReader 是一个HeapReader吗?为了代码复用,我们完全不管什么概念了。当然,如果你和我一样,看重维护概念,那么这么实现吧:

class HeapReader : IReader;
class StackReader : IReader;


这样一来,IReader的实现将被重复,又违背了DRY原则,等着被将来维护的工程师诅咒吧!或许,那个维护工程师就是3个月后的你自己。如果这样真的能够解决问题,那么也还是可以接受的,很快,我们有了一个新的接口:

struct IRWiter : IReader, IWriter;
class RWiter : public IRWiter;


考虑一下IRefCount的语义:它用来记录对所在对象的引用计数。很显然,我从IReader和IWriter中的任意一个分支获得的IRefCount应该都是获得一样的引用计数效果。但是现在,这个继承树存在两个IRefCount的实例,我们不得不在RWiter当中重新重载一遍。这样,从IReader和IWriter继承来的两个实例就作废了,而且,我们可能还浪费了8个字节。为了解决这个问题,我们还可以在另一条危险的道路上继续前进,那就是虚拟继承:

struct IReader : virtual public IRefCount;
struct IWriter : virtual public IRefCount;


还记得大师们给予的忠告吗--“不要在虚基类中存放数据成员”。“这样有什么问题吗,我们不必对大师盲目崇拜”,你一定也听过这样的建议。如果大师们不能说服这些人,那么我也不能。于是,我们进一步在所有的接口中提供默认实现,包括IReader和IWriter.

时间: 2024-08-31 22:22:20

乌托邦式接口和实现分离技术的相关文章

C++中接口与实现分离的技术

在用C++写要导出类的库时,我们经常只想暴露接口,而隐藏类的实现细节.也就是说我们提供的头文件里只提供要暴露的公共成员函数的声明,类的其他所有信息都不会在这个头文件里面显示出来.这个时候就要用到接口与实现分离的技术. 下面用一个最简单的例子来说明. 类ClxExp是我们要导出的类,其中有一个私有成员变量是ClxTest类的对象,各个文件内容如下: lxTest.h文件内容: class ClxTest{ public: ClxTest(); virtual ~ClxTest(); void Do

用乌托邦式梦想打造实名制校内网

坚持推行实名制,听起来更象是一个乌托邦式的梦想,但坚持以自己的名义说话.做事,剥除"戴着面具在黑暗里"的虚拟状态,这一直是张帆的理想.   走出电梯,映入眼帘的是醒目分明的"占座"二字,红黑色调的简约装修,前台墙上的涂鸦以及一个个五颜六色的手掌印无不散发出浓厚的美国街头气息.这里,是网络社区"占座网"的"根据地",而这里的一切都极符合本来就"中西合璧"的亿中邮CEO."占座网"创始人张帆

从海尔获悉,其已经研发出全新的“干湿分离技术”

突破了目前冰箱行业食物保鲜的最高水平.据悉,该技术不仅能通过高保湿技术延长果蔬的保鲜期,而且其独创的干物保鲜技术可以储存冬虫夏草.鹿茸等贵重干物.这是继上月9日发布磁制冷技术后,海尔再次发布超前家电技术.据悉,干湿分离技术主要分两大模块--安心珍品(干物)高保鲜模块和水润果蔬高湿保鲜模块,配备干湿分离技术的冰箱能根据食材特质,提供相应的最佳储存湿度环境,有效解决冰箱保鲜不同食材干湿难均衡的问题.以贵重干货为例,鹿茸.冬虫夏草.名贵茶叶不再是小众消费品,储存成为一大难题,尤其在南方闷热潮湿的环境,

海尔首创干湿分离技术食品保鲜期翻倍延长

10月9日,记者从海尔获悉,其已经研发出全新的"干湿分离技术",突破了目前冰箱行业食物保鲜的最高水平.据悉,该技术不仅能通过高保湿技术延长果蔬的保鲜期,而且其独创的干物保鲜技术可以储存冬虫夏草.鹿茸等贵重干物.这是继上月9日发布磁制冷技术后,海尔再次发布超前家电技术.据悉,干湿分离技术主要分两大模块--安心珍品(干物)高保鲜模块和水润果蔬高湿保鲜模块,配备干湿分离技术的冰箱能根据食材特质,提供相应的最佳储存湿度环境,有效解决冰箱保鲜不同食材干湿难均衡的问题.以贵重干货为例,鹿茸.冬虫夏

C++:显示接口&运行期多态 和 隐式接口&编译期多态

类(class)和面向对象: 显示接口(explicit interface): 即在源代码中可见, 可以在头文件内看到类的所有接口; 运行期多态(runtime polymorphism):成员函数是virtual, 传入类的引用或指针时, 在运行时, 会自动匹配接口, 可能是基类的接口, 也可能是派生类的; 模板(templates)和泛型编程(generic programming): 隐式接口(implicit interface):typename T, 在函数中, 所必须支持一组操作

C++箴言:理解隐式接口和编译期多态

object-oriented programming(面向对象编程)的世界是围绕着 explicit interfaces(显式接口)和 runtime polymorphism(执行期多态)为中心的.例如,给出下面这个(没有什么意义的)的 class(类). class Widget { public: Widget(); virtual ~Widget(); virtual std::size_t size() const; virtual void normalize(); void s

MySQL读写分离技术

阅读目录 1.简介 2.基本环境 3.配置主从复制 4.MySQL读写分离配置 4.1.安装lua 4.2.安装mysql-proxy 5.MySQL读写分离测试 1).修改rw-splitting.lua文件 2).修改完成后,启动mysql-proxy 3).创建用于读写分离的数据库连接用户 4).测试登陆账号proxy1@192.168.95.13进行添加数据 5).关闭12mysql的从复制 6).证明写分离 7).证明读分离 6.建议 回到顶部 1.简介 当今MySQL使用相当广泛,随

java api-API接口通常采用哪些技术?

问题描述 API接口通常采用哪些技术? 在APP的开发过程中,API通常采用哪些技术?哪种技术最为稳定流畅? 解决方案 web API,由web服务器提供功能.

Java 8新特性 内建函数式接口详解_java

Java 8新特性内建函数式接口 在之前的一片博文 Lambda 表达式,提到过Java 8提供的函数式接口.在此文中,将介绍一下Java 8四个最基本的函数式接口 对于方法的引用,严格来讲都需要定义一个接口.不管我们如何操作实际上有可能操作的接口只有四种. Java 8 提供了函数式接口包java.util.function.*,在该包下有许多Java 8内建的函数式接口.不过基本上分为四种基本的: 功能型接口 (Function) 将 T 作为输入,返回 R 作为输出,他还包含了和其他函数组