将对象放在通用抽象类概念中的哪个位置?

时间:2017-02-20 11:05:41

标签: c# generics abstract-class conceptual

注意:请参阅下面的更新,因为我根据第一个答案+评论

更改了原始概念

如果这是一个微不足道的问题,或者它完全取决于程序员的偏好,我很抱歉。因为我不确定e的真正优点或缺点。 G。清晰度或KISS,我问这个问题。

在抽象类概念中,我应该在哪里放置实际对象? (我应该更喜欢BaseObject还是DerivedPrivateObject

public abstract class BaseClass<TBase>
{
    // Should we manage the generic object in the base class?
    protected TBase BaseObject = null;

    // Or should we access it via abstract property?
    protected abstract TBase DerivedObject { get; }

    // The derived implementation has to create the object
    protected abstract TBase CreateObject();

    public void DoSomethingGeneric()
    {
        // Base may directly use its base object
        BaseObject.GenericMethod();

        // Otherwise it has to access the derived property every time
        DerivedObject.GenericMethod();
    }
}

public class DerivedClass : BaseClass<TSpecialized> where TSpecialized: TBase
{
    // Should we manage the specialized object here in the derived class?
    private TSpecialized DerivedPrivateObject = null;

    protected TBase DerivedObject
    {
        get
        {
            return DerivedPrivateObject;
        }
    }

    protected TBase CreateObject()
    {
        DerivedPrivateObject = new DerivedObject();

        // We may additionally store this into the base object, but isn't this ugly?
        BaseObject = DerivedPrivateObject;

        return DerivedPrivateObject;
    }

    public void DoSomething()
    {
        // We can directly use the derived object type
        DerivedPrivateObject.SpecializedMethodNotInBaseType();

        // Otherwise we would have to convert
        (BaseObject as TSpecialized).SpecializedMethodNotInBaseType();
    }
}

David Culp的答案+评论后更新(3)

请注意更新3:正如我所提到的,我尝试简化课程以专注于实际问题。但正如两位评论员都正确地指出的那样,我对此失败了。简化导致错误的代码和不正确的概念。因此,我将下面的代码结构扩展到了实际的实现,我希望这仍然是可以理解的。对于由错误的简化引起的任何混淆,我感到非常抱歉!

David Culp对LSP和DRY的引用让我更好地理解了自己的问题。这不仅摧毁了我对花生爆发的怀疑。

但是,这也让我想出了一个可接受的解决方案:

public abstract class BaseClass<TBase> : ISomeInterface where TBase : class
{
    // We manage the generic object in the base class for allowing generic usage
    protected OtherBaseClass<TBase> BaseObject = null;

    // The derived implementation has to create the object
    protected abstract OtherBaseClass<TBase> CreateObject();

    public void Initialize()
    {
        // Let the derived class create the specialized object, but store it back into here, for generic usage
        BaseObject = CreateObject();
    }

    public void DoSomethingGeneric()
    {
        // Base may directly use the base object
        BaseObject.GenericMethod();
    }
}

public class DerivedClass : BaseClass<ISpecialized>
{
    private SpecializedType DerivedObjectAccessor
    {
        get
        {
            return BaseObject as SpecializedType;
        }
    }

    protected override OtherBaseClass<ISpecialized> CreateObject()
    {
        // Create the derived object, but let base class handle the assignment to BaseObject, for clarity reasons
        return new SpecializedType();
    }

    public void DoSomething()
    {
        // We may use specialized stuff via local accessor property
        DerivedObjectAccessor.SpecializedMethodNotInBaseType();
    }
}

只是为了澄清:

public class SpecializedType : OtherBaseClass<ISpecialized>, ISpecialized
{
    // foreign implementation
}

这是一个关于清晰度和风格的可接受的解决方案吗?

是否存在反对protected abstract void CreateObject();不返回对象的参数,让派生类将其分配给BaseObject(容易出错?) - 我想让它保持开放CreateObject()已经在构造函数中调用或通过公共可用的create方法调用,但我不会省略抽象方法,让派生逻辑知道必须实现它。

(更新2:由于上面的内容很糟糕,我改变了创建对象处理)

要点: 是否有反对使用属性(DerivedObjectAccessor)进行对通用基础对象的本地转换访问的原则?

1 个答案:

答案 0 :(得分:2)

归结为Liskov Substitution PrincipleDRY之间的权衡。 LSP表示不强制派生类实现他们不需要的任何东西,而DRY表示不会多次编写相同的代码。

因此,最好的经验法则,将它放在类层次结构中尽可能高(最通用),因为它可以去并且仍然被所有派生类使用。

编辑后更新

主要混淆似乎是希望在类层次结构和属性类型中使用相同的类型参数(ISpecialized),同时确保它们保持同步。您可能会发现这些方面的内容更清晰。

public class SpecializedType : OtherBaseClass<ISpecialized>, ISpecialized
{
    // foreign implementation
}

public abstract class BaseClass<TBase> : ISomeInterface
    where TBase : OtherBaseClass<ISpecialized>
{
    protected TBase BaseObject = null;

    protected abstract TBase CreateObject();

    public void Initialize()
    {
        BaseObject = CreateObject();
    }

    public void DoSomethingGeneric()
    {
        BaseObject.GenericMethod();
    }
}

public class DerivedClass : SpecializedType 
{
    protected DerivedClass  CreateObject()
    {
        return new DerivedClass();
    }

    public void DoSomething()
    {
        DerivedObjectAccessor.SpecializedMethodNotInBaseType();
    }
}

有一些变化

  • 没有传入type参数,两个泛型类型与SpecializedType同步,只是命名DerivedClass作为CreateObject声明中的参数,同时将该专用类型的处理留给&#39; DerivedClass&#39 ;.
  • 删除了什么都没做的方法。
  • {{1}}成为班级的基本工厂方法

我希望我能跟上编辑的步伐,但如果我在某个地方偏离轨道,请告诉我。 (我还要求在评论中澄清。)