2016-06-29 10 views
7

Я пытаюсь изучить лучшие практики программирования, используя принципы SOLID. Здесь я работаю над образцом приложения Shapes. Я просто хочу знать, я нарушаю принцип где угодно. Ниже приведены классы и его код.Является ли это нарушением моего принципа SOLID?

1. Базовый класс - 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. Классы для фигур, таких как прямоугольник, треугольник и т.д., реализующих базового класса Shape.

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. Фабрика класса, который возвращает форму.

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

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

До здесь все кажется хорошо и выглядит хорошо, но проблема возникает, когда я реализовал его. Я здесь немного запутался. Давайте посмотрим на передний конце код первый:

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

ВОПРОСЫ

  1. Различных формы имеют различные свойства, такие как круг имеет радиус, треугольник имеет основание и высоту, и так далее, так что я решил сохранить свои свойства в классе детей. Я знал, я могу иметь это как виртуальный член в моем базовом классе. Так что есть какой-то другой способ, кроме закодированного выше.

  2. Если нет, то в чем смысл абстрактного класса, если все же я приписываю объект Shape объекту окружности? Я могу просто использовать Circle c = new Circle(). Я не хочу нежелательных проверок вроде (если c - круг) и все.

  3. Что если, меня попросят внедрить новый метод, чтобы получить Окружность круга. Мне нужно создать новый абстрактный класс или поместить его в класс Circle. Но если я ставлю Circle, я думаю, что это нарушит очень первый принцип SOLID, т. Е. SRP . Прошу прощения, я не являюсь моим абстрактным классом, как жирный класс, имеющий ненужные или повторяющиеся свойства.

Заранее спасибо

+5

http://codereview.stackexchange.com/ был бы лучше подходит для этого вопроса – Papa

ответ

0
  1. Различные дочерние классы будут иметь разные свойства, что ожидается и хорошо. Обычно не все производные классы имеют те же свойства, что и их базовый класс. Нет причин принуждать Shape иметь Radius. Какое у вас было бы преимущество? Это просто открывает дверь для неприятностей. Какова ваша конечная цель? Что-то вроде myShape.Dimension = value, и неважно, если это радиус, сторона и т. Д.? Все может быть сделано в зависимости от ваших потребностей.

  2. С вашим абстрактным классом вы можете, например, цикл по списку Shape и позвонить Area() или Volume(), зная, что вы получите свой результат (несмотря на ваши еще не реализованы Volume). Также ваш базовый класс может иметь некоторый общий код, который в этом случае вы не используете. Вы могли бы иметь, например, свойство Unit, которое может быть см, дюймами, метрами и т. Д.а затем метод, как это (глупый пример):

    public string GetAreaString() 
    { 
        return string.Format("{0} {1}", this.Area().ToString(), this.Unit); 
    } 
    
  3. Просто реализовать его в Circle, конечно. Почему это сломало бы единственную ответственность Circle? Ваш класс имеет дело с вычислением своих значений , так же как string сообщает вам, является ли оно нулевым или его длина.

6

Что я обычно делаю в этом случае, это передать параметры конструктора в конкретных классах. Так что я бы изменить свои конкретные формы, чтобы что-то вроде:

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

и так далее

Теперь, я хотел бы использовать фабричный метод, так что ваша ткань теперь будет, как:

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

Обратите внимание, что теперь фабрика знает, как построить форму с конструктором, переданным в своем собственном конструкторе.

Итак, каждый раз, когда вам нужна другая форма, вы создадите новую фабрику.

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

Я думаю, что ответ на ваш 1-й и 2-й вопросы.

Итак, 3: Что вы можете сделать, чтобы изменить DonT вашего класса использовать шаблон стратегии для того, чтобы пройти во время выполнения, как реализовать этот метод:

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

Надеется, что это помогает с вашей тренировкой.

+0

Спасибо за ваш вклад. Очень ценю это. Еще один вопрос: Каково использование и тело класса ShapeFactory. Вы не упомянули об этом в своем ответе. Вы используете мой общий класс, или это какая-то другая ваша. Я вижу CircleFactory и RectangleFactory как конкретный класс. –

+0

oh, я забыл этот класс ... этот класс является абстрактным классом с простым абстрактным методом Create() .. обновлено выше – guijob

+0

Я думаю, что в вашем коде есть несколько ошибок. Например, я думаю, что вы имели в виду 'public int Circunference()' (иначе это выглядит как конструктор), и я думаю, вы хотите, чтобы он «возвращал» этот расчет. Также метод 'IPerimeter' должен быть' int calculatePerimeter (Круг круга); '. Наконец, этот метод должен возвращать (int) (2 * Math.PI * circle.Radius); '. Кстати, есть несколько «длин». ;) – Andrew

0

Для меня ваш пример кажется действительно переработанным. Я думаю, что вы всегда должны реализовывать простейшую вещь, которая больше ничего не работает. Я знаю, что это примерный код, потому что вы хотите изучить принципы SOLID, но я думаю, важно знать, как ужасно ошибочные могут следовать этим принципам в неправильном контексте. В вашем конкретном коде: нужно ли группировать все фигуры с помощью класса Shape? Я имею в виду, вы когда-нибудь планировали перебирать список фигур и вычислять площадь и объем для них? Если нет, наследование абсолютно не имеет смысла. На самом деле я бы сказал, что наследование чрезмерно используется в наши дни, и когда оно чрезмерно используется, вы получаете уродливые графики зависимостей наследования. Что касается фабричного класса: Является ли строительство любого из ваших «фигурных» объектов особенно сложным, трудоемким, сложным. Отличается ли ваш заводский класс какой-то ценностью или он абсолютно бесполезен? Если у него нет реальной причины существовать, я бы его не использовал, новый оператор намного яснее.

Надеюсь, вы не возражаете против моего ответа, но я просто хотел, чтобы вы знали о том, что некоторые SOLID-принципы применяются в очень специфических сценариях. Принуждение их к неправильным местам может привести к уродливому и сложному коду. В некоторых ситуациях в реальном мире, если на приведенные выше вопросы дано «да», ваш шаблон выглядит «ОК». В противном случае точно такая же картина может чрезмерно усложнить ситуацию без каких-либо реальных преимуществ. Думаю, моя точка зрения: просто будьте в курсе, не каждый принцип SOLID хорош в любой ситуации :).

0

Это очень распространенная проблема. В то время как обучение SOLID хорошо, оно требует понимания базовых принципов проектирования, таких как абстракция и косвенность. Причина, по которой вас путают, состоит в том, что в вашем коде отсутствует абстракция.

Представьте, что у вас есть код, который хочет знать область фигуры, но не имеет значения, какая она фигура и как рассчитать площадь этой фигуры. Что-то вроде:

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

Это КРИТИЧЕСКАЯ ЧАСТЬ ООП. В вашем примере это абсолютно ничего. Ваш пример - просто надуманный фрагмент кода, который не имеет к нему никакой логики, не говоря уже о SOLID.