再见,面向对象的编程

几十年来,我一直在以对象为导向的语言进行编程。我使用的第一种OO语言是C ++,然后是SmallTalk,最后是.NET和Java。

我是为了利用遗产,,,,封装, 和多态性。范式的三个支柱。

我渴望获得重复使用的希望,并利用那些在这个新令人兴奋的景观中来到我面前的人所获得的智慧。

我无法将现实世界中的物体映射到他们的班级中,并期望整个世界都将整齐地落入到位。

我再错了。

继承,第一个跌落的支柱

乍一看,继承似乎是面向对象范式的最大好处。所有简单的形状层次结构示例被列为新灌输的示例,似乎是合乎逻辑的。

重用是一天的话。不……成为一年,也许是永远。

我吞咽了整个,并用新发现的洞察力冲向了世界。

香蕉猴丛林问题

有了我的内心宗教和问题,我开始建立班级等级结构和编写代码。一切都是正确的。

我永远不会忘记那天我准备通过从现有班级继承来兑现重复使用的承诺。这是我一直在等待的那一刻。

一个新的项目出现了,我回想起我上一个项目中我非常喜欢的那堂课。

没问题。重用进行营救。我要做的就是简单地从另一个项目中获取该课程并使用它。

好吧……实际上……不仅是那堂课。我们将需要父班。但是……但是就是这样。

gh…等等……看来我们也将需要父母的父母……然后……我们将需要所有父母。好吧……好吧……我处理这个。没问题。

而且很棒。现在它不会编译。为什么??我懂了…对象包含这是另一个目的。所以我也需要。没问题。

等等……我不仅需要目的。我需要该对象的父母及其父母的父母,等等,等等,依此类推,依此类推,所有包含的对象以及所有这些对象以及父母,父母,父母……

啊。

有很棒的报价乔·阿姆斯特朗,Erlang的创造者:

面向对象的语言的问题在于他们拥有随身携带的所有隐含环境。您想要一个香蕉,但您得到的是一颗拿着香蕉和整个丛林的大猩猩。

香蕉猴丛林解决方案

我可以通过不创建太深的层次结构来驯服这个问题。但是,如果继承是重复使用的关键,那么我对该机制的任何限制肯定都会限制再利用的好处。正确的?

正确的。

那么,有什么贫穷的面向对象的程序员是对Kool-Aid的健康帮助?

包含并委托。稍后再详细介绍。

钻石问题

迟早,以下问题将纠正其丑陋,并且根据语言无法解决的头部。

大多数OO语言不支持这一点,即使似乎逻辑上有道理。用OO语言支持这种情况有什么困难?

好吧,想象以下伪代码:

class powereddevice {
}
类扫描仪从powereddevice继承{
函数start(){
}
}
类打印机从powereddevice {
函数start(){
}
}
类复印机从扫描仪,打印机{
}

注意两个扫描器班级和打印机类实现一个称为的函数开始

因此,哪个启动函数可以执行复印机类继承?这扫描器一?这打印机一?不可能两者。

钻石解决方案

解决方案很简单。不要那样做。

恩,那就对了。大多数OO语言都不会让您这样做。

但是,但是……如果我必须为此建模怎么办?我要我的重复使用!

那你必须包含并委托

class powereddevice {
}
类扫描仪从powereddevice继承{
函数start(){
}
}
类打印机从powereddevice {
函数start(){
}
}
类复印机{
扫描仪扫描仪
打印机打印机
函数start(){
打印机.start()
}
}

在这里注意到复印机现在,类包含一个实例打印机扫描器。它委派了开始功能到打印机班级的实施。它可以很容易地委派给扫描器

这个问题是继承支柱的另一个裂缝。

脆弱的基类问题

因此,我使我的层次结构浅,并防止它们周期性。我没有钻石。

一切都是正确的。直到…

有一天,我的代码工作,第二天它停止工作。这是踢脚。我没有更改代码

好吧,也许这是一个错误……但是等等…确实有所改变

但这不是我的代码。事实证明,变化是在我继承的班级中。

基础类别的更改如何破坏我的代码?

这就是……

想象以下基类(用Java编写,但是如果您不知道Java,应该很容易理解):

导入java.util.arraylist;

公共类阵列
{
private arraylist a = new arraylist ();

public void add(对象元素)
{
a.Add(element);
}

public void addall(对象元素[])
{
for(int i = 0; i a.Add(元素[i]);//这条线将要更改
}
}

重要的:注意评论的代码行。这条线将在以后更改,这将破坏东西。

该类在其界面上具有2个函数,添加()全部添加()。这添加()函数将添加一个元素和全部添加()会添加多个元素通过调用添加功能

这是派生的班级:

公共类阵列扩展了数组
{
私有int count = 0;

@Override
public void add(对象元素)
{
super.add(element);
++计数;
}

@Override
public void addall(对象元素[])
{
super.addall(元素);
count += elements.length;
}
}

阵列课程是一般的专业化大批班级。这仅有的行为差异是阵列保留一个数数元素数量。

让我们详细看看这两个课程。

大批添加()将元素添加到本地数组列表
大批全部添加()呼叫本地数组列表为每个元素添加。

阵列添加()称其父母的添加()然后增加数数
阵列全部添加()称其父母的全部添加()然后增加数数按元素数。

一切都很好。

现在打破变化。基类中注释的代码行已更改为以下内容:

public void addall(对象元素[])
{
for(int i = 0; i 添加(元素[i]);//这条线已更改
}

就基类的所有者而言,它仍然可以正常运行。所有自动测试仍然通过

但是所有者不理会派生的阶级。而派生班的所有者是粗鲁的觉醒。

现在ArrayCount Addall()称其父母的全部添加()哪个内部打电话添加()那是Overriden衍生的班级。

这导致数数每次都会增加衍生的添加()被称为递增再次根据在衍生的全部添加()

这是两次。

如果发生这种情况,并且确实如此,那么派生类的作者必须知道如何实施基类。而且必须将基础类别的每一个变化告知他们,因为它可以以无法预测的方式破坏其派生的班级。

啊!这种巨大的裂缝永远威胁着宝贵的继承支柱的稳定性。

脆弱的基类解决方案

再次包含并代表救援。

通过使用包含和委托,我们从白盒编程到黑匣子编程。通过白盒编程,我们必须查看基类的实现。

使用黑匣子编程,我们可以完全不知道实现,因为我们不能通过覆盖其功能之一来将代码注入基类。我们只需要关注界面即可。

这种趋势令人不安…

继承本应该是重复使用的巨大胜利。

面向对象的语言不包含并易于委派。它们旨在使继承变得容易。

如果您像我一样,就开始想知道这种继承的事情。但更重要的是,这应该使您通过层次结构的分类力量充满信心。

层次结构问题

每次我从一家新公司开始时,我都会在创建一个放置公司文件的地方时解决问题,例如员工手册。

我是否创建一个名为文档的文件夹,然后在其中创建一个名为Company的文件夹?

还是我创建一个名为Company的文件夹,然后在其中创建一个名为文档的文件夹?

两者都在工作。但是哪个是对的?哪个最好?

分类层次结构的想法是,有一些基本阶级(父母)更一般,而派生的班级(儿童)是这些课程中更专业的版本。随着我们沿着继承链的发展,甚至更加专业。(请参阅上面的形状层次结构)

但是,如果父母和孩子可以任意切换位置,那么这个模型显然出了问题。

层次结构解决方案

怎么了...

分类层次结构不起作用

那么什么等级制度是有益的呢?

遏制

如果您查看现实世界,您会在各地看到遏制(或独家所有权)层次结构。

您找不到的是分类层次结构。让它沉入一会儿。面向对象的范式是基于现实世界,其中一个充满了物体。但是后来它使用了一个损坏的模型,即。分类层次结构,没有现实世界的类比。

但是现实世界中充满了遏制层次结构。遏制层次结构的一个很好的例子是您的袜子。它们位于梳妆台中一个抽屉中的袜子抽屉中,其中包含在您房屋中的卧室中,等等。

硬盘上的目录是遏制层次结构的另一个示例。他们包含文件。

那么我们如何分类呢?

好吧,如果您想到公司文件,我把它们放在哪里都没关系。我可以将它们放在文档文件夹或称为“物品的文件夹”文件夹中。

我对其进行分类的方式是标签。我用以下标签标记文件:

文档
公司
手册

标签没有顺序或层次结构。(这也解决了钻石问题。)

标签类似于接口,因为您可以具有与文档关联的多种类型。

但是,由于裂缝如此多,似乎遗产支柱已经下降了。

再见,继承。

封装,第二个落下的支柱

乍一看,封装似乎是面向对象编程的第二大好处。

对象状态变量免受外部访问的保护,即它们被封装在对象中。

我们不再需要担心谁知道谁正在访问的全球变量。

封装对您的变量是安全的。

这个封装的东西令人难以置信!

万能封装…

直到…

参考问题

为了提高效率,对象不是通过其值而是通过引用传递给函数。

这意味着函数不会传递对象,而是通过参考或指向对象的指针。

如果通过引用对象构造函数传递对象,则构造函数可以将该对象引用放在受封装保护的私有变量中。

但是过去了对象不安全!

为什么不?因为其他一些代码具有指向对象的指针,即。称为构造函数的代码。它必须具有对对象的引用,否则无法将其传递给构造函数?

参考解决方案

构造函数将不得不克隆传递的对象。不是一个浅的克隆,而是一个深的克隆,即传递在对象中的每个对象,这些对象中的每个对象等等,等等。

这么多提高效率。

这是踢脚。并非所有对象都可以克隆。有些人具有与之相关的操作系统资源,使克隆充其量无用或最坏的不可能。

每一个单个主流oo语言有这个问题。

再见,封装。

多态性,第三个跌倒的支柱

多态性是面向对象的三位一体的红发继子。

这是小组的拉里罚款。

他们到处都在那里,但他只是一个支持角色。

这并不是说多态性不是很好,而是您不需要面向对象的语言来获得这一点。

接口将为您提供这个。而且没有OO的所有行李。

借助接口,您可以混合多少种不同的行为是没有限制的。

因此,没有太多的ADO,我们向多态性和问好基于接口多态性。

诺言

好吧,哦,肯定会在早期承诺很多。这些诺言仍在向坐在课堂上,阅读博客和在线课程的天真程序员做出。

我花了数年的时间才意识到对我的撒谎。我也睁大眼睛,经验不足和信任。

我被烧死了。

再见,面向对象的编程。

那呢?

你好,功能编程。在过去的几年中,与您合作真是太好了。

只是您知道,我不会以表面上的价值承诺您的任何诺言。我必须看到它才能相信。

一旦被烧毁,两次害羞。

你明白。

如果您喜欢这个,请单击下面的内容,以便其他人会在媒体上看到此内容。betway娱乐官网

大约2021年更新:我有一本书,可以使您摆脱面向对象的泥潭,功能性编程变得更容易:逐步指南

如果您想加入Web开发人员的学习社区,并互相帮助使用ELM中的功能编程开发Web应用程序,请查看我的Facebook组,学习ELM编程https://www.facebook.com/groups/lealearnm/

我的Twitter:@CSCALFANI

- -

- -

获取中型应用betway娱乐官网

一个说“在应用商店上下载”的按钮,如果单击,它将带您到iOS App Store
一个说“获取它,Google Play”的按钮,如果单击它,它将带您到Google Play商店
查尔斯·斯卡法尼(Charles Scalfani)

软件工程师和建筑师,老师,作家,电影制片人,摄影师,艺术家…

Baidu