Question

J'ai récemment commencé à refactoriser du code hérité et suis tombé sur deux fonctions permettant de dessiner une grille de coordonnées. Le problème est que ces fonctions ne diffèrent que par les variables orthogonales qu'elles traitent, ce qui ressemble à cela

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

Donc, si je décide d'ajouter des éléments fantaisistes, comme un anti-crénelage ou tout simplement changer de crayon à dessiner ou quoi que ce soit d'autre, il faudra que je mette le même code dans les deux cas. >

Ma question est la suivante: comment pourriez-vous réécrire ces deux fonctions en une seule pour éviter ce problème?

Était-ce utile?

La solution

Dessiner une ligne, c'est simplement joindre deux points et dessiner une incrémentation d'échelle (x0, y0) et (x1, y1) dans une direction particulière, par X et / ou par Y. Cela revient, dans le cas de la balance, à quelle (s) direction (s) se produit (peut-être dans les deux sens pour le plaisir).

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;

Le modèle fera son travail: lors de la compilation, le compilateur supprimera toutes les instructions nulles, c'est-à-dire que deltaX ou deltaY vaut 0 pour la fonction appelée et que la moitié du code disparaît dans chaque foncteur.

Vous pouvez ajouter un anti-alias, un crayon dans cette fonction uniq et obtenir le code généré correctement par le compilateur.

Ceci est couper et coller sur les stéroïdes; -)

- ppi

Autres conseils

Pourquoi ne pas simplement extraire le corps du cycle for dans une fonction distincte? Ensuite, vous pouvez faire les choses amusantes dans la fonction extraite.

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
}

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

Mais cela me semble trop compliqué pour un problème aussi minime, alors je suis impatient de voir vos solutions.

Eh bien, une "solution" évidente. serait de créer une seule fonction et d'ajouter un paramètre supplémentaire (de type enum). Et ensuite, faites un if () ou un switch () à l'intérieur et effectuez les actions appropriées. Parce que hé, la fonctionnalité des fonctions est différente, vous devez donc effectuer ces différentes actions quelque part .

Cependant, cela ajoute une complexité d'exécution (vérification des éléments à l'exécution) à un endroit qui pourrait être mieux contrôlé lors de la compilation.

Je ne comprends pas quel est le problème pour ajouter des paramètres supplémentaires à l'avenir dans les deux (ou plusieurs fonctions). Cela va comme ceci:

  1. ajouter plus de paramètres à toutes les fonctions
  2. compilez votre code, il ne sera pas compilé dans un tas d'endroits car il ne transmet pas de nouveaux paramètres.
  3. corrigez tous les endroits qui appellent ces fonctions en transmettant de nouveaux paramètres.
  4. profit! :)

S'il s'agit de C ++, vous pouvez bien sûr faire de la fonction un modèle et ajouter un paramètre supplémentaire à la place. Vous ajoutez ensuite un paramètre de modèle, puis vous spécialisez les implémentations de modèles pour différentes tâches. Mais, à mon avis, cela ne fait que brouiller les pistes. Le code devient plus difficile à comprendre et le processus d'extension avec plus de paramètres est toujours exactement identique:

  1. ajouter des paramètres supplémentaires
  2. compiler du code, il ne compilera pas dans un tas d'endroits
  3. corrige tous les endroits qui appellent cette fonction

Vous n'avez donc rien gagné, mais le code est plus difficile à comprendre. Pas un objectif louable, IMO.

Je pense que je déménagerais:

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

dans leur propre fonction DrawLine (x0, y0, x0, y0), que vous pouvez appeler à partir de chacune des fonctions existantes.

Ensuite, il y a un endroit pour ajouter des effets de dessin supplémentaires?

Quelques modèles ...:)

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);
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top