2016-06-29 7 views
7

Sto cercando di apprendere le migliori pratiche di programmazione utilizzando i principi SOLID. Qui sto lavorando ad un'applicazione di esempio di Shapes. Voglio solo sapere, sto infrangendo il principio ovunque. In basso ci sono le classi e il suo codice.Questo rompe il mio principio SOLIDO?

1. Classe Base - Dai forma

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. Classi di forme come rettangolo, triangolo ecc attuazione Forma classe di base.

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. Una classe factory che restituisce Shape.

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

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

Fino a qui tutto sembra bello e sembra buono, ma il problema si verifica quando ho implementato esso. Sono poco confuso qui. Vediamo il codice front-end prima:

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

DOMANDE

  1. Diverse forme ha proprietà diverse, come cerchio ha Radius, triangolo ha base e altezza e così via, così ho deciso di mantenere le mie proprietà in classe bambino. Lo sapevo, posso averlo come membro virtuale nella mia classe base. Quindi c'è un modo diverso dal codice sopra.

  2. In caso contrario, qual è l'uso della classe astratta, se continuo a digitare il mio oggetto Forma in oggetto cerchio? Posso usare semplicemente Circle c = new Circle(). Non voglio controlli indesiderati come (se c è cerchio) e tutto.

  3. Cosa succede se, mi viene chiesto di implementare un nuovo metodo per ottenere la circonferenza di un cerchio. Devo creare una nuova classe astratta o metterla in classe Circle. Ma se lo metto Circle, penso che romperà il primissimo principio di SOLID, ad esempio SRP . Si prega di notare, io non la mia classe astratta come una classe di grasso con proprietà inutili o ripetute.

Grazie in anticipo

+5

http://codereview.stackexchange.com/ sarebbe più adatto per questa domanda – Papa

risposta

0
  1. Diverse classi bambino avrà diverse proprietà, che è previsto e OK. Normalmente non tutte le classi derivate hanno le stesse proprietà della loro classe base. Non c'è motivo di forzare lo Shape per avere un Radius. Che vantaggio vorresti avere? Sta solo aprendo la porta alla ricerca di guai. Qual è il tuo obiettivo finale con questo? Avere qualcosa come myShape.Dimension = value e non importa se si tratta di un raggio, un lato, ecc.? Tutto può essere fatto, a seconda delle esigenze.

  2. Con la vostra classe astratta è possibile, per esempio, ciclo in un elenco di Shape e chiamare Area() o Volume(), sapendo che si otterrà il risultato (nonostante il tuo ancora non implementato Volume). Anche la tua classe base potrebbe avere un codice comune, che in questo caso non stai usando. Potresti avere ad esempio una proprietà Unit che potrebbe essere cm, pollici, metri, ecc.e poi hanno un metodo come questo (esempio stupido):

    public string GetAreaString() 
    { 
        return string.Format("{0} {1}", this.Area().ToString(), this.Unit); 
    } 
    
  3. Basta attuare in Circle, naturalmente. Perché dovrebbe rompere la responsabilità unica di Circle? La classe si occupa di calcolo dei valori legati , proprio come un string ti dice se è nullo o la sua lunghezza.

6

Quello che faccio di solito in questo caso è passare i parametri del costruttore nelle classi concrete. Quindi mi piacerebbe cambiare le forme concrete per qualcosa di simile:

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

e così via

Ora, vorrei utilizzare un metodo di fabbrica, in modo che il tessuto sarà ora essere come:

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

Si noti che, ora una fabbrica sa come costruire una forma con il costruttore passato nel proprio costruttore.

Così, ogni volta che si desidera una forma spettacolo diverso si un'istanza di un nuovo stabilimento.

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

Penso che risponda alla prima e alla seconda domanda.

Così, 3: Cosa si può fare per dont modificare la classe è utilizzare il modello di strategia per passare in fase di esecuzione come implementare questo metodo:

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

Spero che questo aiuti con la vostra formazione.

+0

Grazie per il vostro contributo. Lo apprezzo davvero. Ancora una domanda, qual è l'uso e il corpo della classe ShapeFactory. Non l'hai menzionato nella tua risposta. Stai usando la mia classe generica o è un'altra tua. Posso vedere CircleFactory e RectangleFactory come classe concreta. –

+0

oh, ho dimenticato questa classe ... questa classe è una classe astratta con un semplice metodo astratto Create() .. aggiornato sopra – guijob

+0

Penso che ci siano alcuni errori nel codice. Ad esempio, penso che tu intendessi 'public int Circunference()' (altrimenti sembra un costruttore) e suppongo che tu voglia "restituire" quel calcolo. Anche il metodo 'IPerimeter' dovrebbe essere' int calculatePerimeter (Circle circle); '. Infine, questo metodo dovrebbe 'return (int) (2 * Math.PI * circle.Radius);'. A proposito, ci sono anche un po 'di lunghezza. ;) – Andrew

0

Per me il vostro esempio sembra davvero finita ingegnerizzato. Penso che dovresti sempre implementare la cosa più semplice che non funzioni nient'altro di meno. So che questo è un esempio di codice, perché si vuole imparare i principi solido, ma penso che sia importante essere consapevoli di come terribilmente sbagliato può andare questi principi nel contesto sbagliato. Nel tuo codice specifico: hai bisogno di raggruppare tutte le tue forme usando la classe Shape? Voglio dire, hai mai intenzione di scorrere un'elenco di forme e calcolare l'area e il volume per loro? In caso contrario, l'eredità non ha assolutamente senso. In effetti direi che l'ereditarietà è abusata in questi giorni, e quando è sovrautilizzata si finisce con brutti grafici di dipendenza ereditaria. Per quanto riguarda la classe di fabbrica: la costruzione di uno qualsiasi dei tuoi oggetti "di forma" è particolarmente difficile, lunga e complessa. La tua classe di fabbrica fornisce qualche valore o è completamente inutile? Se non ha una vera ragione per esistere, non lo userei, il nuovo operatore è molto più chiaro.

spero che non ti dispiaccia la mia risposta, ma volevo solo che tu sia a conoscenza del fatto che alcuni solidi principi si applicano in scenari molto specifici. Costringerli nei posti sbagliati potrebbe causare un codice brutto e complicato. In alcune situazioni del mondo reale, se le domande di cui sopra hanno una risposta affermativa, il tuo schema sembra OK. In caso contrario, lo stesso identico schema può complicare eccessivamente le cose senza reali benefici. Suppongo che il mio punto sia: attenzione, non tutti i principi SOLID sono buoni in ogni situazione :).

0

Questo è problema molto comune. Sebbene l'apprendimento di SOLID sia piacevole, richiede la comprensione dei principi di progettazione di base come l'astrazione e l'indirezione. Il motivo per cui sei confuso è perché non c'è astrazione nel tuo codice.

Immagina di avere un codice che vuole conoscere l'area della forma, ma non gli importa quale sia la sua forma né come calcolare l'area di quella forma. Qualcosa del tipo:

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

Questa è la PARTE CRITICA del design OOP. Il tuo esempio non ha assolutamente nulla di questo. Il tuo esempio è solo un pezzo di codice inventato che non ha alcuna logica, per non parlare del fatto che è SOLIDO.

Problemi correlati