处理类中的函数,为了清楚起见,应该将其分解为函数?

时间:2011-11-02 16:21:22

标签: c++ oop

这种情况通常如何处理。例如,一个对象可能需要做非常具体的事情:

class Human
{
   public:
   void eat(Food food);
   void drink(Liquid liquid);
   String talkTo(Human human);
}

假设这是这个类应该做的,但实际上这些可能会产生超过10,000行的函数。所以你会打破他们。问题是,许多辅助函数不应该被它们所服务的函数调用。这使得代码在某种程度上令人困惑。例如,咀嚼(食物食品);将由eat()调用,但不应该被类的用户调用,并且可能不应该在其他任何地方调用。

这些案件如何处理?我正在看一些看起来像这样的真实视频游戏的课程:

class CHeli (7 variables, 19 functions)
Variables list

    CatalinaHasBeenShotDown
    CatalinaHeliOn
    NumScriptHelis
    NumRandomHelis
    TestForNewRandomHelisTimer
    ScriptHeliOn
    pHelis

Functions list

    FindPointerToCatalinasHeli (void)
    GenerateHeli (b)
    CatalinaTakeOff (void)
    ActivateHeli (b)
    MakeCatalinaHeliFlyAway (void)
    HasCatalinaBeenShotDown (void)
    InitHelis (void)
    UpdateHelis (void)
    TestRocketCollision (P7CVector)
    TestBulletCollision (P7CVectorP7CVectorP7CVector)
    SpecialHeliPreRender (void)
    SpawnFlyingComponent (i)
    StartCatalinaFlyBy (void)
    RemoveCatalinaHeli (void)
    Render (void)
    SetModelIndex (Ui)
    PreRenderAlways (void)
    ProcessControl (void)
    PreRender (void)

所有这些看起来都是相当高级的功能,这意味着它们的源代码必须非常冗长。这样做的好处在于,一眼就可以清楚地了解这个类可以做什么,并且该类看起来很容易使用。但是,这些函数的代码可能非常大。

程序员在这些情况下应该做些什么;什么是适合这些情况的做法。

4 个答案:

答案 0 :(得分:11)

  

例如,咀嚼(食物食品);将由eat()调用,但不应该被类的用户调用,并且可能不应该在其他任何地方调用。

然后使chew成为privateprotected成员函数,或者在eat实现模块中的匿名命名空间中使用独立函数:

// eat.cc

// details of digestion
namespace {
    void chew(Human &subject, Food &food)
    {
        while (!food.mushy())
            subject.move_jaws();
    }
}

void Human::eat(Food &food)
{
    chew(*this, food);
    swallow(*this, food);
}

private成员函数相比,此方法的好处是可以在不更改标头(需要重新编译客户端代码)的情况下更改eat的实现。缺点是函数不能被其模块之外的任何函数调用,因此它不能被多个成员函数共享,除非它们共享一个实现文件,并且它不能访问private部分的函数。直接上课。

protected成员函数相比的缺点是派生类不能直接调用chew

答案 1 :(得分:1)

允许以任何您想要的方式拆分一个成员函数的实现。

一个流行的选择是使用私有成员函数:

struct Human
{
    void eat();

private:
    void chew(...);
    void eat_spinach();
    ...
};

或使用Pimpl习语:

struct Human
{
    void eat();
private:
    struct impl;
    std::unique_ptr<impl> p_impl;
};

struct Human::impl { ... };

然而,一旦eat的复杂性上升,你肯定不希望私有方法的集合累积(无论是在Pimpl类内还是在私有区内)。

所以你想打破这种行为。您可以使用类:

struct SpinachEater
{
    void eat_spinach();
private:
    // Helpers for eating spinach
};

...

void Human::eat(Aliment* e)
{
    if (e->isSpinach()) // Use your favorite dispatch method here
                        // Factories, or some sort of polymorphism
                        // are possible ideas.
    {
        SpinachEater eater;
        eater.eat_spinach();
    }

    ...
}

基本原则:

  • 保持简单
  • 一级责任
  • 永远不要重复代码

编辑:稍微好一点的插图,显示可能分为几类:

struct Aliment;

struct Human
{
    void eat(Aliment* e);

private:
    void process(Aliment* e); 
    void chew();
    void swallow();
    void throw_up();
};

// Everything below is in an implementation file
// As the code grows, it can of course be split into several
// implementation files.
struct AlimentProcessor
{
    virtual ~AlimentProcessor() {}
    virtual process() {}
};

struct VegetableProcessor : AlimentProcessor
{
private:
    virtual process() { std::cout << "Eeek\n"; }
};

struct MeatProcessor
{
private:
    virtual process() { std::cout << "Hmmm\n"; }
};

// Use your favorite dispatch method here.
// There are many ways to escape the use of dynamic_cast,
// especially if the number of aliments is expected to grow.
std::unique_ptr<AlimentProcessor> Factory(Aliment* e)
{
    typedef std::unique_ptr<AlimentProcessor> Handle;

    if (dynamic_cast<Vegetable*>(e)) 
        return Handle(new VegetableProcessor);
    else if (dynamic_cast<Meat*>(e))
        return Handle(new MeatProcessor); 
    else
        return Handle(new AlimentProcessor);
};

void Human::eat(Aliment* e)
{
    this->process(e);
    this->chew();

    if (e->isGood()) this->swallow();
    else this->throw_up();
}

void Human::process(Aliment* e)
{
    Factory(e)->process();
}

答案 2 :(得分:0)

一种可能性是(或许private)组成Human个较小的对象,每个对象都做一小部分工作。所以,你可能有一个Stomach对象。 Human::eat(Food food)会委托this->stomach.digest(food),返回DigestedFood对象,Human::eat(Food food)函数会进一步处理。

答案 3 :(得分:0)

函数分解是从经验中学习的东西,它通常意味着同时进行类型分解。如果你的功能变得太大,可以做不同的事情,这对于特定情况最好,取决于手头的问题。

  • 将功能分离为私人功能

当函数必须从对象访问相当多的状态,并且它们可以用作一个或多个公共函数的构建块时,这更有意义

  • 将类分解为具有不同职责的不同子类

在某些情况下,部分工作自然会落入其自身的小子问题中,然后可以通过调用内部子对象(通常是该类型的成员)来实现更高级别的功能。

因为您尝试建模的域名可以通过多种不同的方式进行解释,我担心会尝试提供合理的细分,但您可以想象mouth子对象在Human中你可以用来ingest食物或饮料。在mouth子对象中,您可以使用openchewswallow ...

功能