【问题标题】:How to handle composed classes in C#如何在 C# 中处理组合类
【发布时间】:2018-02-01 06:11:48
【问题描述】:

我要问的部分与设计模式有关。

假设我有一个 IDrawing 界面。 另外两个名为 TextDrawingShapeDrawing 的基本类实现了这一点,我有一个知道如何绘制它们的 View 类!

但我有更复杂的绘图类,它们也实现了 IDrawing 接口,但它们本身由几个 IDrawing 类组成!

如何在我的 View 类中绘制这些?显然,教 View 类绘制每个新的 IDrawing 并不是一个好主意!但我还有什么其他选择?也许设计不正确?如何告诉 View 的 Draw 方法知道复杂类的原始部分并绘制它们?

   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() 方法的建议。 View 中的 Draw 方法依赖于外部库进行绘制(在我的例子中,它是 SkiaSharp 库)。如果我在这些类中实现 Draw,它们将不再是通用的!例如,我将无法在其他项目中使用它们,因为我有不同的策略来绘制这些东西。

【问题讨论】:

  • 阅读策略模式,并研究颠倒绘图的责任。
  • 向你的界面添加一个绘图方法,让每个类自己处理绘图。
  • @Nkosi 你能看看我的更新吗?
  • @Nkosi 我现在明白了!这太强大了!
  • @Vahid 很高兴你知道了。请记住,强大的力量伴随着巨大的责任。负责任地使用这种力量。

标签: c# inheritance design-patterns interface composition


【解决方案1】:

我会在接口中添加一个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);
    }
}

【讨论】:

  • @Vahid 完成。我已经抽象出对SkiaSharp 的依赖
  • 感谢 phuzi 的完整解释。
【解决方案2】:
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.

【讨论】:

  • 您说,“View 中的 Draw 方法依赖于外部库进行绘制(在我的情况下,它是 SkiaSharp 库”。但现在它对绘图策略一无所知。它们是多态绑定的.
【解决方案3】:

您可以选择强制类实现名为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(); 的调用将导致正确的实现。

如何告诉 View 的 Draw 方法知道复杂类的原始部分并绘制它们?

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

public class SignDrawing : IDrawing
{
    public TextDrawing Text { get; set; }
    public ShapeDrawing Border { get; set; }

    public void Draw()
    {
        Text.Draw();

        Border.Draw();

        Text.Draw();
    }
}

【讨论】:

  • 感谢 Mong,View 中的 Draw 方法依赖于外部库进行绘制(在我的例子中,它是 SkiaSharp 库)。如果我在这些类中实现 Draw,它们将不再是通用的!例如,我将无法在其他项目中使用它们,因为我有不同的绘制事物的策略。
  • @Vahid 你不希望文本绘制总是以相同的方式绘制吗? “将军”到底是什么意思?
  • 例如,我现在使用 SkiaSharp(第 3 方库)来绘制我的绘图。如果我将 Draw() 方法移动到绘图类,那么它们将依赖于这个特定的库,然后如果我决定使用例如 WPF 本身来可视化这些绘图,我就会遇到问题!
猜你喜欢
  • 1970-01-01
  • 2019-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多