【问题标题】:C++: Composition InterfaceC++:组合接口
【发布时间】:2010-01-04 19:48:23
【问题描述】:

所以我花了一些时间思考这个问题,并在谷歌上到处寻找“解决方案”(解决方案很可能是一个偏好问题,但我对此不确定)。下面是我遇到的问题,但是这个问题可以应用于很多关于构图的情况。

我有一个类 Colour,其中包含 red、green、blue 和 alpha 的成员。它们中的每一个都有一个 set 和 get 成员函数。很简单。

class Colour
{
    public:
        float getRed();
        void setRed(float);
        float getGreen();
        void setGreen(float);
        ...
    private:
        float red;
        float green;
        ...
};

我将在其他类中使用这个类来指定它们的颜色,例如(为了简洁省略构造函数和析构函数):

class ColourableObject
{
    private:
        Colour colour;
};

现在我的问题是:这个 ColourableObject 类如何最好地访问这个 Color 对象?最好检索 Color 对象以直接访问其成员函数,如下所示:

class ColourableObject
{
    public:
        Colour& getColour();
    private:
        Colour colour;
};

或者,最好给 ColourableObject 类提供自己的集合并获取颜色函数,其中作用于 Color 对象,如下所示:

class ColourableObject
{
    public:
        float getRed();
        void setRed(float);
        ...
    private:
        Colour colour;
};

对我来说,前者是最合乎逻辑的,因为它可以省去很多麻烦,只需直接作用于这个 Color 对象,就可以为每个需要这个 Color 对象的类添加功能。

但是,后者不是更容易受到 ColourableObject 类更改的影响吗?更不用说 colourableObject.getColour().setRed(x) 对我来说似乎不太自然,因为 get 和 set 相互冲突。

我可能完全有错误的方法。我对C++比较陌生,所以我愿意学习!那么问题来了,我应该使用前一种方法,后一种方法,还是完全不同的方法?

【问题讨论】:

  • 你有没有想过,如果你为每个数据成员提供get/set方法,效果就是没有封装。您不妨一开始就将数据成员公开。
  • @jalf 除非他完全删除 Color 对象,并用其他东西替换它。也许他开始将所有内容存储在 CMYK 中,并在 setRed 中进行计算以生成适当的值。

标签: c++ class interface composition


【解决方案1】:

不直接回答你的问题,但我会摆脱所有的设置器(通常在任何情况下都有设置器是一个不好的迹象),并使用构造函数:

Color c = Color( 123, 17, 77 );

其中三个构造函数参数是 RGB 值。

【讨论】:

  • 我当然会使用构造函数。为了简洁起见,我省略了它们,尽管为了清楚起见我应该添加它们,我很抱歉。当我需要在 Color 对象的生命周期内检索或更改 RGBA 值时,就会出现问题,这就是问题所在。很抱歉给您带来不便,并感谢您的回复。
  • 我的意思是你应该一次更改所有三个 RGB 值,最简单的方法是使用构造函数创建一个新的 Color 值。
  • 我明白了。好吧,当我在下面评论 Shmoopty 的回答时,我考虑了这一点。当我只需要更改一个值时,我想尝试避免更改所有值。我很可能会使用这个解决方案。非常感谢大家的回复,非常感谢。
  • @Charlie 感谢您的礼貌,但在 StackOverflow 上,我们一般不会对每个评论或回答说“谢谢”。只需为您认为有用的答案投票,并记住接受您认为最有用的答案。
【解决方案2】:

如果您可能需要添加其他颜色,例如橙色、紫色、青色或 Crayola 的新 Smooky Applewood,我可能会重新创建您的课程!

有点像基础颜色类,并通过引入新颜色来作用于该基础类。 这样,您处理的颜色无关紧要。这都是一个黑匣子。这也将回答您的第二个问题,因为您不必重新定义您的 setColor 和 getColor 名称方法。他们不需要知道或关心您指的是什么颜色。

我认为 Code Complete(这本书)有一个章节是关于查看具有太多 get/set 方法的类的部分。它通常归结为错误的做事方式

【讨论】:

    【解决方案3】:

    在这种情况下,您对 getter 和 setter 的使用没有多大意义。您也可以将Colourfloat 成员公开。除非您需要对多个私有成员进行验证或操作,否则请继续公开这些成员,并创建一个构造函数来适当地初始化 Colour

    至于ColourableObject,您应该问的问题是:其他不相关的类是否需要访问对象的Colour 成员?他们需要对其进行更改吗?如果其中任何一个的答案是“否”,我会说您根本不应该在该对象上使用任何类型的 getter 或 setter。否则,同样,除非您需要进行验证或其他状态更改,否则只需将 Colour 设为公开即可。

    【讨论】:

    • 其他类可能需要访问 ColourableObject,例如,如果 ColourableObject 是 ColourableObjectStorage 的子类,它具有可能希望更改其子类颜色的函数。如前所述,此问题与此没有直接关系。这是与任何与此类似的组合相关的问题。我已阅读此页面上的所有答案,并且对如何处理这些问题有一个很好的了解。感谢您的回复。
    【解决方案4】:

    DRY principle 支持您的第一个选项,提供对Colour 对象的访问。

    另外,你可能希望改变

        Colour& getColour();
    

        const Colour& getColour();
        void setColour( const Colour& );
    

    ...因为这将确保您的 ColourableObject 始终知道其颜色何时发生变化。

    【讨论】:

    • 我实际上正在考虑这个。但是,我确实对此不以为然,因为创建一个全新的对象来更改单个值似乎浪费了处理能力。比如我想改变红色的值,我需要重新构造所有的RGBA值。我忘了在问题中写这个考虑,我可以考虑一下,谢谢你的回复。
    • @Charlie Wilkinson:无论您采用哪种方法,都尽量不要陷入降低处理能力的困境。任何程序都可以逐步优化,直到时间结束。只有在项目结束时才知道是否需要首先进行任何优化。
    • @Charlie Wilkinson:Shmoopty 有一个观点。如果您想要最大速度,请将 Color 设置为仅存储 32 位值(用于 R、G、B、a)的不可变结构。然后复制整个实例将与设置任何一个属性一样便宜。
    【解决方案5】:

    最终,答案取决于您希望(或不希望)如何限制对嵌入 Color 对象的访问。

    在您的第一选择中...

    class ColourableObject
    {
    public:
        Colour& getColour();
    private:
        Colour colour;
    };
    

    ...您实际上根本没有限制访问。如果用户可以通过getColour() 访问私人会员,那么将其设为私人有什么意义?您不妨跳过中间步骤,直接执行以下操作:

    class ColourableObject
    {
    public:
        Colour colour;
    };
    

    这样做时,人们可以直接引用数据成员及其所有函数,例如C.colour.getRed();(假设 C 是 ColourableObject)

    现在,假设您想以某种方式限制最终用户 - 他不能设置绿色或蓝色,但您可以允许他设置红色。在这种情况下,您可能需要使用第二个选择:

    class ColourableObject
    {
    public:
        float getRed();
        void setRed(float);
        ...
    private:
        Colour colour;
    };
    

    这是因为ColourableObject 的用户只能访问您的公共函数,而不能访问底层的私有成员。如果您希望在最终用户和颜色选择之间有一些中间步骤,这个原理也适用。例如:

    class ColourableObject
    {
    public:
        void setMood(enum Mood);
    private:
        Colour colour;
    };
    
    void ColourableObject::setMood(enum Mood)
    {
       if(Mood == HAPPY) colour.setRed(3);
       if(Mood == SAD)  colour.setBlue(11);
       ...
    }
    

    最后,您可以做一个混合,保持 colour 数据成员公开,但也为该数据成员添加额外的访问器。

    总结:

    • 第一种方法:

      • Pro 的:Color 接口通过。
      • 缺点:公开数据成员。
    • 第二种方法:

      • 专业版:封装数据。未来对 Colour 界面的更改只会破坏您的代码,而不是最终用户的代码。
      • 缺点:您必须重新实现您希望最终用户能够使用的 Colour 界面的任何部分。

    【讨论】:

      【解决方案6】:

      就 Colorable 的成员而言,我个人更喜欢删除“get”部分:

      colorableObject.Color().setRed(1.0f);

      正如你所说,这完全是个人喜好(拼写也是如此!:) 但这样,对我来说,它看起来像一个“属性”,而不是一个 getter/setter 方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-06-01
        • 1970-01-01
        • 2016-01-05
        • 2016-01-05
        • 2015-07-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多