Frage

Ich habe angefangen, ein Koordinatengitter einige Vermächtnis Refaktorierungscode vor kurzem und kam über zwei Funktionen zum Zeichnen, das Problem ist, dass diese Funktionen unterscheiden sich nur in orthogonalen Variablen sie behandeln, so etwas wie das

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

Also, wenn ich mich entscheide, einige ausgefallene Sachen zu schreiben, Anti-Aliasing oder nur ändern Bleistiftzeichnung oder was auch immer ich den gleichen Code in beide setzen müssen, und es ist Code-Duplizierung und es ist schade, dass wir alle wissen, warum.

Meine Frage ist, wie würden Sie diese beiden Funktionen in einem einzigen umschreiben dieses Problem zu vermeiden?

War es hilfreich?

Lösung

Zeichnen eine Linie einfach zwei Punkte beitritt, und eine Skalierung erhöhen (x0, y0) und (x1, y1) in einer bestimmten Richtung, durch X zeichnen, und / oder durch Y. Dies läuft darauf hinaus, im Maßstab Fall, in welche Richtung (en) Stepping (vielleicht beiden Richtungen zum Spaß) auftritt.

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;

Die Vorlage wird seine Arbeit tun. Bei der Kompilierung der Compiler entfernen Sie alle null Aussagen heißt deltaX oder deltaY 0 in Bezug auf die Funktion aufgerufen wird und die Hälfte des Codes geht weg in jedem Funktor

Sie können Sie hinzufügen, Anti-Aliasing, Bleistift Zeug in dieser uniq Funktion und erhalten den Code richtig vom Compiler generiert generiert.

Dies ist Ausschneiden und Einfügen auf Steroiden; -)

- ppi

Andere Tipps

Warum extrahieren Sie nicht nur den Körper des für den Zyklus in eine separate Funktion? Dann können Sie die lustigen Sachen in der extrahierten Funktion tun.

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
}

Hier ist meine eigene Lösung


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

Aber ich so scheint es mir zu kompliziert für so ein kleines Problem, so dass ich freue mich auf noch Ihre Lösungen zu sehen.

Nun, eine offensichtliche „Lösung“ wäre eine einzige Funktion zu machen und einen zusätzlichen Parameter hinzufügen (von ENUM-like-Typ). Und dann tun ein if () oder Schalter () nach innen, und die entsprechenden Aktionen auszuführen. Weil hey, die Funktionalität der Funktionen ist anders, so dass Sie diese verschiedenen Aktionen zu tun haben, irgendwo .

Allerdings fügt diese Laufzeitkomplexität (Dinge zur Laufzeit überprüfen) in einem Ort, der zum Zeitpunkt der Kompilierung nur besser kontrolliert werden könnte.

Ich verstehe nicht, was das Problem ist in dem Hinzufügen zusätzliche Parameter in der Zukunft in den beiden (oder mehr Funktionen). Es geht so:

  1. mehr Parameter auf alle Funktionen hinzufügen
  2. kompilieren Sie den Code, wird es nicht in einem Bündel von Orten kompilieren, weil es keine neuen Parameter nicht besteht.
  3. reparieren alle Orte, die diese Funktionen aufrufen, indem Sie neue Parameter.
  4. Gewinn! :)

Wenn es C ++ ist, natürlich könnte man die Funktion eine Vorlage machen sein, und stattdessen Hinzufügen eines zusätzlichen Parameter, fügen Sie einen Template-Parameter und dann Template-Implementierungen spezialisiert verschiedene Dinge zu tun. Aber das ist das Verschleiern nur den Punkt, meiner Meinung nach. Code wird schwerer zu verstehen, und der Prozess wird es mit mehreren Parametern der Erweiterung ist noch genau das gleiche:

  1. fügen Sie zusätzliche Parameter
  2. kompiliert Code, wird es in einem Bündel von Orten, die nicht kompiliert
  3. reparieren alle Orte, die diese Funktion
  4. rufen

Damit Sie nichts gewonnen haben, aber gemacht Code schwerer zu verstehen. Nicht ein lohnendes Ziel, IMO.

Ich glaube, ich würde bewegen:

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

in ihre eigene Funktion DrawLine (X0, Y0, X0, Y0), die Sie von jedem der vorhandenen Funktionen aufrufen können.

Dann gibt es noch einen Ort zusätzliche Zeichnung Effekte hinzufügen?

Eine kleine Vorlagen ...:)

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);
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top