如何在 C# 中处理组合类



我在问一个关于设计模式的问题。我有一个IDrawing接口,以及两个名为TextDrawingShapeDrawing的类来实现此接口。我还有一个View课,知道如何绘制这些类。但是,我有更复杂的绘图类,它们也实现了IDrawing接口,但由其他几个IDrawing类组成。

我不确定如何在View课上绘制这些复杂的类。我不想教View班如何绘制每个新的IDrawing类,因为这不是一个好的设计。我还有哪些其他选择?我的设计是否不正确,如果是,我如何告诉ViewDraw方法来了解复类的原始部分并绘制它们?这是我的代码:

public interface IDrawing
{
}
public class TextDrawing : IDrawing
{
}
public class ShapeDrawing : IDrawing
{      
}
public class SignDrawing : IDrawing
{
public TextDrawing Text { get; set; }
public ShapeDrawing Border { get; set; }
}
public class MoreComplexDrawing : IDrawing
{
public TextDrawing Text { get; set; }
public ShapeDrawing Border1 { get; set; }
public ShapeDrawing Border2 { get; set; }
}
public class View
{
public void Draw(IDrawing drawing)
{
// The View only knows how to draw TextDrawing and ShapeDrawing.
// These as the primitive building blocks of all drawings.
// How can it draw the more complex ones!
if (drawing is TextDrawing)
{
// draw it
}
else if (drawing is ShapeDrawing)
{
// draw it
}
else
{
// extract the drawings primitive parts (TextDrawing and ShapeDrawing) and draw them!
}
}
}

更新:

我收到了在我的绘画类中实现Draw()方法的建议,但如果我这样做,它们将不再是通用的。例如,我将无法在具有不同绘图策略的其他项目中使用它们。有没有办法让我的设计更灵活并保持其通用性?

我会在接口中添加一个Draw()方法,并在您的每个类中实现它。

这样做的好处是,您的View不在乎实际类型是什么。

public interface IDrawing
{
void Draw();
}
public class TextDrawing : IDrawing
{
public void Draw()
{
// Draw a TextDrawing
}
}
public class ShapeDrawing : IDrawing
{      
public void Draw()
{
// Draw a ShapeDrawing
}
}
public class SignDrawing : IDrawing
{
public TextDrawing Text { get; set; }
public ShapeDrawing Border { get; set; }
public void Draw()
{
// Draw a SignDrawing
}
}
public class MoreComplexDrawing : IDrawing
{
public TextDrawing Text { get; set; }
public ShapeDrawing Border1 { get; set; }
public ShapeDrawing Border2 { get; set; }
public void Draw()
{
// Draw a MoreComplexDrawing
}
}
public class View
{
public void Draw(IDrawing drawing)
{
//Draw the drawing
drawing.Draw();
}
}

更新 - 抽象出对SkiaSharp的依赖

您需要为实际执行绘图的SkiaSharp或任何外部依赖项创建一个包装器。这应该与 IDrawing 接口和派生类存在于同一个程序集中。

public interface IDrawingContext
{
// Lots of drawing methods that will facilitate the drawing of your `IDrawing`s
void DrawText(...);
void DrawShape(...);
void DrawBorder(...);
}

以及具体实施SkiaSharp

public class SkiaSharpDrawingContext IDrawingContext
{
// Lots of drawing methods that will facilitate the drawing of your IDrawings
public void DrawText(...) { /* Drawing code here */ }
public void DrawShape(...) { /* Drawing code here */ }
public void DrawBorder(...) { /* Drawing code here */ }
}

IDrawing接口更新为

public interface IDrawing
{
void Draw(IDrawingContext drawingContext);
}

也更新您的类以反映此更改。类将在IDrawingContext实现上调用方法来执行绘图。

在应用程序中创建特定于依赖项的实现,并更新View类以使用新SkiaSharpDrawingContext

public class View
{
public void Draw(IDrawing drawing)
{
// This should ideally be injected using an IOC framework
var drawingContext = new SkiaSharpDrawingContext(...);
//Draw the drawing
drawing.Draw(drawingContext);
}
}

您可以选择强制类实现名为Draw()的方法。

public interface IDrawing
{
void Draw(); 
}

现在,您可以简单地调用此方法,并将内部嵌入的责任交给类自己。

public void Draw(IDrawing drawing)
{
drawing.Draw();

例如,TextDrawing将被迫实现此方法

public class TextDrawing : IDrawing
{
public void Draw()
{
// draw Text-like
}
}

View类中,对drawing.Draw();的调用将导致正确的实现。

如何告诉视图的 Draw 方法了解复杂类的基元部分并绘制它们?

现在,在复杂类中,您只需使用已在简单绘图类中实现的方法

public class SignDrawing : IDrawing
{
public TextDrawing Text { get; set; }
public ShapeDrawing Border { get; set; }
public void Draw()
{
Text.Draw();
Border.Draw();
Text.Draw();
}
}
you can polymorphically draw without checking the type.
public class View
{
public void Draw(IDrawing drawing)
{
drawing.Draw();
}
}
Encapsulate each drawing strategy (Strategy Pattern) in different class and you could inject each strategy via DI.