我在一家制造工厂工作,该工厂使用大型C ++项目来实现制造过程的自动化。
我看到一个地方的某种做法,似乎只是让代码变得不必要地长,我想知道是否有使用这种做法的具体原因。
请参阅下文,了解演示此做法的简化示例。
第一档:
class A {
private:
int a;
public:
int get_a()
{ return a; }
void set_a(int arg)
{ a = arg; }
};
第二档:
class B {
private:
int b;
public:
int get_b()
{ return b; }
void set_b(int arg)
{ b = arg; }
};
第三档:
class C {
private:
A obj1;
B obj2;
public:
int get_a()
{ return obj1.get_a(); }
int get_b()
{ return obj2.get_b(); }
void set_a(int arg)
{ obj1.set_a(arg); }
void set_b(int arg)
{ obj2.set_b(arg); }
};
在我看来,设计理念的微小变化可能会大大减少第三个文件中的代码量。像这样:
class C {
public:
A obj1;
B obj2;
};
obj1
班级中obj2
和public
成员C
似乎并不安全,因为A
和B
每个类都安全地处理它们自己的成员变量的获取和设置。
我能想到这样做的唯一缺点是调用函数的C
类的任何实例都需要执行类似obj.obj1.get_a()
而不仅仅是obj.get_a()
的操作但这似乎比在A
类中拥有私有B
和C
对象实例,然后手动需要“中继”其所有成员函数更不方便。
我意识到这个简单的例子,它没有多少额外的代码,但是对于我公司使用的这个大型项目,它增加了数万代码行。
我错过了什么吗?
答案 0 :(得分:1)
可能有很多原因。一个是以下内容:
想象一下,你编写了一个与成员a
做某事的函数。您希望相同的代码接受A
以及C
。你的功能可能如下所示:
template <typename T>
void foo(T& t) {
std::cout << " a = " << t.get_a();
}
这不适用于您的C
,因为它有不同的界面。
封装有其好处,但我同意你的看法,为了封装,封装通常会导致更多代码,而且通常只会产生更多代码。
通常,不鼓励强制调用代码来编写类似obj.obj1.get_a()
的内容,因为它会显示实现细节。如果您更改了a
的类型,那么您的C
无法控制该更改。另一方面,如果原始代码a
从int
更改为double
,则C
可以决定是否保留int
界面并进行一些转换(如果适用)或改变其界面。
答案 1 :(得分:1)
它确实添加了一些额外的代码,但重要的是你的界面。一个类有责任,它拥有的成员是实现细节。如果您公开内部对象并强制用户获取对象,则对其进行调用&#34;如果您只提供一个为用户完成工作的界面,那么您将调用者与实现相结合。作为一个类比,[借用维基百科]当一个人想要一只狗走路时,一个人不会让狗的腿直接走路;相反,一个命令狗然后命令它自己的腿。
更正式地说,函数的Demeter法则要求对象O的方法m只能调用以下类型对象的方法:
特别是,对象应该避免调用另一个方法返回的成员对象的方法。对于许多使用点作为字段标识符的现代面向对象语言,法律可简单地表示为&#34;仅使用一个点&#34;。也就是说,代码a.b.c.Method()违反了a.b.Method()没有的规律。