Трудно точно сказать, что вы пытаетесь сделать, но ваш пример кода будет нарушать принцип замены Лискова. Ваш метод рендеринга должен работать с каждой формой, которую передают. Поэтому, если позже вы добавите Parallelogram, вам не нужно менять логику страницы. Это должно просто работать, потому что это форма.
Что-то вроде приведенного ниже кода должно вам помочь.
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 art, если вы создадите для них средство визуализации.
Другой вариант - сделать что-то похожее на то, как 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);
}
Эти другие варианты могут решить вашу проблему, но я думаю, что это, вероятно, больше проблем, чем стоит.