正交变量代码重复问题

发布于 2024-07-05 13:55:18 字数 592 浏览 8 评论 0原文

我最近开始重构一些遗留代码,遇到了两个用于绘制坐标网格的函数,问题是这些函数仅在它们处理的正交变量上有所不同,就像这样

void DrawScaleX(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    for(int x = x0; x < x1; x += step)
    {
         MoveToEx(dc, x, y0, NULL);
         LineTo(dc, x, y1);
    }
}
void DrawScaleY(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    for(int y = y0; y < y1; y += step)
    {
         MoveToEx(dc, x0, y, NULL);
         LineTo(dc, x1, y);
    }
}

所以如果我决定添加一些奇特的东西,比如抗锯齿或只需更换绘图铅笔或其他任何东西,我就必须在它们中放入相同的代码,这是代码重复,我们都知道为什么,这很糟糕。

我的问题是如何将这两个函数重写为一个函数以避免这个问题?

I've started refactoring some legacy code recently and came across two functions for drawing a coordinate grid, the problem is that these functions differ only in orthogonal variables they treat, something like that

void DrawScaleX(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    for(int x = x0; x < x1; x += step)
    {
         MoveToEx(dc, x, y0, NULL);
         LineTo(dc, x, y1);
    }
}
void DrawScaleY(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    for(int y = y0; y < y1; y += step)
    {
         MoveToEx(dc, x0, y, NULL);
         LineTo(dc, x1, y);
    }
}

So if I decide to add some fancy stuff, like antialiasing or merely change drawing pencil or whatever I'll have to put the same code in both of them and it's code duplication and it's bad we all know why.

My question is how would you rewrite these two functions into a single one to avoid this problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

阳光下的泡沫是彩色的 2024-07-12 13:55:18

好吧,一个明显的“解决方案”是创建一个函数并添加一个额外的参数(类似枚举类型)。 然后在里面执行 if() 或 switch() ,并执行相应的操作。 因为嘿,函数的功能是不同的,所以你必须在某个地方执行这些不同的操作。

然而,这增加了运行时的复杂性(在运行时检查事物),而在编译时可以更好地检查。

我不明白将来在两个(或更多函数)中添加额外参数有什么问题。 它是这样的:

  1. 向所有函数添加更多参数来
  2. 编译您的代码,它不会在一堆地方编译,因为它不传递新参数。
  3. 通过传递新参数修复所有调用这些函数的地方。
  4. 利润! :)

如果是 C++,当然你可以让函数成为一个模板,而不是添加一个额外的参数,而是添加一个模板参数,然后专门化模板实现来完成不同的事情。 但在我看来,这只是混淆了这一点。 代码变得更难理解,并且使用更多参数扩展它的过程仍然完全相同相同:

  1. 添加额外的参数
  2. 编译代码,它不会在一堆地方编译
  3. 修复所有调用它的地方所以

你什么也没赢得,反而让代码变得更难理解。 在我看来,这不是一个有价值的目标。

Well, an obvious "solution" would be to make a single function and add one extra parameter (of enum-like type). And then do an if() or switch() inside, and perform the appropriate actions. Because hey, the functionality of the functions is different, so you have to do those different actions somewhere.

However, this adds runtime complexity (check things at runtime) in a place that could be just better checked at compile time.

I don't understand what's the problem in adding extra parameters in the future in both (or more functions). It goes like this:

  1. add more parameters to all functions
  2. compile your code, it won't compile in a bunch of places because it does not pass new parameters.
  3. fix all places that call those functions by passing new parameters.
  4. profit! :)

If it's C++, of course you could make the function be a template, and instead adding an extra parameter, you add a template parameter, and then specialize template implementations to do different things. But this is just obfuscating the point, in my opinion. Code becomes harder to understand, and the process of extending it with more parameters is still exactly the same:

  1. add extra parameters
  2. compile code, it won't compile in a bunch of places
  3. fix all places that call that function

So you've won nothing, but made code harder to understand. Not a worthy goal, IMO.

柏拉图鍀咏恒 2024-07-12 13:55:18

我想我会移动:

     MoveToEx(dc, x0, y, NULL);
     LineTo(dc, x1, y);

到他们自己的函数 DrawLine(x0,y0,x0,y0) 中,您可以从每个现有函数中调用它。

那么有没有一个地方可以添加额外的绘图效果呢?

I think I'd move:

     MoveToEx(dc, x0, y, NULL);
     LineTo(dc, x1, y);

into their own function DrawLine(x0,y0,x0,y0), which you can call from each of the existing functions.

Then there's one place to add extra drawing effects?

绝情姑娘 2024-07-12 13:55:18

一些模板...:)

void DrawLine(HDC dc, int x0, int y0, int x0, int x1)
{
    // anti-aliasing stuff
    MoveToEx(dc, x0, y0, NULL);
    LineTo(dc, x1, y1);
}

struct DrawBinderX
{
    DrawBinderX(int y0, int y1) : y0_(y0), y1_(y1) {}

    void operator()(HDC dc, int i)
    {
        DrawLine(dc, i, y0_, i, y1_);
    }

private:
    int y0_;
    int y1_;

};

struct DrawBinderY
{
    DrawBinderX(int x0, int x1) : x0_(x0), x1_(x1) {}

    void operator()(HDC dc, int i)
    {
        DrawLine(dc, x0_, i, x1_, i);
    }

private:
    int x0_;
    int x1_;

};

template< class Drawer >
void DrawScale(Drawer drawer, HDC dc, int from, int to, int step)
{
    for (int i = from; i < to; i += step)
    {
         drawer(dc, i);
    }
}

void DrawScaleX(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    DrawBindexX drawer(y0, y1);
    DrawScale(drawer, dc, x0, x1, step);
}

void DrawScaleY(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    DrawBindexY drawer( x0, x1 );
    DrawScale(drawer, dc, y0, y1, step);
}

A little templates... :)

void DrawLine(HDC dc, int x0, int y0, int x0, int x1)
{
    // anti-aliasing stuff
    MoveToEx(dc, x0, y0, NULL);
    LineTo(dc, x1, y1);
}

struct DrawBinderX
{
    DrawBinderX(int y0, int y1) : y0_(y0), y1_(y1) {}

    void operator()(HDC dc, int i)
    {
        DrawLine(dc, i, y0_, i, y1_);
    }

private:
    int y0_;
    int y1_;

};

struct DrawBinderY
{
    DrawBinderX(int x0, int x1) : x0_(x0), x1_(x1) {}

    void operator()(HDC dc, int i)
    {
        DrawLine(dc, x0_, i, x1_, i);
    }

private:
    int x0_;
    int x1_;

};

template< class Drawer >
void DrawScale(Drawer drawer, HDC dc, int from, int to, int step)
{
    for (int i = from; i < to; i += step)
    {
         drawer(dc, i);
    }
}

void DrawScaleX(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    DrawBindexX drawer(y0, y1);
    DrawScale(drawer, dc, x0, x1, step);
}

void DrawScaleY(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    DrawBindexY drawer( x0, x1 );
    DrawScale(drawer, dc, y0, y1, step);
}
傾城如夢未必闌珊 2024-07-12 13:55:18

为什么不将 for 循环的主体提取到单独的函数中? 然后你可以在提取的函数中做一些有趣的事情。

void DrawScaleX(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    for(int x = x0; x < x1; x += step)
    {
        DrawScale(dc, x, y0, x, y1);
    }
}

void DrawScaleY(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    for(int y = y0; y < y1; y += step)
    {
        DrawScale(dc, x0, y, x1, y);
    }
}

private void DrawScale(HDC dc, int x0, int y0, int x1, int y1)
{
    //Add funny stuff here

    MoveToEx(dc, x0, y0, NULL);
    LineTo(dc, x1, y1);

    //Add funny stuff here
}

Why you just do not extract the body of the for cycle into a separate function? Then you can do the funny stuff in the extracted function.

void DrawScaleX(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    for(int x = x0; x < x1; x += step)
    {
        DrawScale(dc, x, y0, x, y1);
    }
}

void DrawScaleY(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    for(int y = y0; y < y1; y += step)
    {
        DrawScale(dc, x0, y, x1, y);
    }
}

private void DrawScale(HDC dc, int x0, int y0, int x1, int y1)
{
    //Add funny stuff here

    MoveToEx(dc, x0, y0, NULL);
    LineTo(dc, x1, y1);

    //Add funny stuff here
}
叫思念不要吵 2024-07-12 13:55:18

绘制一条线只是连接两个点,并在特定方向上通过 X 和/或通过 Y 绘制递增的 (x0,y0) 和 (x1,y1) 缩放。
这归结为,在比例情况下,发生哪个方向的步进(也许两个方向都是有趣的)。

template< int XIncrement, YIncrement >
struct DrawScale
{
  void operator()(HDC dc, int step, int x0, int x1, int y0, int y1)
  {
    const int deltaX = XIncrement*step;
    const int deltaY = YIncrement*step;
    const int ymax = y1;
    const int xmax = x1;
    while( x0 < xmax && y0 < ymax )
    {
        MoveToEx(dc, x0, y0, NULL);
        LineTo(dc, x1, y1);
        x0 += deltaX;
        x1 += deltaX;
        y0 += deltaY;
        y1 += deltaY;
    }
  }
};
typedef DrawScale< 1, 0 > DrawScaleX;
typedef DrawScale< 0, 1 > DrawScaleY;

模板将完成其工作:在编译时,编译器将删除所有空语句,即有关调用哪个函数的 deltaX 或 deltaY 为 0,并且每个函子中的一半代码消失。

您可以在这个 uniq 函数中添加抗锯齿、铅笔等内容,并让编译器正确生成代码。

这是在类固醇上剪切和粘贴的;-)

-- ppi

Drawing a line is simply joining two points, and drawing a scaling incrementing (x0,y0) and(x1,y1) in a particular direction, through X, and/or through Y.
This boils down to, in the scale case, which direction(s) stepping occurs (maybe both directions for fun).

template< int XIncrement, YIncrement >
struct DrawScale
{
  void operator()(HDC dc, int step, int x0, int x1, int y0, int y1)
  {
    const int deltaX = XIncrement*step;
    const int deltaY = YIncrement*step;
    const int ymax = y1;
    const int xmax = x1;
    while( x0 < xmax && y0 < ymax )
    {
        MoveToEx(dc, x0, y0, NULL);
        LineTo(dc, x1, y1);
        x0 += deltaX;
        x1 += deltaX;
        y0 += deltaY;
        y1 += deltaY;
    }
  }
};
typedef DrawScale< 1, 0 > DrawScaleX;
typedef DrawScale< 0, 1 > DrawScaleY;

The template will do its job: at compile time the compiler will remove all the null statements i.e. deltaX or deltaY is 0 regarding which function is called and half of the code goes away in each functor.

You can add you anti-alias, pencil stuff inside this uniq function and get the code properly generated generated by the compiler.

This is cut and paste on steroids ;-)

-- ppi

め可乐爱微笑 2024-07-12 13:55:18

这是我自己的解决方案


class CoordGenerator
{
public:
    CoordGenerator(int _from, int _to, int _step)
        :from(_from), to(_to), step(_step), pos(_from){}
    virtual POINT GetPoint00() const = 0;
    virtual POINT GetPoint01() const = 0;
    bool Next()
        {
            if(pos > step) return false;
            pos += step;
        }
protected:
    int from;
    int to;
    int step;
    int pos;
};

class GenX: public CoordGenerator
{
public:
    GenX(int x0, int x1, int step, int _y0, int _y1)
        :CoordGenerator(x0, x1, step),y0(_y0), y1(_y1){}
    virtual POINT GetPoint00() const
        {
            const POINT p = {pos, y0};
            return p;
        }
    virtual POINT GetPoint01() const
        {
            const POINT p = {pos, y1};
            return p;
        }
private:
    int y0;
    int y1;
};

class GenY: public CoordGenerator
{
public:
    GenY(int y0, int y1, int step, int _x0, int _x1)
        :CoordGenerator(y0, y1, step),x0(_x0), x1(_x1){}
    virtual POINT GetPoint00() const
        {
            const POINT p = {x0, pos};
            return p;
        }
    virtual POINT GetPoint01() const
        {
            const POINT p = {x1, pos};
            return p;
        }
private:
    int x1;
    int x0;
};

void DrawScale(HDC dc, CoordGenerator* g)
{
    do
    {
        POINT p = g->GetPoint00();
        MoveToEx(dc, p.x, p.y, 0);
        p = g->GetPoint01();
        LineTo(dc, p.x, p.y);
    }while(g->Next());
}

,但在我看来,对于这样一个小问题来说太复杂了,所以我仍然期待看到您的解决方案。

Here is my own solution


class CoordGenerator
{
public:
    CoordGenerator(int _from, int _to, int _step)
        :from(_from), to(_to), step(_step), pos(_from){}
    virtual POINT GetPoint00() const = 0;
    virtual POINT GetPoint01() const = 0;
    bool Next()
        {
            if(pos > step) return false;
            pos += step;
        }
protected:
    int from;
    int to;
    int step;
    int pos;
};

class GenX: public CoordGenerator
{
public:
    GenX(int x0, int x1, int step, int _y0, int _y1)
        :CoordGenerator(x0, x1, step),y0(_y0), y1(_y1){}
    virtual POINT GetPoint00() const
        {
            const POINT p = {pos, y0};
            return p;
        }
    virtual POINT GetPoint01() const
        {
            const POINT p = {pos, y1};
            return p;
        }
private:
    int y0;
    int y1;
};

class GenY: public CoordGenerator
{
public:
    GenY(int y0, int y1, int step, int _x0, int _x1)
        :CoordGenerator(y0, y1, step),x0(_x0), x1(_x1){}
    virtual POINT GetPoint00() const
        {
            const POINT p = {x0, pos};
            return p;
        }
    virtual POINT GetPoint01() const
        {
            const POINT p = {x1, pos};
            return p;
        }
private:
    int x1;
    int x0;
};

void DrawScale(HDC dc, CoordGenerator* g)
{
    do
    {
        POINT p = g->GetPoint00();
        MoveToEx(dc, p.x, p.y, 0);
        p = g->GetPoint01();
        LineTo(dc, p.x, p.y);
    }while(g->Next());
}

But I it seems to me too complicated for such a tiny problem, so I'm looking forward to still see your solutions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文