C++ 接口中包含Init方法是不是一个好实践?

背景

看到类似如下项目代码:

struct IA {
	virtual ~IA() {}
	virtual bool Init() = 0;
	...
};

class A: public IA {
public:
	A(const std::string& path);
	bool Init() override {
		FILE* fp = fopen(path_.c_str());
		...
	}
private:
	std::string path_;
};

int main() {
	A a("/etc/a.config");
	if (a.Init() == false) {
		return -1;
	}
	return 0;
}

有单独的Init方法,并且Init可能失败,开发者遵循了不在构造函数中抛出异常的原则。但是看着代码,让人想思考以前没有考虑明白或者考虑明白了又忘记的问题,比如下面的。

是否应该在Init方法中提供参数而非在构造函数中提供?

上面例子中,为了能够在Init时读取配置文件,需要使用一个构造时传入的path字符串,并保存为path_成员,对于此成员,除了在Init中使用,别的地方不再使用。path_成员仅为Init而存在,如果A的对象很多,这里可能会占用不少的内存。为什么不把path参数放到Init方法参数列表中,减少一个path_成员呢?于是我们得到:

struct IA {
	virtual bool Init(const std::string& path) 0;
	...
};
class A: public IA {
public:
	bool Init(const std::string& path) override {
		FILE* fp = fopen(path.c_str());
		...
	}
};

int main() {
	A a;
	if (a.Init("/etc/a.config") == false) {
		return -1;
	}
	return 0;
}

除此之外,在Init中提供参数还有一个好处,即,有时我们不得不面临两个类互相依赖的情况,假如在构造函数时提供参数:

class A {
public:
	A(B* b);
...
}
class B {
public:
	B(A* a);
...
};

此时我们无法构造A和B的实例。改为使用Init,则可以实现:

class A {
public:
	bool Init(B* b);
...
}
class B {
public:
	bool Init(A* a);
...
};
int main() {
	A a;
	B b;
	a.Init(&b);
	b.Init(&a);
	return 0;
}

因此,Init中提供参数,有两个好处:a) 节省内存,b) 有时能以尚算优雅的方式解决环形引用的问题。

是否应该在接口中包含Init方法?

从前面问题的讨论,我们考虑提出在Init中提供参数的编程规范,但随即发现,不同的接口实现者,Init时需要的参数不同,这就类似于不同实现类有不同的构造条件,难以抽象出相同Init接口方法。不仅如此,在使用Init方法时,往往紧随构造之后,而非在定义接口的框架中使用。既然如此,这个Init实际上是实现相关的,他是构造函数的延续,不应该在接口中定义Init方法。于是,代码变为:

struct IA {
	...
};
class A: public IA {
public:
	bool Init(const std::string& path) {
		FILE* fp = fopen(path.c_str());
		...
	}
};

int main() {
	A a;
	if (a.Init("/etc/a.config") == false) {
		return -1;
	}
	return 0;
}

Init方法是否有存在必要?

现在规范变为,接口中不要包含Init方法,再另见我的前面一篇文章,得出结论不管有没有接口,不要使用Init方法。代码演变为:

struct IA {
	...
};
class A: public IA {
public:
	A(const std::string& path, std::error_code& errc) {
		FILE* fp = fopen(path.c_str());
		if (fp == nullptr) {
			errc = std::errc::io_error;
			return;
		}
		...
	}
};

int main() {
	std::error_code errc;
	A a("/etc/a.config", errc);
	if (errc) {
		return -1;
	}
	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值