很难准确地说出您要做什么,但是您的示例代码会违反 Liskov 替换原则。您的渲染方法应该适用于传入的每个形状。因此,如果稍后添加平行四边形,则无需更改页面逻辑。它应该可以工作,因为它是一个形状。
下面的代码应该可以帮到你。
public interface IShapeRenderer
{
void DrawLine(int x1, int y1, int x2, int y2);
}
public abstract class Shape
{
public abstract void Render(IShapeRenderer renderer);
}
public class Triangle: Shape
{
public override void Render(IShapeRenderer renderer)
{
// draw 3 lines
}
}
public class Square: Shape
{
public override void Render(IShapeRenderer renderer)
{
// draw 4 lines
}
}
public class HtmlRenderer: IShapeRenderer
{
public void DrawLine(int x1, int y1, int x2, int y2)
{
// draw html line
}
}
那么您的渲染外观可能如下所示:
var renderer = new HtmlRenderer();
foreach (var shape in shapes)
{
shape.Render(renderer);
}
现在,您创建的每个形状只需要知道如何使用渲染器提供的指令来创建自己。您可以渲染 Html 或图像或 ascii 艺术,只要您为其创建渲染器即可。
另一种选择是做类似于GetEnumerator() 在集合上的工作方式,并包含一个知道如何进行绘图的内部私有类。然后您可以向GetDrawInstructions() 询问形状并使用渲染器。这样,您的形状和绘图说明在不同的类中是不同的关注点。只是其中一个嵌套在另一个中。
public interface IDrawInstructions
{
void Draw(IShapeRenderer renderer);
}
public abstract class Shape
{
public abstract IDrawInstructions GetDrawInstructions();
}
public class Triangle: Shape
{
public override IDrawInstructions GetDrawInstructions()
{
return new TriangleDrawInstructions(this);
}
private class TriangleDrawInstructions: IDrawInstructions
{
public Triangle Triangle { get; private set; }
public TriangleDrawInstructions(Triangle triangle)
{
Triangle = triangle;
}
public void Draw(IShapeRenderer renderer)
{
// draw 3 lines using information from triangle
}
}
}
var renderer = new HtmlRenderer();
foreach (var shape in shapes)
{
var instructions = shape.GetDrawInstructions();
instructions.Draw(renderer);
}
最后一个选择是使用 DrawInstructionsRegistry,并在注册表中注册每个形状的绘图指令,然后在需要时调用它。
public class ShapeDrawInstructionsRegistry
{
private static Dictionary<Type, IDrawInstructions> _registry = new Dictionary<Type, IDrawInstructions>();
public static void Register<T>(IDrawInstructions instructions) where T: Shape
{
var type = typeof (T);
if(_registry.ContainsKey(type))
_registry[type] = instructions;
else
{
_registry.Add(type, instructions);
}
}
public static IDrawInstructions Lookup(Shape shape)
{
var type = shape.GetType();
if (!_registry.ContainsKey(type)) return null;
return _registry[type];
}
}
public class SquareDrawInstructions: IDrawInstructions
{
public void Draw(Shape shape, IShapeRenderer renderer)
{
var square = shape as Square;
if (square == null) throw new Exception();
// draw 4 sides
}
}
// in your global.asax.cs or bootstrapper class
ShapeDrawInstructionsRegistry.Register<Square>(new SquareDrawInstructions());
ShapeDrawInstructionsRegistry.Register<Triangle>(new TriangleDrawInstructions());
// your loop
var renderer = new HtmlRenderer();
foreach (var shape in shapes)
{
var instructions = ShapeDrawInstructionsRegistry.Lookup(shape);
instructions.Draw(shape, renderer);
}
这些其他选项可能会解决您的问题,但我认为这可能比它的价值更麻烦。