我正在尝试使用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 。 请注意,我不把我的抽象类作为具有不必要或重复属性的胖类。
提前致谢
答案 0 :(得分: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()));
我认为这回答了你的第一和第二个问题。
所以,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; }
}
希望这有助于您的培训。
答案 1 :(得分:0)
不同的子类将具有不同的属性,这是预期的和确定的。通常,并非所有派生类都具有与其基类完全相同的属性。我没有理由强迫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
告诉您它是否为空或其长度。
答案 2 :(得分:0)
对我来说,你的例子似乎真的过于设计了。我认为你应该总是实现最简单的东西,而不是更多。我知道这是一个示例代码,因为您想学习SOLID原则,但我认为重要的是要意识到在错误的上下文中这些原则是多么可怕的错误。在您的特定代码中:您是否需要使用Shape类对所有形状进行分组?我的意思是,你有没有计划迭代形状列表并计算它们的面积和体积?如果没有,继承绝对没有意义。事实上,我会说继承现在被过度使用,当它被过度使用时,你最终会得到丑陋的继承依赖图。关于工厂类:任何“形状”物体的构造都特别困难,耗时,棘手。您的工厂类是否提供了某些价值,或者它是完全无用的?如果它没有真正的理由存在,我就不会使用它,新的运算符要清晰得多。
我希望你不介意我的回复,但我只是想让你意识到一些SOLID原则适用于非常具体的情况。强迫他们在错误的地方可能会导致丑陋和过于复杂的代码。在某些现实情况下,如果上述问题的回答是肯定的,那么您的模式似乎没问题。否则,完全相同的模式可能会使事情过于复杂而没有任何实际好处。我想我的观点是:请注意,并非所有SOLID原则在任何情况下都是好的:)。
答案 3 :(得分:0)
这是一个非常常见的问题。虽然学习SOLID很好,但它需要理解抽象和间接等基本设计原则。你感到困惑的原因是你的代码中没有抽象。
想象一下,您拥有想要了解形状区域的代码,但它并不关心它的形状以及如何计算该形状的区域。类似的东西:
public void PrintArea(Shape shape)
{
Console.WriteLine(shape.Area());
}
这是OOP设计的关键部分。你的例子绝对没有这个。你的例子只是一些没有逻辑的设计代码,更不用说是SOLID了。