0

我开始编写更大的对象,其中包含其他对象。有时,我需要能够从包含它的对象的类外部调用子对象的方法,例如从 main() 函数。

到目前为止,我正在使用我所学到的 getter 和 setter。这将给出类似于以下代码的内容:

class Object {
public:
    bool Object::SetSubMode(int mode);
    int Object::GetSubMode();
private:
    SubObject subObject;
};

class SubObject {
public:
    bool SubObject::SetMode(int mode);
    int SubObject::GetMode();
private:
    int m_mode(0);
};

bool Object::SetSubMode(int mode) { return subObject.SetMode(mode); }
int Object::GetSubMode() { return subObject.GetMode(); }

bool SubObject::SetMode(int mode) { m_mode = mode; return true; }
int SubObject::GetMode() { return m_mode; }

这感觉非常不理想,迫使我为需要从外部访问的每个方法编写(丑陋的)代码。我希望能够做一些简单的事情Object->SubObject->Method(param);

我想到了一个简单的解决方案:将子对象作为公共对象放在我的对象中。这样我应该能够简单地从外部访问它的方法。问题是,当我学习面向对象编程时,有人告诉我,除了方法之外,公开任何东西都是亵渎神灵,我不想开始养成不良的编码习惯。我在研究过程中遇到的另一个解决方案是添加一个指向子对象的公共指针?

如何以简洁的方式访问子对象的方法?

将对象作为公共对象放入类中以访问其方法是否允许/一种好习惯?否则怎么办?

非常感谢您在这方面的帮助。

4

2 回答 2

0

指针和公共成员对象的问题在于您刚刚删除了信息隐藏。您的代码现在更加脆弱,因为它都“知道”您已经实现了具有 4 个对象 Wheel 成员的对象 Car。而不是调用隐藏细节的 Car 函数,如下所示:

Car->SetRPM(200);  // hiding

您想像这样直接开始旋转车轮:

Car.wheel_1.SetRPM(200);  // not hiding! and brittle!
Car.wheel_2.SetRPM(200);

如果你改变类的内部结构呢?以上内容现在可能已损坏,需要更改为:

Car.wheel[0].SetRPM(200);  // not hiding!
Car.wheel[1].SetRPM(200);

此外,对于您的 Car,您可以说 SetRPM(),然后该类会判断它是前轮驱动、后轮驱动还是全轮驱动。如果您直接与轮子成员交谈,则不再隐藏实施细节。

有时您确实需要直接访问类的成员,但创建类的一个目标是封装和隐藏调用者的实现细节。

请注意,您可以使用 Set 和 Get 操作来更新类中的多个成员数据,但理想情况下,这些操作对 Car 本身有意义,而不是特定的成员对象。

于 2017-06-26T20:46:18.113 回答
0

有人告诉我,除了方法之外,公开任何东西都是亵渎神明

像这样的笼统陈述是危险的;您必须考虑每种风格的优点和缺点,但彻底禁止公众成员在 IMO 中是个坏主意。

拥有公共成员的主要问题是它暴露了可能更好地隐藏的实现细节。例如,假设您正在编写一些库:

struct A {
    struct B {
        void foo() {...}
    };
    B b;
};

A a;
a.b.foo();

现在几年后,您决定要A根据上下文改变行为;也许你想让它在测试环境中以不同的方式运行,也许你想从不同的数据源加载,等等。哎呀,也许你只是决定成员的名称b描述性不够。但是因为b是公开的,你不能在A不破坏客户端代码的情况下改变行为。

struct A {
    struct B {
        void foo() {...}
    };
    struct C {
        void foo() {...}
    };
    B b;
    C c;
};

A a;
a.c.foo(); // Uh oh, everywhere that uses b needs to change!

现在,如果您要A包装实现:

class A {
public:
    foo() { 
        if (TESTING) {
            b.foo();
        } else {
            c.foo();
        }
private:
    struct B {
        void foo() {...}
    };
    struct C {
        void foo() {...}
    };
    B b;
    C c;
};

A a;
a.foo(); // I don't care how foo is implemented, it just works

(这不是一个完美的例子,但你明白了。)

当然,这里的缺点是它需要很多额外的样板,就像你已经注意到的那样。所以基本上,问题是“你是否期望实现细节在未来发生变化,如果是这样,现在添加样板会花费更多,还是以后重构每个调用?” 而如果你正在编写一个供外部用户使用的库,那么“重构每个调用”就会变成“破坏所有客户端代码并强制它们重构”,这会让很多人非常不爽。

当然SubObject,您可以为 中的每个函数添加一个 getter,而不是为每个函数编写转发函数subObject

const SubObject& getSubObject() { return subObject; }

// ...

object.getSubObject().setMode(0);

SubObject尽管它更容易解决,因为接口不一定与实现相关联,但它会遇到一些与上述相同的问题。

尽管如此,我认为在某些时候公众成员肯定是正确的选择。例如,简单的结构,其主要目的是充当另一个函数的输入,或者只是从 A 点到 B 点获取一束数据。有时所有这些样板文件真的是矫枉过正。

于 2017-06-26T21:00:12.437 回答