相关链接:
https://medium.com/@cscalfani/goodbye-object-oriented-programming-a59cda4c0e53
I’ve been programming in Object Oriented languages for decades. The first OO language I used was C++ and then Smalltalk and finally .NET and Java.
- 几十年来,我一直在面向对象语言编程。 我使用的第一个OO语言是C ++,然后是Smalltalk,最后是.NET和Java。
I was gung-ho to leverage the benefits of Inheritance, Encapsulation, and Polymorphism. The Three Pillars of the Paradigm.
- 我很高兴利用继承,封装和多态的好处。 范式的三大支柱。
I was eager to gain the promise of Reuse and leverage the wisdom gained by those who came before me in this new and exciting landscape.
- 我渴望获得重用的承诺,并在这个令人兴奋的新景观中利用那些来到我面前的人所获得的智慧。
I couldn’t contain my excitement at the thought of mapping my real-world objects into their Classes and expected the whole world to fall neatly into place.
- 想到将现实世界的物体映射到他们的类中并期望整个世界整齐地落到我的位置时,我无法抑制自己的兴奋。
I couldn’t have been more wrong.
- 我不可能更错。
Inheritance, the First Pillar to Fall
- 继承,堕落的第一支柱
At first glance, Inheritance appears to be the biggest benefit of the Object Oriented Paradigm. All the simplistic examples of shape hierarchies that are paraded out as examples to the newly indoctrinated seem to make logical sense.
乍一看,继承似乎是面向对象范式的最大好处。 形式层次结构的所有简单例子,作为新灌输的例子,似乎都具有逻辑意义。
Shape: 形状
Circle: 圆
FilledCircle: 填充圆
Dot: 点
Rectangle: 矩形
Polygon: 多边形
Square: 正方形
FilledPolygon: 填充多边形
key Abstract, Optional
And Reuse is the word of the day. No… make that the year and perhaps evermore.
- 而重用是当天的话语。 不......做那一年,也许永远。
I swallowed this whole and rushed out into the world with my newfound insight.
- 我吞下这整体,用我新发现的洞察力冲向世界。
Banana Monkey Jungle Problem
- 香蕉猴丛林问题
With religion in my heart and problems to solve, I started building Class Hierarchies and writing code. And all was right with the world.
- 我心中有宗教和要解决的问题,我开始构建类层次结构并编写代码。 一切都与世界是对的。
I’ll never forget that day when I was ready to cash in on the promise of Reuse by inheriting from an existing class. This was the moment I had been waiting for.
- 我永远不会忘记那一天,当我准备通过继承现有的类来兑现重用的承诺。 这是我一直在等待的那一刻。
A new project came along and I thought back to that Class that I was so fond of in my last project.
- 一个新项目出现了,我回想起那个我在上一个项目中非常喜欢的类。
No problem. Reuse to the rescue. All I gotta do is simply grab that Class from the other project and use it.
- 没问题。 重新使用救援。 我要做的就是从其他项目中获取该类并使用它。
Well… actually… not just that Class. We’re gonna need the parent Class. But… But that’s it.
- 嗯......实际上......不只是那个类。 我们需要父类。 但是......但就是这样。
Ugh… Wait… Looks like we gonna also need the parent’s parent too... And then… We’re going to need ALL of the parents. Okay… Okay… I handle this. No problem.
- 呃......等等......看起来我们也需要父类的父类......然后......我们将需要所有的父类。 好的......好的......我处理了这件事。 没问题。
And great. Now it won’t compile. Why?? Oh, I see… This object contains this other object. So I’m gonna need that too. No problem.
- 太棒了 现在它不会编译。 为什么?? 哦,我明白了...这个对象包含了这个其他对象。 所以我也需要它。 没问题。
Wait… I don’t just need that object. I need the object’s parent and its parent’s parent and so on and so on with every contained object and ALL the parents of what those contain along with their parent’s, parent’s, parent’s…
- 等等......我不只是需要那个对象。 我需要对象的父对象及其父对象的父对象,依此类推,每个包含的对象以及包含父对象,父对象,父对象的所有父对象......
Ugh.
- 啊。
There’s a great quote by Joe Armstrong, the creator of Erlang:
- Erlang的创建者Joe Armstrong引用了一句很棒的话:
The problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.
- 面向对象语言的问题在于它们具有所有这些隐含的环境,它们随身携带。 你想要一个香蕉,但是你得到的却是一只拿着香蕉的大猩猩和整个丛林。
Banana Monkey Jungle Solution
- 香蕉猴丛林解决方案
I can tame this problem by not creating hierarchies that are too deep. But if Inheritance is the key to Reuse, then any limits I place on that mechanism will surely limit the benefits of Reuse. Right?
- 我可以通过不创建太深的层次结构来解决这个问题。 但是如果继承是重用的关键,那么我对该机制的任何限制肯定会限制重用的好处。 对?
Right.
- 是的
So what’s a poor Object Oriented Programmer, who’s had a healthy helping of the Kool-aid, to do?
- 那么,一个可怜的面向对象程序员,谁能够得到Kool援助的健康帮助呢?
Contain and Delegate. More on this later.
- 包含和委托。 稍后会详细介绍。
The Diamond Problem
- 钻石问题
Sooner or later, the following problem will rear its ugly and, depending on the language, unsolvable head.
- 迟早,下面的问题会让它变得丑陋,并且取决于语言,无法解决的问题。
Most OO languages do not support this, even though this seems to make logical sense. What’s so difficult about supporting this in OO languages?
- 大多数面向对象语言都不支持这一点,尽管这似乎具有逻辑意义。 用面向对象语言支持这个有什么困难吗?
Well, imagine the following pseudocode:
好吧,想象下面的伪代码:
Class PoweredDevice {
}
Class Scanner inherits from PoweredDevice {
function start() {
}
}
Class Printer inherits from PoweredDevice {
function start() {
}
}
Class Copier inherits from Scanner, Printer {
}
Notice that both the Scanner class and the Printer class implement a function called start.
- 请注意,Scanner类和Printer类都实现了一个名为start的函数。
So which start function does the Copier class inherit? The Scanner one? The Printer one? It can’t be both.
- 那么Copier类继承了哪个启动函数? 扫描仪在? 打印机一个? 它不可能两者兼而有之。
The Diamond Solution
- 钻石解决方案
The solution is simple. Don’t do that.
- 解决方案很简单。 不要那样做。
Yes that’s right. Most OO languages don’t let you do this.
- 恩,那就对了。 大多数面向对象语言都不允许你这样做。
But, but… what if I have to model this? I want my Reuse!
- 但是,但是......如果我必须对此进行建模怎么办? 我想要我的重用!
Then you must Contain and Delegate.
- 然后你必须包含和委派。
Class PoweredDevice {
}
Class Scanner inherits from PoweredDevice {
function start() {
}
}
Class Printer inherits from PoweredDevice {
function start() {
}
}
Class Copier {
Scanner scanner
Printer printer
function start() {
printer.start()
}
}
Notice here that the Copier class now contains an instance of a Printer and of a Scanner. It delegates the start function to the Printer class’s implementation. It could just as easily delegated to the Scanner.
- 请注意,Copier类现在包含打印机和扫描仪的实例。 它将start函数委托给Printer类的实现。 它可以很容易地委派给扫描仪。
This problem is yet another crack in the Inheritance pillar.
- 这个问题是继承支柱中的另一个问题。
The Fragile Base Class Problem
- 脆弱的基类问题
So I’m making my hierarchies shallow and keeping them from being cyclical. No diamonds for me.
- 因此,我正在使我的层次结构变浅,并使它们不再是周期性的。 我没有钻石。
And all was right with the world. That is until…
- 一切都与世界是对的。 那是......
One day, my code works and the next day it stops working. Here’s the kicker. I didn’t change my code.
- 有一天,我的代码工作,第二天它停止工作。 这是踢球者。 我没有改变我的代码。
Well, maybe it’s a bug… But wait… Something did change…
- 好吧,也许这是一个错误......但是等等......有些事情发生了变化......
But it wasn’t in my code. Turns out the change was in the class that I inherited from.
- 但它不在我的代码中。 原来这个变化发生在我继承的类里面。
How could a change in the Base class break my code??
- 怎么可能在Base类中的更改破坏我的代码?
This is how…
- 这就是......
Imagine the following Base class (It’s written in Java, but it should be easy to understand if you don’t know Java):
- 想象一下下面的Base类(它是用Java编写的,但如果你不了解Java,它应该很容易理解):
import java.util.ArrayList;
public class Array
{
private ArrayList<Object> a = new ArrayList<Object>();
public void add(Object element)
{
a.add(element);
}
public void addAll(Object elements[])
{
for (int i = 0; i < elements.length; ++i)
a.add(elements[i]); // this line is going to be changed
}
}
}
IMPORTANT: Notice the commented line of code. This line is going to be changed later which will break things.
- 重要提示:请注意注释的代码行。 这条线将在以后改变,这将破坏事情。
This class has 2 functions on its interface, add() and addAll(). The add() function will add a single element and addAll() will add multiple elements by calling the add function.
- 该类在其接口上有2个函数,add()和addAll()。 add()函数将添加单个元素,addAll()将通过调用add函数添加多个元素。
And here’s the Derived class:
- 这是Derived类:
The ArrayCount class is a specialization of the general Array class. The only behavioral difference is that the ArrayCount keeps a count of the number of elements.
- ArrayCount类是泛型Array类的特化。 唯一的行为差异是ArrayCount保持元素数量的计数。
Let’s look at both of these classes in detail.
- 让我们详细看看这两个类。
The Array add() adds an element to a local ArrayList.
- Array add()将一个元素添加到本地ArrayList。
The Array addAll() calls the local ArrayList add for each element.
- Array addAll()为每个元素调用本地ArrayList add。
The ArrayCount add() calls its parent’s add() and then increments the count.
- ArrayCount add()调用其父级的add(),然后递增计数。
The ArrayCount addAll() calls its parent’s addAll() and then increments the count by the number of elements.
- ArrayCount addAll()调用其父级的addAll(),然后将计数增加元素数。
And all works fine.
- 一切正常。
Now for the breaking change. The commented line of code in the Base class is changed to the following:
- 现在发生了改变。 Base类中注释的代码行更改为以下内容:
public void addAll(Object elements[])
{
for (int i = 0; i < elements.length; ++i)
add(elements[i]); // this line was changed
}
As far as the owner of the Base class is concerned, it still functions as advertised. And all of the automated tests still pass.
- 就Base类的所有者而言,它仍然像宣传的那样起作用。 并且所有自动化测试仍然通过。
But the owner is oblivious to the Derived class. And the owner of Derived class is in for a rude awakening.
- 但是所有者对Derived类没有注意。 衍生类的所有者正在进行粗暴的觉醒。
Now ArrayCount addAll() calls its parent’s addAll() which internally calls the add() which has been OVERRIDEN by the Derived class.
- 现在,ArrayCount addAll()调用其父级的addAll(),它在内部调用Derived类已经OVERRIDEN的add()。
This causes the count to be incremented each time the Derived class’s add() is called and then it’s incremented AGAIN by the number of elements that were added in the Derived class’s addAll().
- 这会导致计数在每次调用Derived类的add()时递增,然后按照Derived类的addAll()中添加的元素数量递增AGAIN。
IT’S COUNTED TWICE.
- 它计算了两次。
If this can happen, and it does, the author of the Derived class must KNOW how the Base class has been implemented. And they must be informed about every change in the Base class since it could break their Derived class in unpredictable ways.
- 如果发生这种情况,那么Derived类的作者必须知道Base类是如何实现的。 并且他们必须被告知Base类中的每一个变化,因为它可能以不可预测的方式打破他们的Derived类。
Ugh! This huge crack is forever threatening the stability of precious Inheritance pillar.
- 啊! 这巨大的裂缝永远威胁着珍贵的传承支柱的稳定性。
The Fragile Base Class Solution
- 脆弱的基类解决方案
Once again Contain and Delegate to the rescue.
- 再次包含并代表救援。
By using Contain and Delegate, we go from White Box programming to Black Box programming. With White Box programming, we have to look at the implementation of the base class.
- 通过使用Contain和Delegate,我们从White Box编程转向Black Box编程。 使用White Box编程,我们必须查看基类的实现。
With Black Box programming, we can be completely ignorant of the implementation since we cannot inject code into the Base class by overriding one of its functions. We only have to concern ourselves with the Interface.
- 使用Black Box编程,我们可以完全不了解实现,因为我们不能通过覆盖其中一个函数将代码注入Base类。 我们只需要关注界面。
This trend is disturbing…
- 这种趋势令人不安......
Inheritance was supposed to be a huge win for Reuse.
- 继承被认为是重用的巨大胜利。
Object Oriented languages don’t make Contain and Delegate easy to do. They were designed to make Inheritance easy.
- 面向对象的语言不会使Contain和Delegate变得容易。 它们旨在使继承变得容易。
If you’re like me, you’re starting to wonder about this Inheritance thing. But more important, this should shake your confidence in the power of Classification via Hierarchies.
- 如果你像我一样,你开始怀疑这个继承的事情。 但更重要的是,这应该会让您对通过层次结构进行分类的能力产生信心。
The Hierarchy Problem
- 层次结构问题
Every time I start at a new company, I struggle with the problem when I’m creating a place to put my Company Documents, e.g. the Employee Handbook.
- 每当我开始在一家新公司工作时,当我创建一个放置公司文档的地方时,我就会遇到问题。 员工手册。
Do I create a folder called Documents and then create a folder called Company in that?
- 我是否创建了一个名为Documents的文件夹,然后在其中创建一个名为Company的文件夹?
Or do I create a folder called Company and then create a folder called Documents in that?
- 或者我创建一个名为Company的文件夹,然后创建一个名为Documents的文件夹?
Both work. But which is right? Which is best?
- 两者都有效。 但哪个是对的? 哪个最好?
The idea of Categorical Hierarchies was that there were Base Classes (parents) that were more general and that Derived Classes (children) were more specialized versions of those classes. And even more specialized as we make our way down the inheritance chain. (See the Shape Hierarchy above)
- 分类层次结构的概念是,基类(父类)更通用,派生类(子)是这些类的更专业版本。 当我们沿着继承链走下去时,甚至更专业。 (参见上面的形状层次结构)
But if a parent and child could arbitrarily switch places, then clearly something is wrong with this model.
- 但如果父母和孩子可以任意切换位置,那么这个模型显然有问题。
The Hierarchy Solution 层次结构解决方案
What’s wrong is…
- 怎么了......
Categorical Hierarchies don’t work.
- 分类层次结构不起作用。
So what are hierarchies good for?
- 那么层次结构有什么用呢?
Containment.
- 遏制。
If you look at the real world, you’ll see Containment (or Exclusive Ownership) Hierarchies everywhere.
- 如果你看一下现实世界,你会看到无处不在的遏制(或独占所有权)层次结构。
What you won’t find is Categorical Hierarchies. Let that sink in for a moment. The Object Oriented Paradigm was predicated upon the real world, one filled with Objects. But then it uses a broken model, viz. Categorical Hierarchies, where there is no real-world analogy.
- 您找不到的是分类层次结构。 让它沉入片刻。 面向对象的范式是基于现实世界,一个充满了对象。 但后来它使用了破碎的模型,即。 分类层次结构,没有真实世界的类比。
But the real world is filled with Containment Hierarchies. A great example of a Containment Hierarchy is your socks. They are in a sock drawer which is contained in one drawer in your dresser which is contained in your bedroom which is contained in your house, etc.
- 但现实世界充满了遏制层次结构。 遏制层次结构的一个很好的例子是你的袜子。 它们放在一个袜子抽屉里,它放在你的梳妆台的一个抽屉里,这个抽屉装在你房子里的卧室里,等等。
Directories on your hard drive are another example of a Containment Hierarchy. They contains files.
- 硬盘驱动器上的目录是包含层次结构的另一个示例。 它们包含文件。
So how do we categorize then?
- 那么我们如何分类呢?
Well, if you think of the Company Documents, it pretty much doesn’t matter where I put them. I can put them in a folder of Documents or a folder called Stuff.
- 好吧,如果你想到公司文件,我把它们放在哪里都没关系。 我可以将它们放在Documents文件夹或名为Stuff的文件夹中。
The way I categorize it is with tags. I tag the file with the following tags:
- 我对它进行分类的方式是使用标签。 我使用以下标记标记文件:
Document 文献
Company 公司
Handbook 手册
Tags have no order or hierarchy. (This solves the Diamond Problem too.)
- 标签没有订单或层次结构。 (这也解决了钻石问题。)
Tags are analogous to interfaces since you can have multiple types associated with the document.
- 标签类似于接口,因为您可以将多种类型与文档关联。
But with so many cracks, it looks like the Inheritance pillar has fallen.
- 但是有这么多裂缝,看起来继承支柱已经下降了。
Goodbye, Inheritance.
- 再见,继承。
Encapsulation, the Second Pillar to Fall
- 封装,堕落的第二支柱
At first glance, Encapsulation appears to be second biggest benefit of Object Oriented Programming.
- 乍一看,Encapsulation似乎是面向对象编程的第二大优势。
Object state variables are protected from outside access, i.e. they’re Encapsulated in the Object.
- 保护对象状态变量不受外部访问,即它们在对象中被封装。
No longer will we have to worry about global variables that are being accessed by who-knows-who.
- 我们不再需要担心who-know-who正在访问的全局变量。
Encapsulation is a Safe for your variables.
- 封装对您的变量是安全的。
This Encapsulation thing is INCREDIBLE!!
- 这个封装的事情是令人难以置信的!
Long live Encapsulation… - 万能封装.....
That is until…
- 那是......
The Reference Problem
- 参考问题
For efficiency sake, Objects are passed to functions NOT by their value but by reference.
- 为了提高效率,对象不是通过它们的值而是通过引用传递给函数。
What that means is that functions will not pass the Object, but instead pass a reference or pointer to the Object.
- 这意味着函数不会传递Object,而是传递一个引用或指向Object的指针。
If an Object is passed by reference to an Object Constructor, the constructor can put that Object reference in a private variable which is protected by Encapsulation.
- 如果通过引用Object构造函数传递Object,则构造函数可以将该Object引用放在受Encapsulation保护的私有变量中。
But the passed Object is NOT safe!
- 但传递的对象并不安全!
Why not? Because some other piece of code has a pointer to the Object, viz. the code that called the Constructor. It MUST have a reference to the Object otherwise it couldn’t pass it to the Constructor?
- 为什么不? 因为其他一些代码有一个指向Object的指针,即。 调用构造函数的代码。 它必须有一个对象的引用,否则它无法将它传递给构造函数?
The Reference Solution
- 参考解决方案
The Constructor will have to Clone the passed in Object. And not a shallow clone but a deep clone, i.e. every object that is contained in the passed in Object and every object in those objects and so on and so on.
- 构造函数必须克隆传入的Object。 而不是浅层克隆而是深层克隆,即传入的Object中包含的每个对象以及这些对象中的每个对象,依此类推。
So much for efficiency.
- 效率如此之高。
And here’s the kicker. Not all objects can be Cloned. Some have Operating System resources associated with them making cloning useless at best or at worst impossible.
- 这是踢球者。 并非所有对象都可以克隆。 有些人拥有与他们相关的操作系统资源,使得克隆在最好或最不可能的情况下毫无用处。
And EVERY single mainstream OO language has this problem.
- 而且每个主流OO语言都有这个问题。
Goodbye, Encapsulation.
- 再见,封装。
Polymorphism, the Third Pillar to Fall
- 多态性,堕落的第三支柱
Polymorphism was the redheaded stepchild of the Object Oriented Trinity.
- 多态性是面向对象三位一体的红发继子。
It’s sort of the Larry Fine of the group.
- 这是该组织的Larry Fine。
Everywhere they went he was there, but he was just a supporting character.
- 他们去的每个地方他都在那里,但他只是一个支持角色。
It’s not that Polymorphism isn’t great, it’s just that you don’t need an Object Oriented language to get this.
- 并不是多态性不是很好,只是你不需要面向对象的语言来实现这一点。
Interfaces will give you this. And without all of the baggage of OO.
- 接口会给你这个。 而且没有OO的所有包袱。
And with Interfaces, there isn’t a limit to how many different behaviors you can mix in.
- 使用Interfaces,您可以混合使用的行为数量没有限制。
So without much ado, we say goodbye to OO Polymorphism and hello to interface-based Polymorphism.
- 所以,没有太多的麻烦,我们告别OO多态性,并向基于接口的多态性问好。
Broken Promises
- 破碎的承诺
Well, OO sure promised a lot in the early days. And these promises are still being made to naive programmers sitting in classrooms, reading blogs and taking online courses.
- 好吧,OO肯定在早期承诺了很多。 这些承诺仍然是针对坐在教室,阅读博客和上网课程的天真程序员。
It’s taken me years to realize how OO lied to me. I too was wide-eyed and inexperienced and trusting.
- 我花了很多年才意识到OO对我撒谎。 我也是睁大眼睛,缺乏经验和信任。
And I got burned.
- 我被烧了
Good-bye, Object Oriented Programming.
- 再见,面向对象的编程。
So then what?
- 那么呢?
Hello, Functional Programming. It’s been so nice to work with you over the past few years.
- 你好,功能编程。 在过去的几年里和你一起工作真是太棒了。
Just so you know, I’m NOT taking any of your promises at face value. I’m going to have to see it to believe it.
- 只是你知道,我不会从表面上看你的任何承诺。 我将不得不相信它。
Once burned, twice shy and all.
- 一旦被烧,两次害羞和所有。
You understand.
- 你明白。
If you liked this, click the💚 below so other people will see this here on Medium.
- 如果你喜欢这个,请点击下面的so,这样其他人就可以在这里看到这个。
If you want to join a community of web developers learning and helping each other to develop web apps using Functional Programming in Elm please check out my Facebook Group, Learn Elm Programminghttps://www.facebook.com/groups/learnelm/
- 如果你想加入一个网络开发者社区,学习并互相帮助,使用Elm中的Functional Programming来开发网络应用程序,请查看我的Facebook群组