我如何解释继承的用处?[关闭]


16

当试图解释OOP中的继承概念时,常见的例子通常是哺乳动物。恕我直言,这确实是一个不好的例子,因为它将导致新手以错误的方式使用此概念。而且,他们在日常设计工作中将面临的不是常见的设计。

那么,使用继承解决的一个好,简单而具体的问题是什么?


1
“常见的例子通常是哺乳动物”?你什么意思?您可以为此提供链接,参考或报价吗?
S.Lott


3
解释继承=比尔·盖茨的孩子信托基金有用的最好的真实例子是什么?
马丁·贝克特

1
@克里斯:这个问题怎么没有建设性?您是在声明一个询问者和14个答复者在浪费每个人的时间吗?
Dan Dascalescu 2015年

@DanDascalescu-它在两年前被关闭,以回应一个标语:“请考虑关闭不是建设性的:从堆积的答案来看,这看起来是一个典型的列表/民意调查问题”。如果您认为这是错误的,请对其进行编辑以清楚地表明不是,然后让社区通过重新打开审核队列进行决策。
克里斯·

Answers:


15

像哺乳动物这样的纯学术实例没有错。我也喜欢矩形/正方形示例,因为它指出了为什么现实世界中的分类法并不总是直接转换为您期望的继承关系。

我认为,最典型的日常示例是GUI工具包。这是每个人都在使用的东西,但是初学者可能没有对他们如何在后台工作进行推理。您可以谈论所有容器,所有小部件,事件等共有的行为,而无需了解任何给定实现的详细知识。


5
+1用于GUI工具包...我也喜欢shapes示例,其中一个Shape为基础,具有最小的draw(),其后代形状为定制的draw()。
yati sagade 2011年

14

我的真实示例是一个简单的HR应用程序的域模型。我告诉我们可以创建一个名为Employee的基类,因为经理当然也是雇员。

public class Employee
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public int Code { get; set; }

    public string GetInsuranceHistory()
    {
        // Retrieving insurance history based on employee code.
    }
}

然后我解释说开发人员员工测试人员员工项目经理员工。因此,他们都可以从员工类继承


2
为了显示继承优势,还可能需要展示开发人员与员工的不同之处。如果没有区别,则根本不需要创建Developer类。
大卫,

3
似乎也Employee可能是一abstract门课。
StuperUser 2011年

+1这是我大多数老师使用的示例,我非常喜欢它。这完全有意义,并提供了有关如何使用继承的真实示例。
大卫·彼得曼

18
除非这在实践中永远行不通,否则总会有至少一个人同时需要a Developer和a Tester。另一个类似的情况是您拥有Customerand 的联系人数据库Supplier,但是正如创建了这样一个系统的任何人都会告诉您的那样,总是存在a Company两者都是的情况。这就是为什么大多数这些示例都将您引向错误的方向。
Scott Whitlock

11

封装变化的内容……向他们展示一个模板方法模式,它通过将常见行为放在基类中并将变化的行为封装在子类中,展示了继承的有用性。

UI controls并且Streams也是继承的用处一个很好的例子。


我认为工厂将是一个更好的例子。
Let_Me_Be 2011年

1
@Let_Me_Be:我认为工厂与继承之间的关系本质上是间接的。当然,它会产生具体的类型并返回抽象/基本类型,但它也可能仅返回接口类型!恕我直言,这并不比经典的动物例子好。
猎鹰

@Let_Me_Be:另外,抽象工厂是一个非常复杂的示例,涉及不同的继承层次结构(一个用于项目,一个用于工厂)。我认为这是继承的很好用法,但不是一个简单的好例子。
猎鹰

3

记得

对象的每个实例都是继承有用的具体示例!

如果您专门指的是继承,那么您现在处于分类法领域,并且这些分类将因使用它们的系统目标而大不相同。动物/哺乳动物示例使用了生物学上常见且希望熟悉的分类法,但是(如您所提到的)对于绝大多数程序设计问题几乎没有用。

因此,尝试一些通用的东西:程序的概念。每个程序都将开始,运行和结束。每个程序都有一个名称和可选的命令行参数。因此,基本的Program类对于启动执行,获取和处理命令行参数,运行主逻辑以及正常关闭非常有用。

这就是为什么这么多面向对象的编程语言提供Program类或行为完全类似于Program类的原因。


那么,您编写的程序中包含很多程序?:)以我的经验,程序对象几乎总是单例,没有继承,所以恕我直言,它们不是最好的例子。
keppla 2011年

@keppla:您曾经使用过Java或.NET吗?.NET有一个显式的Program类,而Java是隐式的。他们不是单身人士
Steven A. Lowe

我使用Java,版本1.4.2左右。那时,只有静态void main,所以我猜它有所变化。有多个Programm类实例的典型原因是什么?
keppla 2011年

@keppla:java的静态void main隐式地使入口类表示程序。每个运行您的程序的用户都会创建一个新的实例。目前,我正在运行三个Google Chrome实例,四个Word文档,三个记事本和两个Windows资源管理器。如果他们都是单身人士,我将永远无法做到。
Steven A. Lowe

1
我认为您稍微扩展了定义。class Programm { public static void main(String[] args) { system.out.println('hello world'); }}是最小的Java程序。当我调用它时,没有程序的实例。程序不继承任何东西。当我启动3个进程(就像您对crhome所做的那样)时,可能会有3个程序,但是在它们各自的内存区域中,仍然只有一个程序。恕我直言,单例意味着“每个进程只有一个实例”,而不是每个机器。如果是这样,就不可能产生单例,没有什么可以阻止您两次运行任何代码。
keppla 2011年

3

我在工作时使用相机。我们有连接到不同模型的设备,因此我们有一个抽象的“相机类”,每个模型都继承自此类,以支持该相机的特定功能。这是一个真实的例子,不难理解。


2
如果有的话,这可能会崩溃,例如,既是a Camera又是a的模型Phone(就像我们现在都掏腰包一样)。它应该从哪个基类继承?还是不应该同时实现ICameraIPhone接口?(哈哈)
斯科特·惠特洛克

2
@Scott:您无法实现iPhone接口,否则将被Apple起诉。
梅森·惠勒

3

化学元素实例

这是另一个突然出现的例子:

类Element_
{
    双atomicWeight;//元素的原子量
    double atomicNumber; //元素的原子数
    字符串属性;//元素的属性
    //其他(如果有)
}


同位素扩展Element_ //元素可能存在同位素
{
    半衰期加倍;
   //其他(如果有)

}

2
虽然atomicNumber可以(应该?)可能是整数...
Andrew

我不会为此使用继承。isotope不是的特例Elemenet。我宁愿在拥有Element财产Isotope
CodesInChaos

2

真实世界的例子几乎总是弄错了,因为他们给的例子,其中总会有一些既是可能性TypeATypeB,但许多语言的单一继承层次结构不允许。

我编写的程序越多,我越能摆脱继承。

甚至“继承”一词在这里也被不正确地使用。例如,您继承了父亲约50%的特质和母亲约50%的特质。实际上,您的DNA是父亲的一半,母亲的一半。这是因为生物学实际上更喜欢组成而不是继承,您也应该这样做。

对那些刚接触面向对象编程的人来说,简单地实现接口,甚至更好地实现“鸭子类型”,再加上依赖注入,是一件好得多的事情。



1

一个很好的例子是排序中的比较功能:

template<class T>
class CompareInterface {
public:
   virtual bool Compare(T t1, T t2) const=0;
};
class FloatCompare : public CompareInterface<float> { };
class CompareImplementation : public FloatCompare {
public:
   bool Compare(float t1, float t2) const { return t1<t2; }
};
template<class T>
void Sort(T*array, int size, CompareInterface<T> &compare);

唯一的问题是新手常常认为性能比好的代码更重要...


0

我的真实示例是车辆:

public class Vehicle
{
    public Vehicle(int doors, int wheels)
    {
        // I describe things that should be
        // established and "unchangeable" 
        // when the class is first "made"
        NumberOfDoors = doors;
        NumberOfWheels = wheels;
    }

    public void RollWindowsUp()
    {
        WindowsUp = true;
    }

    // I cover modifiers on properties to show
    // how to protect certain things from being
    // overridden
    public int NumberOfDoors { get; private set; }
    public int NumberOfWheels { get; private set; }

    public string Color { get; set; }
    public bool WindowsUp { get; set; }
    public int Speed { get; set; }
}

public class Car : Vehicle
{
    public Car : base(4, 4)
    {

    }
}

public class SemiTruck : Vehicle
{
    public SemiTruck : base(2, 18)
    {

    }
}

该示例可以根据您的需要进行详细介绍,并且在车辆上附加了各种属性,以说明您可能要教的修饰符的用法。


2
我一直讨厌以载具为例,因为它并没有使新程序员更接近理解如何使用继承来改进代码。车辆是极其复杂的机器,在非程序员的脑海中回想起许多非抽象的想法。试图用代码来描述这一点使普通的新手相信示例中遗漏了很多细节,并给人一种感觉,即它们离使某项工作变得更近了。我从经验中说出这一点,因为当有人尝试使用车辆向我解释时,这正是我的感受。
riwalk 2011年

@ Stargazer712:我之所以使用车辆,主要是因为它们可以随您喜欢而复杂。我将由指导老师判断学生的水平。我使用描述通用基础知识的车辆的简单属性向妻子(编程经验为零)向他们解释了基本OOP。所有车辆都有车门,所有车辆都有车轮,等等。不能将目标示例归咎于糟糕的课程计划。
乔尔·埃瑟顿

你的妻子不是想写代码。我可以肯定地说,车辆实例并不能帮助我理解继承。无论您用什么来描述继承,它都必须完整实用。目的不是以非程序员可以理解的方式描述它。目的是以一种使新手程序员可以使用它的方式来描述它,而做到这一点的唯一方法是显示一个新手示例,说明一个专业程序员将如何使用它。
riwalk 2011年

@ Stargazer712:我会把您的无能力一开始就理解继承问题放在一个糟糕的教学计划上。我还用各种手段向与我一起工作的大三学生解释了继承问题,而且我从未遇到过这种概念的问题。我认为,如果一个课程计划被彻底和正确地构建,那么一个车辆对象既完整又实用。面对我向OOP教过的30个左右的实习生和初级开发人员,互联网上没有一个随便的家伙不会改变这一点。如果您不喜欢这辆车,请向下投票并继续前进。
乔尔·埃瑟顿

如您所愿....
riwalk

0

这个非哺乳动物,非鸟类,非鱼类的示例可能会有所帮助:

public abstract class Person {

    /* this contains thing all persons have, like name, gender, home addr, etc. */

    public Object getHomeAddr() { ... }

    public Person getName() { ... }

}

public class Employee extends Person{

    /* It adds things like date of contract, salary, position, etc */

    public Object getAccount() { ... }

}

public abstract class Patient extends Person {
    /* It adds things like medical history, etc */
}

然后

public static void main(String[] args) {

    /* you can send Xmas cards to patients and employees home addresses */

    List<Person> employeesAndPatients = Factory.getListOfEmployeesAndPatients();

    for (Person p: employeesAndPatients){
        sendXmasCard(p.getName(),p.getHomeAddr());
    }

    /* or you can proccess payment to employees */

    List<Employee> employees = Factory.getListOfEmployees();

    for (Employee e: employees){
        proccessPayment(e.getName(),e.getAccount());
    }       

}

注意:只是不要告诉这个秘密:人会延伸哺乳动物。


1
一直工作到您的一名员工也成为患者为止。
Scott Whitlock

在这种情况下,我认为将Patient和Employee声明为接口而不是抽象类更有意义。这使您可以使Person实现多个接口的灵活性。
Jin Kim

@JinKim-我完全同意,那是更好的方法。
Scott Whitlock,2012年

@JinKim它们不是唯一的。您可以在任何给定时间(但不能同时)将某人视为雇员或患者。两个接口都可以,但是什么时候调用实现这两个接口的具体类EmployeePatient呢?您将拥有多少种组合?
图兰斯·科尔多瓦

您可以根据需要调用具体类。如果代码只希望与雇员打交道,则可以将引用声明为雇员。(即Employee employee = new Person();)如果代码仅希望与患者打交道,则您将引用声明为患者。很少要直接将引用声明为具体类。
Jin Kim

0

代数表达式的层次结构怎么样。很好,因为它既包含继承又包含组成:

+--------------------+------------------------+
| Expression         |<------------------+    |
+--------------------+----------+        |    |
| + evaluate(): int  |<---+     |        |    |
+--------------------+    |     |        |    |
          ^               |     |        |    |
          |               |     |        |    |
   +--------------+  +---------------+  +-------------+  ...
   | Constant     |  | Negation      |  | Addition    |
   +--------------+  +---------------+  +-------------+
   | -value: int  |  |               |  |             |
   +--------------+  +---------------+  +-------------+
   | +evaluate()  |  | +evaluate()   |  | +evaluate() |
   | +toString()  |  | +toString()   |  | +toString() |
   +--------------+  +---------------+  +-------------+

   Addition(Constant(5), Negation(Addition(Constant(3),Constant(2))))
   (5 + -(3 + 2)) = 0

除了根表达式Constant以外,所有其他表达式都是一个Expression,并且包含一个或多个表达式。


-1

我将以鸟为例

像鸡,鸭,鹰

我将解释它们都有爪,啄和翅膀,但是它们的属性不同。

鸡不能飞,不能游泳,可以吃蠕虫,可以吃谷物

鸭子不能飞,不能游泳,可以吃谷物,不能吃蠕虫

鹰会飞,不会游泳,可以吃蠕虫,不能吃谷物


4
我曾经读过,构图和界面可能是传达这种概念的最佳方式,即飞行,游泳等
dreza 2012年

鸭子不能飞吗?
亚当·卡梅隆

-3

典型的rails-clone提供了许多实际示例:您拥有(抽象的)基本模型类,该类封装了所有数据操作,而您拥有了基本的控制器类,封装了所有HTTP通信。


精心阐述为什么这个答案不好?
keppla
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.