2013-04-14 6 views
7

Ich habe ein einfaches Programm, das geometrische Figuren basierend auf Mausdaten vom Benutzer erstellt. Ich habe eine Klasse, die die Maus-Tracking behandelt (es erhält die Liste mit der Mausbewegung Geschichte) und eine abstrakte Klasse namens Shape. Aus dieser Klasse lasse ich einige zusätzliche Formen wie Kreis, Rechteck usw. erkennen - und jeder von ihnen überschreibt die abstrakte Funktion Draw().Vorbei kommen Open-Closed Prinzip

Alles funktioniert gut, aber das Problem kommt, wenn ich möchte, dass der Benutzer gewünschte Shape manuell wechseln kann. Ich bekomme die Mausdaten und ich weiß, welche Form ich zeichnen soll. Das Problem besteht darin, den Code so zu gestalten, dass er "weiß", welches Objekt erstellt werden soll, und geeignete Parameter an den Konstruktor übergeben. Es ist auch unmöglich, an dieser Stelle neue Shape-Ableitungen hinzuzufügen, was offensichtlich falsch ist.

ich obiously wollen nicht wie mit dem Code kommen:

List<Shape> Shapes = new List<Shape>(); 
// somwhere later 

if(CurrentShape == "polyline"){ 
    Shapes.Add(new Polyline(Points)); 
} 
else if (CurrentShape == "rectangle"){ 
    Shapes.Add(new Rectangle(BeginPoint, EndPoint)); 
} 
// and so on. 

Der obige Code eindeutig vilates das Open-Geschlossen-Prinzip. Das Problem ist, dass ich keine gute Idee habe, wie ich darüber hinwegkommen könnte. Das Hauptproblem ist, dass verschiedene Shapes Konstruktoren mit verschiedenen Parametern haben, was es viel mühsamer macht.

Ich bin mir ziemlich sicher, dass dies ein häufiges Problem ist, aber ich weiß nicht, wie ich darüber hinwegkommen soll. Hast du eine Idee?

+2

Das ist nicht das "Offen-Geschlossen-Prinzip". Das ist nur Polymorphismus –

+1

Nun, ich möchte die Shape-Klasse Code für die Edition geschlossen und für Erweiterungen geöffnet, so denke ich, dass es das OCP-Problem entspricht. –

+0

So sehr du möchtest, dass es wahr ist, ist es nicht! –

Antwort

3

Es erfordert eine Fabrik, aber nicht nur die Fabrik, sondern die Fabrik mit injizierbaren Arbeiter.

Auf diese Weise ist der Code für Erweiterungen geöffnet - neue Fabrikarbeiter können frei erstellt und zur Fabrik hinzugefügt werden.

+0

Hallo. Ich würde gerne wissen, wie ist das besser als die andere Antwort, die 'ShapeFactory' anstelle von injizierenden Arbeitern erweitert, wenn die andere Antwort auch einen' string ShapeName' Parameter verwendet? – Blueriver

+0

@Blueriver: Dies würde beide Antworten gleichwertig machen. Aber in der anderen Antwort gibt es keine Fabrik. Die Macher von dort sind dämliche Arbeiter von hier. Die Fabrik selbst sollte niemals modifiziert werden, da sie den geschlossenen Teil des OCP brechen würde. –

+0

Ich sehe. Vielen Dank! – Blueriver

6

Wenn Sie Objekte erstellen müssen, die alle von einer einzelnen Klasse abgeleitet sind oder die gleiche Schnittstelle implementieren, ist ein gemeinsamer Ansatz die Verwendung eines factory. In Ihrem Fall reicht jedoch eine einfache Fabrik möglicherweise nicht aus, da die Fabrik selbst erweiterbar sein muss.

Eine Möglichkeit, es zu implementieren, ist wie folgt:

interface IShapeMaker { 
    IShape Make(IList<Point> points); 
} 
class RectMaker : IShapeMaker { 
    public Make(IList<Point> points) { 
     // Check if the points are good to make a rectangle 
     ... 
     if (pointsAreGoodForRectangle) { 
      return new Rectangle(...); 
     } 
     return null; // Cannot make a rectangle 
    } 
} 
class PolylineMaker : IShapeMaker { 
    public Make(IList<Point> points) { 
     // Check if the points are good to make a polyline 
     ... 
     if (pointsAreGoodForPolyline) { 
      return new Polyline(...); 
     } 
     return null; // Cannot make a polyline 
    } 
} 

Mit diesen Maker Klassen in der Hand, können Sie eine Registrierung von Entscheidungsträgern machen (eine einfache List<IShapeMaker>) die Macher gehen durch sie die Punkte, und Stoppen wenn Sie eine Nicht-Null-Form zurückbekommen.

Dieses System erweiterbar bleibt, weil Sie ein Paar NewShape und NewShapeMaker hinzufügen können, und „stecken Sie sie in“ in den bestehenden Rahmen: einmal NewShapeMaker in der Registrierung erhält, wird der Rest des Systems sofort bereit und Verwendung zu erkennen Ihre .

+2

Es gibt einen Nachteil dieser Implementierung - da die gleiche Menge von Punkten möglicherweise von mehreren Herstellern akzeptiert werden kann, ist plötzlich die Reihenfolge der Hersteller wichtig - Änderung der Reihenfolge würde die Fabrik eine andere Form zurückgeben. Die ursprüngliche Implementierung ist frei von diesem Problem, da der Factory-Client explizit festlegt, welche Form er haben möchte. Dies könnte leicht behoben werden, indem dieser zusätzliche Parameter an die Factory-Methode übergeben wird (siehe meine Antwort). –

+0

@WiktorZychla Der zusätzliche Parameter gibt dem Aufrufer explizite Kontrolle, aber es macht es auch notwendig, den Aufrufer jedes Mal zu ändern, wenn Sie das System durch Hinzufügen einer neuen Form erweitern (dh der Aufrufer muss "lernen", den Namen des zu übergeben) jedes Mal, wenn eine neue Form verfügbar wird, neue Form. Ich habe die Frage gelesen, dass das OP diesen zusätzlichen Parameter vermeiden möchte. – dasblinkenlight

+0

Er schreibt "Ich kenne die Mausdaten und die Form, die ich zeichnen sollte". Daraus würde ich sagen, dass der Anrufer die genaue Form kennt. Ich glaube, er hat eine Werkzeugkiste mit allen verfügbaren Formen oder etwas ähnliches. –