【问题标题】:The correct way to change the base class from derived class从派生类更改基类的正确方法
【发布时间】:2017-08-22 21:26:18
【问题描述】:

我正在尝试设计几个基本绘图类,我可以从中继承和定义复杂的绘图。

如您所见,LineDrawingCompoundDrawing 派生自一个抽象的绘图类。 CompoundDrawing 有一个内部List<Drawing>,它允许我们存储任意数量的Drawing 对象并定义更复杂的图形。

public abstract class Drawing
{       
    public bool CanBeRotated 
    {
       get; 
       set;
    }

    private float m_rotation;
    public float Rotation
    {
        get 
        { 
           return m_rotation;
        }
    }

    protected Drawing()
    {
        CanBeRotated = true;
    }

    public void Rotate(float degree)
    {
        if (CanBeRotated)
            m_rotation = degree;
    }
}

public sealed class LineDrawing : Drawing
{
    private readonly Line m_line;

    public Line Line
    {
        get
        {
           return m_line;
        }
    }

    public LineDrawing(Line line)
    {
        m_line = line;
    }
}

public class CompoundDrawing : Drawing
{
    protected IList<Drawing> m_drawings;

    protected CompoundDrawing(IList<Drawing> drawings)
    {
        m_drawings = new List<Drawing>(drawings);
    }
}

假设我想定义一个派生自 CompoundDrawing 的 RectangleDrawing。

public class RectangleDrawing : CompoundDrawing
{
    public RectangleDrawing(IList<LineDrawing> lineDrawings) : base(lineDrawings)
    {
        foreach(var line in lineDrawings)
        {
            line.CanBeRotated = false;
        }
    }    
}

现在我在这里遇到了一个问题!显然,我不希望这个新类中的 LineDrawing 对象是可旋转的!但我不确定我应该在这个模式的哪个位置设置它!

【问题讨论】:

  • 所以您不想旋转RectangleDrawing 或组合线段?
  • @BradleyDotNET 线段!
  • 我猜你只需要对lineDrawings 做一个foreach 并设置他们的CanBeRotated 属性。或者改变你的继承模型,让每个类要么总是可旋转的,要么不能旋转。
  • 那又怎样?你还在作曲。如果您的基本问题是“派生类可以接触基类成员”,那么答案是响亮的
  • 您仍然需要旋转线条才能旋转矩形。如果您将该属性设置为false,那么您需要在旋转矩形时再次将其临时重置为true。我认为你的整个设置太复杂了,它对你不利而不是帮助。

标签: c# inheritance design-patterns


【解决方案1】:

我会将 Rotate 移到它自己的界面,因为它不适用于所有类型的绘图。

public interface IRotate
{
    void Rotate(float degree);
}

public class SquareDrawing : Drawing, IRotate
{
    public void Rotate(float degree)
    {
         //actual code to rotate the drawing
    }
}

public class RectangleDrawing : CompoundDrawing
{
     public RectangleDrawing(IList<LineDrawing> lineDrawings) : base(lineDrawings)
     {
        foreach(var line in lineDrawings)
        {
            if (line is IRotate)
            {
                 ((IRotate)line).Rotate(45.0);
            }
        }
     }    
}

【讨论】:

    【解决方案2】:

    我会考虑采用不可变的方法:

    public abstract class Drawing
    { 
        //some shapes might not be rotable or rotation
        //simply doesn't make sense: circle      
        public virtual bool CanBeRotated => true;
    
        public Drawing Rotate(float degree)
        {
            //Don't mutate this, return a new
            //rotated instance.
            if (!CanBeRotated) return this;
            return rotate(float);
        }
    
        //let each concrete type handle its own rotation.
        protected abstract Drawing rotate(float);
    
        //etc.
    }
    

    不变性能给你带来什么?好吧,谁在乎是否有人试图旋转复合对象的单个线条?它们不会改变任何重要的东西,因为没有任何东西可以改变,它们只会获得不属于原始复合对象的旋转线。

    CompoundDrawing 的旋转逻辑会相当简单:

    protected override Drawing rotate(float degeee)
       //Note I'm passing in an IEnumerable<Line> instead of an IList<>
       //Why the need of an IList<>, don't burden the API with unecessary 
       //constraints.
        => new Rectange(lines.Select(l => l.Rotate(degree)));
    

    说了这么多,我建议您将构建块更改为PointLine 似乎是一个奇怪的选择。这样,所有形状都是按特定顺序由点组成的复合图形。绘制任何形状只是将点按顺序连接起来,旋转只是旋转每个点,等等。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-31
      • 1970-01-01
      • 2012-04-28
      • 1970-01-01
      • 2014-03-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多