C# Abstract class 有另一个抽象 class 对象

C# Abstract class has another abstract class object

我有几个 classes 表示一些测量数据,首先 ICut 抽象 class 派生 RoundCutSquareCut 像这样:

public abstract class ICut
{
}

public class RoundCut : ICut
{
    DoSomeWithRoundCut(){}
}

public class SquareCut : ICut
{
    DoSomeWithSquareCut(){}
}

当然它包含一些实现,但对于这个问题来说并不重要。 ICut 是抽象的 class 而不是接口,因为它本身有一些实现。

然后,这里有 classes 表示 ICut 数据集,同样是基础抽象 IRoll 和派生 RoundRollSquareRoll:

public abstract class IRoll
{
    // list of measured cuts
    public List<ICut> cuts;     

    // Create new cut
    public abstract ICut CreateCut();
}

public class RoundRoll : IRoll
{        
    public RoundRoll ()
    {
        cuts = new List<RoundCut>();
    }

    public override ICut CreateCut()
    {
        RoundCut cut = new RoundCut();
        cuts.Add(cut);
        return cut;
    }
}

public class SquareRoll : IRoll
{
    public SquareRoll()
    {
        cuts = new List<SquareCut>();
    }

    public override ICut CreateCut()
    {
        SquareCut cut = new SquareCut();
        cuts.Add(cut);
        return cut;
    }
}

现在,我当然无法通过调用直接达到 RoundCutSquareCut 额外的实现,例如:

IRoll roll = new RoundRoll();
roll.CreateCut();
roll.cuts[0].DoSomeWithRoundRoll();

我两个都不能用:

(roll.cuts[0] as RoundCut).DoSomeWithRoundRoll();

因为我一般不知道是哪个IRoll推导roll

我正在重构一个巨大的项目,其中所有 roll 个对象都是 RoundRoll 类型,现在必须添加另一个。

也许我缺少某种合适的设计模式,我正处于高级 OOP 模式学习曲线的开始,我一直在思考这个问题的解决方案。

更新 经过多次实验,我意识到与我的主要观点相反,我最终得到了@The-First-Tiger 的解决方案并进行了一些改进。我创建了简单的工厂:

// Cut factory
public static class CutFactory
{
    // Get new cut by given shape type
    public static ICut GetCut(RollShape shape)
    {
        switch (shape)
        {
            case RollShape.Round:
                return new RoundCut();
            case RollShape.Square:
                return new SquareCut();
            default:
                throw new ArgumentException();
        }
    }
}

所以我可以像这样创建剪辑:

ICut cut = CutFactory.GetCut(roll.GetShape());

如果需要有不同的行为:

if (cut is RoundCut) 
    (cut as RoundCut).RoundCutSpecificMethod();
else if (cut is SquareCut) 
    (cut as SquareCut).SquareCutSpecificMethod();

解决该问题的一种方法是在其 ICut 的类型上使 IRoll 通用:

public abstract class AbstractRoll<T> where T : ICut, new {
    // list of measured cuts
    public List<T> cuts = new List<T>();
    // Create new cut
    public T CreateCut() {
       var res = new T();
       curs.Add(res);
       return res;
    }
}

现在你可以这样做了:

public class RoundRoll : AbstractRoll<RoundCut> {
    ...
}

public class SquareRoll : AbstractRoll<SquareCut> {
    ...
}

请注意,C# 允许您通过对泛型类型应用约束将样板代码移动到基 class。

现在唯一剩下的问题是 AbstractRoll 不再是 RoundRollSquareRoll 的通用接口,因此您无法创建卷集。

这个问题可以通过在 AbstractRoll class 之上添加一个非通用接口 IRoll 来解决,其中的操作对所有 rolls 都是通用的,也是独立的卷的类型 ICut:

public interface IRoll {
    IEnumerable<ICut> Cuts { get; }
    ... // add other useful methods here
}
public abstract class AbstractRoll<T> : IRoll where T : ICut, new {
    ...
    public IEnumerable<ICut> Cuts {
        get {
            return curs.Cast<ICut>();
        }
    }
    ... // implement other useful methods here
}

您可以使用接口重写您的代码:

public interface ICut
{
   DoSomething();
}

public class RoundCut : ICut
{
    DoSomething(){}
}

public class SquareCut : ICut
{
    DoSomething(){}
}

public interface IRoll
{
    IEnumerable<ICut> Cuts { get; };      

    ICut CreateCut();
}

public class RoundRoll : IRoll
{        
    public IEnumerable<ICut> Cuts { get; private set; }

    public RoundRoll ()
    {
        this.Cuts = new List<ICut>();
    }

    public ICut CreateCut()
    {
        var cut = new RoundCut();
        this.Cuts.Add(cut);

        return cut;
    }
}

public class SquareRoll : IRoll
{
    public IEnumerable<ICut> Cuts { get; private set; }

    public SquareRoll ()
    {
        this.Cuts = new List<ICut>();
    }

    public ICut CreateCut()
    {
        var cut = new SquareCut();
        this.Cuts.Add(cut);

        return cut;
    }
}

IRoll roll = new RoundRoll();
var cut = roll.CreateCut();
cut.DoSomething();

更新

如果 SquareCut 和 RoundCut 有很多不常见的方法,您仍然可以检查 ICuts 具体类型,然后转换为它:

IRoll roll = new RoundRoll();
var cut = roll.CreateCut();

if (cut is RoundCut) {
    (cut as RoundCut).RoundCutSpecificMethod();
}
else if (cut is SquareCut) {
    (cut as SquareCut).SquareCutSpecificMethod();
}