为什么要使用抽象类作为类型?

时间:2017-03-23 07:22:15

标签: c# interface abstract-class

我对接口/抽象类/类有相当的了解,但只是想了解其他内容。请看下面的代码:

namespace AbstractClassExample
{
    class Program
    {
        static void Main(string[] args)
        {
            BaseEmployee fullTimeEmployee = new FullTimeEmployee();

            BaseEmployee contractEmployee = new ContractEmployee();
        }
    }

    public abstract class BaseEmployee
    {
        public string EmployeeID { get; set; }
        public string EmployeeName { get; set; }
        public string EmployeeAddress { get; set; }

        public abstract double CalculateSalary(int hoursWorked);
    }

    public class FullTimeEmployee : BaseEmployee
    {
        public override double CalculateSalary(int hoursWorked)
        {
            //do something
        }
    }

    public class ContractEmployee : BaseEmployee
    {
        public override double CalculateSalary(int hoursWorked)
        {
            //do something
        }
    }
}
然而,我没有达到线下(第一种方法):

BaseEmployee fullTimeEmployee = new FullTimeEmployee();
BaseEmployee contractEmployee = new ContractEmployee();

为什么不这样写(第二种方法):

FullTimeEmployee fullTimeEmployee = new FullTimeEmployee();

完全可以使用第二种方法,它将起作用关系。如果上面的抽象类在DLL中,那么工作中的任何开发人员将如何知道。当您与您或某种文档进行编码时,可能会使用第一种方法。不是吗?

类似的例子也适用于接口声明。像:

interface IPointy {
    void MyMethod();
}

class Pencil : IPointy {
    void MyMethod() {
    }

    void MyOtherMethod() {
    }
}

IPointy itPt = new Pencil();

不是第一种让它变得复杂的方法吗?什么是好的做法?任何良好的练习与第一和第二的不良练习第二?

3 个答案:

答案 0 :(得分:0)

使用第一种方法启用多态性

假设你有一个公司类:

class Company {

}

当然,公司有全职和合同雇员。我们将它们添加为属性:

public FullTimeEmployee[] EmployeesFullTime { get; set; }
public ContractEmployees[] EmployeesContract { get; set; }

这似乎都很好。

但是,如果您的公司现在可以拥有另一种有不同方式计算薪水的员工呢?您必须添加另一个属性:

public FullTimeEmployee[] EmployeesFullTime { get; set; }
public ContractEmployee[] EmployeesContract { get; set; }
public AnotherKindOfEmployee[] EmployeesOther { get; set; }

这不好,是吗?每次添加新员工时,都必须添加其他属性!

这就是你使用BaseEmployee的原因,它不关心它拥有什么样的员工,它仍然可以计算薪水!

public BaseEmployee[] AllEmployees { get; set; }

答案 1 :(得分:0)

您将FullTimeEmployee分配给BaseEmployee的原因之一是您可以将它们放在FullTimeEmployees' and ContractEmployees`的集合中:

List<BaseEmployee> allEmployees = new List<BaseEmployee>()
{
    new FullTimeEmployee() {...},
    new FullTimeEmployee() {...},
    new ContractEmployee() {...},
    new FullTimeEmployee() {...},
}

这样做的缺点是,您无法使用({1}} FullTimeEmployee ContractEmployees没有的allEmployees功能,但如果您在处理{{1}时不需要此功能这个方法比创建两个员工集合更可取。例如,您可以编写一个适用于FullTimeEmployeesContractEmployees的函数:

private void PaySalary(List<BaseEmployee> employees)
{
    foreach (var employee in employees)
    {
        var salary = employee.CalculateSalary()
        Pay(salary, ...);
    }
}

创建面向对象设计时的一个指导原则是您应该设计变更,这意味着您的设计应该可以轻松地为您的设计添加类型,或者更改类的内部。

假设您需要一种新型员工HiredEmployees。因为你是从BaseEmployee派生的,所以你知道你可以计算他们的工资。您不必更改函数PaySalary

如果你给了FullTimeEmployee和你的ContractEmployee接口,这也会有用:

interface ISalaryReceiver
{
    double CalculateSalary(int hoursWorked);
}

class BaseEmployee
{
    public string EmployeeID { get; set; }
    ...
}

class FullTimeEmployee : BaseEmployee, ISalaryReceiver
{
    public override double CalculateSalary(int hoursWorked)
    {
        ...
    }
}

class ContractEmployee : BaseEmployee, ISalaryReceiver
{
    public double CalculateSalary(int hoursWorked)
    {
        ...
    }
}

void PaySalary(List<ISalaryReceiver> employees)
{
    ...
}

此方法可行。它甚至可以做出改变:只要它实现了ISalaryReceiver,你就可以发明任何员工。

然而!

假设您的BaseEmploye具有需要CalculateSalary的函数:

class BaseEmployee
{
    ...
    public void PaySalary()
    {
       double salary = ... // how to calculate the salary?
    }
}

您不能让BaseEmployee实施ISalaryReceiver,因为BaseEmployee不知道如何计算工资。

使用抽象方法时,您可以告诉BaseEmployeeBaseEmployee的每个对象都知道如何CalculateSalary

abstract class BaseEmployee
{
    abstract double CalculateSalary(...);
    public void PaySalary()
    {
       double salary = this.CalculateSalary(...);
       ...
    }
}

因此,如果您的基类需要每个派生类不同的函数,并且没有正确的默认功能,那么您的基类需要一个抽象函数。保证每个派生类都实现了这个函数,因此基类可以调用它。

因为在这种情况下创建基类的对象没有意义(毕竟,基类不知道如何CalculateSalary),所以基类必须被声明为abstract,结果是你不能创建基类的对象。只能创建实现CalculateSalary的派生类的对象。

答案 2 :(得分:0)

抽象类的一个好处是 - 您可以在方法中使用抽象类型作为参数类型。

假设您有一个public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; } 类,方法<div class="container"> <div class="row"> <div class="col-xs-12 col-md-6 col-lg-6">A</div> <div class="col-xs-12 col-md-6 col-lg-6">B</div> <div class="col-xs-12 col-md-12 col-lg-6">C</div> <div class="col-xs-12 col-md-12 col-lg-6">D</div> </div> </div> 在生成报告时需要计算员工薪水

Report

您不希望在需要计算工资的每种方法中使用Generate语句 因此,public class Report { public SalaryReport Generate(BaseEmployee employee) { // ... var salary = employee.CalculateSalary(); // ... } } 类并不关心if..else如何实现,它只关心Report类有这种方法。