2016-06-29 4 views
7

Ich versuche, bessere Programmierpraktiken mit SOLID-Prinzipien zu lernen. Hier arbeite ich an einer Beispielanwendung von Shapes. Ich möchte nur wissen, ob ich das Prinzip irgendwo durchbringe. Nachstehend sind Klassen und ihr Code.Brich dies mein SOLID-Prinzip?

1. Basisklasse - Shape

public abstract class Shape 
{ 
    public abstract double Area(); 
    public virtual double Volume() 
    { 
     throw new NotImplementedException("You cannot determine volume from here...Method not implemented."); 
    } 
} 

2. Klassen für Formen wie Rechteck, Dreieck usw. Implementierung Basisklasse Form.

public class Circle : Shape 
{ 
    public int Radius { get; set; } 
    public override double Area() { return 3.14 * Radius * Radius; } 
} 

public class Triangle : Shape 
{ 
    public int Height { get; set; } 
    public int Base { get; set; } 
    public override double Area() 
    { 
     return 0.5 * Base * Height; 
    } 
} 

public class Rectangle : Shape 
{ 
    public int Length { get; set; } 
    public int Breadth { get; set; } 
    public override double Area() 
    { 
     return Length * Breadth; 
    } 
} 

public class Square : Shape 
{ 
    public Square() { } 
    public int Side { get; set; } 
    public override double Area() 
    { 
     return Side * Side; 
    } 
} 

3. Eine Fabrik-Klasse, die Form zurückkehrt.

internal class ShapeFactory<K, T> where T : class, K, new() 
{ 
    static K k; 
    private ShapeFactory() { } 

    public static K Create() 
    { 
     k = new T(); 
     return k; 
    } 
} 

Bis hier alles scheint in Ordnung und sieht gut aus, aber Problem tritt auf, wenn ich es umgesetzt. Ich bin hier etwas verwirrt. Lässt das vordere Ende Code zuerst sehen:

internal class Program 
{ 
    private static void Main(string[] args) 
    { 
     try 
     { 

      var c = ShapeFactory<Shape, Circle>.Create(); 
      // this part is not clear to me. See the questions below 
      if(c is Circle) 
      { 
       var circle = c as Circle; 
       circle.Radius = 5; 
       Console.WriteLine(string.Format("{0}", circle.Area())); 
      } 


     } 

     catch (Exception ex) 
     { 

      Console.WriteLine("Error: {0}", ex.Message); 
     } 
     Console.Read(); 
    } 
} 

FRAGEN

  1. Verschiedene Formen hat bekam unterschiedliche Eigenschaften wie Kreis hat Radius, Dreieck hat Grundfläche und Höhe und so weiter, so habe ich beschlossen, meine Eigenschaften zu halten in der Kinderklasse. Ich wusste, dass ich das als virtuelles Mitglied in meiner Basisklasse haben kann. Gibt es einen anderen Weg als oben beschrieben?

  2. Wenn nicht, was ist dann die Verwendung der abstrakten Klasse, wenn ich immer noch mein Shape-Objekt in ein Kreisobjekt umwandele? Ich kann einfach Circle c = new Circle() verwenden. Ich möchte keine unerwünschten Überprüfungen wie (wenn c Kreis ist) und alle.

  3. Was wäre, wenn ich gebeten werde, eine neue Methode zu implementieren, um den Umfang eines Kreises zu erhalten. Muss ich eine neue Klasse Abstract erstellen oder in die Circle-Klasse einfügen? Aber wenn ich es Circle schreibe, denke ich, es wird das erste Prinzip von SOLID brechen, d. H. SRP . Bitte beachten Sie, dass ich meine abstrakte Klasse nicht als eine Fettklasse mit unnötigen oder wiederholten Eigenschaften habe.

Vielen Dank im Voraus

+5

http://codereview.stackexchange.com/ wäre besser für diese Frage geeignet – Papa

Antwort

0
  1. Verschiedene Kindklassen unterschiedliche Eigenschaften haben wird, ist, dass erwartet und ok. Normalerweise haben nicht alle abgeleiteten Klassen die gleichen Eigenschaften wie ihre Basisklasse. Es gibt keinen Grund, Shape zu zwingen, eine Radius zu haben. Welchen Vorteil hättest du? Das öffnet nur die Tür für Ärger. Was ist dein ultimatives Ziel? Etwas wie myShape.Dimension = value und nicht egal, ob es ein Radius, eine Seite usw. ist? Alles kann getan werden, abhängig von Ihren Bedürfnissen.

  2. Mit Ihrer abstrakten Klasse können Sie zum Beispiel Schleife durch eine Liste von Shape und rufen Area() oder Volume(), zu wissen, dass Sie Ihr Ergebnis erhalten werden (trotz Ihres noch Volume nicht implementiert). Auch Ihre Basisklasse könnte einen gemeinsamen Code haben, den Sie in diesem Fall nicht verwenden. Sie könnten zum Beispiel eine Unit Eigenschaft haben, die cm, Zoll, Meter usw. sein könnte.und hat dann ein Verfahren wie dieses (dummes Beispiel):

    public string GetAreaString() 
    { 
        return string.Format("{0} {1}", this.Area().ToString(), this.Unit); 
    } 
    
  3. einfach implementieren es in Circle, natürlich. Warum sollte es Circle einzige Verantwortung brechen? Ihre Klasse beschäftigt sich mit der Berechnung ihrer verwandten Werte, genau wie ein string Ihnen sagt, ob es null oder seine Länge ist.

6

Was ich in diesem Fall normalerweise tue, besteht darin, Konstruktorparameter in konkreten Klassen zu übergeben. Also würde ich Ihre konkrete Formen zu so etwas wie ändern:

public class Circle : Shape 
{ 
    public int Radius { get; set; } 

    public Circle(int radius) { 
     this.Radius = radius; 
    } 

    public override double Area() { return 3.14 * this.Radius * this.Radius; } 
} 

public class Rectangle : Shape 
{ 
    public int Length { get; set; } 
    public int Breadth { get; set; } 

    public Rectangle(int lenght, int breadth) { 
     this.Length = lenght; 
     this.Breadth = breadth; 
    } 

    public override double Area() 
    { 
     return Length * Breadth; 
    } 
} 

und so weiter

Jetzt möchte ich eine Factory-Methode verwenden, so dass Ihr Gewebe wird nun sein, wie:

public abstract class ShapeFactory 
{ 
    abstract Create(); 
} 

public class CircleFactory : ShapeFactory 
{ 
    private int radius; 

    public CircleFactory(int radius){ 
     this.radius = radius; 
    } 

    protected override Shape Create() 
    { 
     return new Circle(this.radius); 
    } 
} 

public class RectangleFactory : ShapeFactory 
{ 
    private int length; 
    private int breadth; 

    public RectangleFactory(int length, int breadth){ 
     this.lenght = length; 
     this.breadth = breadth;  
} 

    protected override Shape Create() 
    { 
     return new Rectangle(this.length, this.breadth); 
    } 
} 

Beachten Sie, dass eine Fabrik jetzt weiß, wie man eine Form erstellt, deren Konstruktor in einem eigenen Konstruktor übergeben wird.

Also jedes Mal, wenn Sie eine andere Form möchten, werden Sie eine neue Fabrik instanziieren.

ShapeFactory factory = new CircleFactory(5); 
Shape shape = factory.Create(); 
Console.WriteLine(shape.Area())); 

Ich denke, dies beantwortet Ihre erste und zweite Frage. So

, 3: Was Sie tun können, um nicht ändern Sie Ihre Klasse ist die Strategie-Muster verwenden, um zur Laufzeit übergeben, wie diese Methode zu implementieren:

public interface IPerimeter 
{ 
    int calculatePerimeter(); 
} 

public class Circunference : IPerimeter 
{ 
    public int calculatePerimeter(Circle circle) { 
     return 2*pi*circle.radius; 
    } 
} 

public class Circle : Shape 
{ 
    public int Radius { get; set; } 
    private IPerimeter perimeter; 

    public Circle(int radius, IPerimeter perimeter) { 
     this.Radius = radius; 
     this.perimeter = perimeter; 
    } 

    public Circunference() { 
     perimeter.calculatePerimeter(this); 
    } 

    public override double Area() { return 3.14 * this.Radius * this.Radius; } 
} 

Hope this mit dem Training hilft.

+0

Vielen Dank für Ihre Eingabe. Schätze das wirklich. Noch eine Frage, was ist die Verwendung und der Körper der ShapeFactory-Klasse. Du hast das in deiner Antwort nicht erwähnt. Benutzt du meine generische Klasse oder es ist eine andere deiner eigenen Klasse. Ich kann CircleFactory und RectangleFactory als konkrete Klasse sehen. –

+0

oh, ich habe diese Klasse vergessen ... Diese Klasse ist eine abstrakte Klasse mit einer einfachen abstrakten Methode Create() .. aktualisiert oben – guijob

+0

Ich denke, es gibt ein paar Fehler in Ihrem Code. Zum Beispiel glaube ich, du meintest "public int Circunference()" (sonst sieht es wie ein Konstruktor aus) und ich denke, du willst, dass es diese Berechnung zurückgibt. Die 'IPerimeter' Methode sollte auch' int calculatePerimeter (Circle circle); 'sein. Schließlich sollte diese Methode 'return (int) (2 * Math.PI * circle.Radius);'. Übrigens gibt es auch ein paar "Längen". ;) – Andrew

0

Für mich scheint Ihr Beispiel wirklich überentwickelt. Ich denke, du solltest immer die einfachste Sache implementieren, die nicht mehr und nicht weniger funktioniert. Ich weiß, dass dies ein Beispielcode ist, weil Sie die SOLID-Prinzipien lernen wollen, aber ich denke, es ist wichtig, sich bewusst zu sein, wie schrecklich falsch diese Prinzipien im falschen Kontext gehen können. In Ihrem spezifischen Code: Müssen Sie alle Ihre Shapes mit der Shape-Klasse gruppieren? Ich meine, hast du jemals vor, durch eine Liste von Formen zu iterieren und die Fläche und das Volumen für sie zu berechnen? Wenn nicht, hat die Vererbung absolut keinen Sinn. In der Tat würde ich sagen, dass Vererbung in diesen Tagen überstrapaziert wird, und wenn es überstrapaziert wird, enden Sie mit hässlichen Vererbungsgraphen. Bezüglich der Fabrikklasse: Ist der Aufbau eines Ihrer "Form" -Objekte besonders schwierig, zeitraubend, knifflig. Bietet Ihre Fabrikklasse einen Wert oder ist sie völlig nutzlos? Falls es keinen wirklichen Grund gibt zu existieren, würde ich es nicht benutzen, der neue Betreiber ist viel klarer.

Ich hoffe, dass Sie meine Antwort nicht stört, aber ich wollte nur, dass Sie sich der Tatsache bewusst sind, dass einige SOLID-Prinzipien in sehr spezifischen Szenarien gelten. Sie an den falschen Stellen zu zwingen, kann zu hässlichem und übermäßig kompliziertem Code führen. In einigen realen Situationen scheint Ihr Muster OK zu sein, wenn die obigen Fragen mit Ja beantwortet werden. Andernfalls kann das exakt gleiche Muster die Dinge ohne wirkliche Vorteile übermäßig komplizieren. Ich denke, mein Punkt ist: Sei dir bewusst, dass nicht jedes SOLID-Prinzip in irgendeiner Situation gut ist :).

0

Dies ist ein sehr häufiges Problem. Während das Lernen von SOLID nett ist, erfordert es das Verständnis grundlegender Designprinzipien wie Abstraktion und Indirektion. Der Grund, warum Sie verwirrt sind, liegt darin, dass es in Ihrem Code keine Abstraktion gibt.

Stellen Sie sich vor, Sie haben einen Code, der den Bereich der Form kennen möchte, aber es ist egal, welche Form er hat und wie dieser Bereich berechnet wird. Etwas wie:

public void PrintArea(Shape shape) 
{ 
    Console.WriteLine(shape.Area()); 
} 

Dies ist der entscheidende Teil der OOP-Design. Dein Beispiel hat davon absolut nichts. Ihr Beispiel ist ein erfundenes Stück Code, das keine Logik hat, geschweige denn SOLID zu sein.