Pregunta

He empezado a refactorización algún código heredado hace poco y que surgió a través de dos funciones para dibujar una cuadrícula de coordenadas, el problema es que estas funciones sólo se diferencian en el ortogonal variables que tratar, algo así como que

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);
    }
}

Así que si me decido a agregar algunas complicaciones, como el suavizado de líneas o simplemente cambiar el dibujo a lápiz o lo que sea voy a tener que poner el mismo código en cada una de ellas y es la duplicación de código y es malo, todos sabemos por qué.

Mi pregunta es ¿cómo podría usted escribir estos dos funciones en una sola para evitar este problema?

¿Fue útil?

Solución

Dibujo de una línea es simplemente la unión de dos puntos, y la elaboración de una escala de incremento (x0,y0) y(x1,y1) en una dirección en particular, a través de X, y/o a través de Y.Esto se reduce a que, en la escala de caso, que la dirección(s) paso a paso ocurre (tal vez ambas direcciones para la diversión).

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;

La plantilla va a hacer su trabajo:en tiempo de compilación, el compilador va a quitar las instrucciones nulas decirdeltaX o deltaY es de 0 con respecto a la que se llama a la función y la mitad de el código desaparece en cada functor.

Usted puede agregar los anti-alias, lápiz cosas dentro de este uniq función y obtener el código correctamente generado generado por el compilador.

Este es cortar y pegar en los esteroides ;-)

-- ppi

Otros consejos

Por qué no extraer el cuerpo del ciclo en una función separada?A continuación, puede ver la cosas graciosas en el extraído de la función.

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
}

Aquí está mi propia solución


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());
}

Pero yo a mí me parece demasiado complicado para que un pequeño problema, así que estoy deseando ver sus soluciones.

Bien, una evidente "solución" sería realizar una sola función y añadir un parámetro adicional (de enum-como tipo).Y, a continuación, hacer un if() o switch() en el interior, y realizar las acciones adecuadas.Porque oye, el funcionalidad de las funciones es diferente, así que usted tiene que hacer las diferentes acciones en algún lugar.

Sin embargo, esto añade tiempo de ejecución de la complejidad (comprobar las cosas en tiempo de ejecución) en un lugar que podría ser mejor comprueban en tiempo de compilación.

No entiendo cuál es el problema en añadir parámetros adicionales en el futuro en los dos (o más funciones).Va como esto:

  1. agregar más parámetros de todas las funciones
  2. compilar el código no compila en un montón de lugares debido a que no pase de nuevo los parámetros.
  3. revisión de todos los lugares que llamar a esas funciones con la aprobación de nuevos parámetros.
  4. la ganancia!:)

Si es C++, por supuesto, usted puede hacer la función de ser una plantilla, y en lugar de añadir un parámetro adicional, agregar un parámetro de plantilla y, a continuación, se especializan plantilla implementaciones para hacer cosas diferentes.Pero esto es sólo dejando en la oscuridad el punto de, en mi opinión.El código se convierte en más difícil de entender, y el proceso de ampliación con mayor número de parámetros es todavía exactamente la misma:

  1. añadir parámetros adicionales
  2. compilar el código, no va a compilar en un montón de lugares
  3. revisión de todos los lugares que llame a esa función

Por lo que he ganado nada, pero se hace el código más difícil de entender.No es un objetivo que vale la pena, de la OMI.

Creo que me mueva:

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

en su propia función DrawLine(x0,y0,x0,y0), a los que puede llamar desde cada una de las funciones existentes.

A continuación, hay un lugar para añadir efectos de dibujo?

Un poco de plantillas...:)

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);
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top