这是不是违反了我的 SOLID 原则?

Posted

技术标签:

【中文标题】这是不是违反了我的 SOLID 原则?【英文标题】:Is this breaking my SOLID Principle?这是否违反了我的 SOLID 原则? 【发布时间】:2016-06-29 04:59:21 【问题描述】:

我正在尝试使用 SOLID 原则学习更好的编程实践。在这里,我正在研究 Shapes 的示例应用程序。我只是想知道,我是否在任何地方都违反了原则。下面是类及其代码。

1.基类 - 形状

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.返回 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;
    

到这里一切看起来都很好,看起来不错,但是当我实现它时出现了问题。我在这里有点困惑。先看前端代码:

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

问题

    不同的形状有不同的属性,比如圆形有半径,三角形有底和高等等,所以我决定将我的属性保留在子类中。我知道,我可以将它作为我的基类中的虚拟成员。那么除了上面的编码还有什么办法吗。

    如果不是,那么抽象类有什么用,如果我仍然将我的 Shape 对象类型转换为圆形对象?我可以简单地使用 Circle c = new Circle()。我不想要不需要的检查,比如(如果 c 是圆形)等等。

    如果,我被要求实施一种新方法来获取圆的周长。我是否需要创建一个新的 Abstract 类或将其放在 Circle 类中。但如果我把它放在 Circle 上,我认为它会打破 SOLID 的第一原则,即 SRP . 请注意,我的抽象类不是具有不必要或重复属性的胖类。

提前致谢

【问题讨论】:

codereview.stackexchange.com 更适合这个问题 【参考方案1】:

在这种情况下,我通常做的是在具体类中传递构造函数参数。因此,我会将您的具体形状更改为:

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

我认为这回答了您的第一个和第二个问题。

所以,3: 你可以做的是不修改你的类是使用策略模式以便在运行时传递如何实现这个方法:

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; 

希望这对您的培训有所帮助。

【讨论】:

感谢您的意见。真的很感激。还有一个问题,ShapeFactory 类的用途和主体是什么。您在回答中没有提到这一点。您是在使用我的通用类还是您自己的其他类。我可以将 CircleFactory 和 RectangleFactory 视为具体类。 哦,我忘记了这个类...这个类是一个抽象类,有一个简单的抽象方法 Create().. 上面更新了 我认为您的代码中存在一些错误。例如,我认为您的意思是public int Circunference()(否则它看起来像一个构造函数),我猜您希望它用于return 那个计算。同样IPerimeter 的方法应该是int calculatePerimeter(Circle circle);。最后,这个方法应该return (int)(2 * Math.PI * circle.Radius);。顺便说一句,还有一些lenght。 ;)【参考方案2】:

    不同的子类会有不同的属性,这是可以预料的。通常,并非所有派生类都具有与其基类完全相同的属性。没有理由强迫Shape 拥有Radius。你会有什么优势?这只是为麻烦打开了大门。您对此的最终目标是什么?有类似myShape.Dimension = value 的东西而不关心它是半径、边等吗?任何事情都可以完成,这取决于您的需要。

    例如,使用您的抽象类,您可以循环遍历Shape 的列表并调用Area()Volume(),知道您将得到您的结果(尽管您仍未实现Volume)。此外,您的基类可能有一些通用代码,在这种情况下您没有使用这些代码。例如,您可以有一个 Unit 属性,它可以是厘米、英寸、米等,然后有一个像这样的方法(愚蠢的例子):

    public string GetAreaString()
    
        return string.Format("0 1", this.Area().ToString(), this.Unit);
    
    

    当然,只需在Circle 中实现即可。为什么会破坏Circle 的单一职责?你的班级正在处理它的计算 相关值,就像 string 告诉您它是 null 还是它的长度。

【讨论】:

【参考方案3】:

对我来说,你的例子似乎真的过度设计了。我认为您应该始终实现最简单的东西,仅此而已。我知道这是一个示例代码,因为您想学习 SOLID 原则,但我认为重要的是要意识到这些原则在错误的上下文中会发生多么可怕的错误。在您的特定代码中:您是否需要使用 Shape 类对所有形状进行分组?我的意思是,你有没有打算遍历一个 Shapes 列表并计算它们的面积和体积?如果没有,继承绝对没有意义。事实上,我会说这些天继承被过度使用了,当它被过度使用时,你最终会得到丑陋的继承依赖图。关于工厂类:构建任何“形状”对象是否特别困难、耗时、棘手。你的工厂类是提供了一些价值还是完全没用?万一它没有真正存在的理由,我不会使用它,新的运算符要清楚得多。

希望您不要介意我的回复,但我只是想让您了解一些 SOLID 原则适用于非常具体的场景这一事实。将它们强制放在错误的位置可能会导致代码丑陋且过于复杂。在某些现实世界的情况下,如果上述问题的回答是肯定的,那么您的模式似乎没问题。否则,完全相同的模式会使事情变得过于复杂而没有任何真正的好处。我想我的观点是:请注意,并非每个 SOLID 原则在任何情况下都是好的:)。

【讨论】:

【参考方案4】:

这是一个非常普遍的问题。虽然学习 SOLID 很好,但它需要了解抽象和间接等基本设计原则。您之所以感到困惑,是因为您的代码中没有抽象。

假设您的代码想知道形状的面积,但它并不关心它是什么形状,也不关心如何计算该形状的面积。类似的东西:

public void PrintArea(Shape shape)

    Console.WriteLine(shape.Area());

这是 OOP 设计的关键部分。你的例子完全没有这个。您的示例只是一段没有逻辑的人为代码,更不用说是 SOLID 了。

【讨论】:

以上是关于这是不是违反了我的 SOLID 原则?的主要内容,如果未能解决你的问题,请参考以下文章

Facade模式是否违反SOLID原则?

SOLID - 违反开闭原则

如何设计遵循 SOLID 的类而不在其他地方加载违反 SOLID 的内容?

每个开发者都应该知道的SOLID原则

为啥弱化先决条件不违反里氏替换原则

依赖倒置原则|SOLID as a rock