转发函数调用成员的不良做法?

时间:2016-09-25 17:52:25

标签: c++ oop

在我最近编写的代码中,我被迫直接访问一个对象的成员来调用它的函数,但是,这样做感觉不对,因为它似乎违反了封装和Demeter法则。然而,我能想出的唯一好的选择是在类中为我可能想要调用的那个成员的每个函数编写我自己的函数,这将是非常乏味和冗余的。例如:

class Object
{
    public:
        void setNum(int x)
        {
            num = x;
        }

    private:
        int num;
};

class Object2
{
    public:
        Object obj;
};

int main()
{
    Object2 obj2;
    obj2.obj.setNum(5);
}

VS

class Object
{
    public:
        void setNum(int x)
        {
            num = x;
        }

    private:
        int num;
};

class Object2
{
    public:
        void setNum(int x)
        {
            obj.setNum(x);
        }

    private:
        Object obj;
};

int main()
{
    Object2 obj2;
    obj2.setNum(5);
}

setNum中对Object2的来电被转发到Object中的同一个功能。这样的设计被认为是不好的做法吗?是否更好地访问obj

我也可以从Object2继承Object,但在这种情况下,我将继承的类不是设计为基类,而是将受保护的成员公开给Object2 ,并且似乎不适合开始,因为它不是一种关系,而且组合将是首选。

我的具体情况:我正在使用SFML制作游戏,有一个类Ship当然需要一个精灵来代表它在世界上。任何时候我想设置或获得船舶的位置,旋转等。我必须直接访问其精灵或在Ship中写入冗余转发功能。问题是,做这些事情中的任何一个看起来像代码气味:要么违反封装和Demeter法则,要么写冗余代码。

这里最佳做法是什么?我是否过于挑剔编写简单的转发功能?或者在这种情况下直接访问Ship的精灵真的没有错吗?

这个问题:C++ Forward method calls to embed object without inheritance实际上正是我所要求的,然而,这两个答案都没有给出一个好的解决方案。一个没有被宽恕,显然有很差的封装,另一个只是使用一个似乎是安慰剂的吸气剂,如果有的话,没有人解决我的主要问题,这是什么是最优雅和可接受的解决方案?

2 个答案:

答案 0 :(得分:2)

最好的解决方案在很大程度上取决于封装的基本语义。您需要尽可能地解耦代码。我将在例子中描述:

  1. 你有一个Ship类,它有一个Sprite。您可能希望将游戏逻辑和渲染分开。因此Ships知道渲染的所有内容都是它有一个处理它的Sprite对象。所以在这种情况下,你要分担责任,这很好。如此简单的getter是一个很好的解决方案。
  2. 但是如果绝对坐标和旋转必须存储在精灵中,那么事情会发生变化:游戏逻辑通常需要两个,所以它们必须在Ship和Sprite中一致地设置。实现这一目标的最佳方法是使Ship的setPosition和setRotation方法也设置Sprite位置和旋转。这样,您可以简化与Ship一起使用的代码,而不考虑Ship复杂性。鉴于Ship是从几个地方操纵的,这是值得的。注意:您仍然可能希望通过getter公开Sprite以进行渲染。并且您可能希望阻止除Ship之外的任何人设置Sprite位置和旋转,如果这不会使您的代码膨胀太多。
  3. 让我们假设Ship类主要用于渲染。在这种情况下,您可能希望隐藏使用sprite用于图形的外部类(因为如果您更改渲染引擎,如果您不需要重写除渲染代码之外的任何内容将会很好)。在这种情况下,你会想要通过Ship代理所有setPosition和setRotation调用Sprite来隐藏Sprite的存在。
  4. 在这种情况下,没有使用继承,因为继承意味着孩子是其祖先的变体。你可以说BattleShip是一艘船的变种。但是Ship并不是Sprite的变种,它们太不同了,意味着不同的东西。

    所以:如果封装类太具体而且不应该在外部可见,或者必须与主对象一致地操作,那么编写一堆代理方法是一个很好的解决方案。否则这些方法只会膨胀你的代码,最好提供一种获取嵌套对象的方法。但在这种情况下,我投票选择了getter方法而不是公共财产。

答案 1 :(得分:1)

尽管经典的 javanese oop学校可以思考,但永远不会忘记DRY(不要重复自己)也被认为是一种很好的做法。

虽然OOP只是C ++可以支持的众多编程范例之一,但是DRY是所有编程的精髓所在,因为第一个汇编程序得到了宏,并且在oop发明者甚至远离他们自己的父母之前很久就是这样。 39;思想和愿望。

对于我的不公平体验的所有内容......如果尊重良好的oop练习迫使你写出重复代码的无用锅炉板,那就意味着

  • 您正在使用的语言基本上是已损坏以达到您想要达到的目的,而不是正确支持范例或...
  • OOP 已损坏,目的是为了达到目的。而在C ++中,OOP真的是破碎的范例。

要解决您的具体问题,委托(这种模式通常如何命名)在以下情况下有意义:

  • 可以更改委派方式或
  • 委托是隐藏部分成员接口。

在您的情况下,您有一个函数调用可从固定成员访问的固定方法。

这仅针对此特定样本或在您的实现中是否总是这样设计?

如果是这样,如果不仅仅将a.b.m()缩减为a.m(),则委托不会添加任何语义值。如果你写的不仅仅是......让我们说 3"什么都不做只是前进"功能你在浪费你的时间。

如果b有50个方法而且你将它设为私有,只委托其中的5个方法,那么它就非常有意义了。