Oop 具有空子类的类继承

Oop 具有空子类的类继承,oop,object,inheritance,Oop,Object,Inheritance,假设我有一个建模费用项目的类,名为expense。我们有交通费、食物费和工资费。假设所有属性都是相同的,因此只有一个带有“类型”属性的费用类别。但是,假设我添加了一种新类型的费用,称为培训,这需要一些关于培训类型、地点和发生日期的新字段。此外,还有一些新的方法专门用于此培训费用。所以在这一点上,我需要对Expense类进行子类化,这样我就可以有一个TrainingExpense类。由于子类现在正在定义费用的类型,这是否使得在费用基类中有一个“type”属性变得多余?我现在是否应该为每种不同的类型

假设我有一个建模费用项目的类,名为expense。我们有交通费、食物费和工资费。假设所有属性都是相同的,因此只有一个带有“类型”属性的费用类别。但是,假设我添加了一种新类型的费用,称为培训,这需要一些关于培训类型、地点和发生日期的新字段。此外,还有一些新的方法专门用于此培训费用。所以在这一点上,我需要对Expense类进行子类化,这样我就可以有一个TrainingExpense类。由于子类现在正在定义费用的类型,这是否使得在费用基类中有一个“type”属性变得多余?我现在是否应该为每种不同的类型对Expense基类进行子类化?或者我应该将“type”属性保留在基类中,让它对任何子类都是多余的吗?

这里我将使用composition而不是继承。在基类中创建类型为interface
IEExpenseExtension
的属性sayExpenseExtension

对于具有额外属性/方法的费用类型,从IExpenseExtension继承并添加所需的额外属性/方法。在TravelExpense案例中,它将有一个
TravelExpenseExtension
类,该类继承自IEExpenseExtension,具有您需要的额外属性/方法

在基类中,基于
ExpenseType
属性实例化相应的
ExpenseExtension

在这里使用组合而不是继承将使它更加灵活

我还建议您创建一个包含所有类型的枚举,并将其用于ExpenseType属性

查看下面的Url以了解组合与继承:

从链接: 将对象组合优先于类继承有助于封装每个类并将其集中在一个任务上您的类和类层次结构将保持较小,并且不太可能成长为无法管理的怪物。

更新: 请参见以下C#中的代码示例:


另一种方法可能是定义一个IExpense接口,然后实现,这样所有的“标准”行为都可以通过接口访问,而特殊行为则可以通过类访问。构图方法与blacktie24相似,但相反。这一切都取决于您将如何访问额外的内容。

Thx,我非常喜欢这种解决方案。但是对于额外的方法,我会怎么做呢?同样的事情。将其添加到ExpenseExtension类。使用名称扩展而不是字段,所以称之为IEExpenseExtension和TravelExpenseExtension。我刚刚更新了答案,说扩展而不是字段,但你可以选择你喜欢的名称。因为你也可以直接实例化Expense,它不是一个抽象类,所以使用组合更有意义。(尽管这条规则并不总是适用)。使用继承将使场景中的事情复杂化。继承在其他地方更好,比如说你有一个AnimalBase(抽象类),并且有哺乳动物和鸟类作为子类。is-a vs HAS-a规则是决定是否使用继承vs合成的好规则,但是你应该根据你的情况来解释它。正如您所解释的,费用有支出在您的情况下,扩展比培训费用更合理。
public class Expense
{
    public string Food { get; set; }

    public ExpenseType Type { get; set; }

    private IExpenseExtension expenseExtension;

    public IExpenseExtension ExpenseExtension
    {
        get
        {
            if(expenseExtension == null)
            {
                // Logic to instantiate the correct Extension based on Type property.
                // You can use Factory design pattern or something like that for this as well.
            }

            return expenseExtension;
        }
    }
}

public interface IExpenseExtension
{

}

public class TrainingExpenseExtension : IExpenseExtension
{
    public string Location { get; set; }

    public void GetTrainingDetails()
    {

    }
}