在 redux 中有没有面向对象的地方?

Posted

技术标签:

【中文标题】在 redux 中有没有面向对象的地方?【英文标题】:Is there any place for OOP in redux? 【发布时间】:2017-09-28 01:41:52 【问题描述】:

25 年来我一直在使用面向对象的编程实践,并且在过去 5 年中一直在尝试转向函数式编程,但是当我尝试做一些复杂的事情时,我的想法总是转向 OOP,尤其是现在ES6 支持不错的 OOP 语法,这是我构建东西的自然方式。

我现在正在学习 Redux,并且我理解(c.f. How to put methods onto the objects in Redux state?)将类实例放入 reducer 是一个禁忌;在普通 reducer 状态之上计算的推荐方法是使用选择器(例如,通过重新选择)。当然,React 推荐组合而不是继承(https://facebook.github.io/react/docs/composition-vs-inheritance.html,React redux oop classes)。

但是,在 React/Redux 生态系统中,有方法和继承的类对象吗?

我想,为了回答我自己的问题,OOP 类鼓励在同一位置添加数据属性和对数据的操作,这有助于提高可读性,但不适用于纯函数和不可变数据.

如果我要使用 OOP,我是否需要放弃让我的实例持续存在并保持状态任何时间的想法?就像,每次我想使用一个时,我都会从存储数据中实例化它,使用我想要的任何方法,然后把它扔掉?这可能会消除很多使用 OOP 类的动力。但是如果我保留实例,我会很头疼让它们与商店同步。

那么,当我想使用方法时总是使用选择器,而当我想使用继承时总是使用组合,答案是否正确?具体来说,我的意思是在存储和操作 Redux 存储中保存的数据以用于 React 组件时。如果是这样,它应该放在哪里?连接到选择器?像我建议的那样立即丢弃?


为了清楚起见,添加我的用例:我的数据基本上是一个巨大的图表:许多对象具有许多属性以及对象之间的许多关系。它是只读的,但很复杂。我的对象被称为“概念”。

在做出(可能是愚蠢的)迁移到 Redux 的决定之前,我使用类来构建和表示概念、概念集以及概念之间的关系。我的课程包括用于获取概念集的异步 api 逻辑、有关每个概念的信息以及有关每个概念相关的其他概念的信息。如果用户选择向下钻取,这些类将递归地获取并实例化新的概念集。 Redux 文档推荐用于嵌套数据 (http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html) 的扁平、规范化结构,这对于存储来说可能是明智的,但我的 OOP 模型非常适合遍历图形和内容的各个部分。使用选择器和可能涉及嵌套、可能带有循环或需要对更多数据进行异步调用的不可变状态,我很难理解。

我成功地将https://redux-observable.js.org/ 用于 api 内容。

也许@Sulthan 的回答是正确的:我应该可以在我的 Redux 应用程序中随意使用 OOP 技术。但这似乎仍然很奇怪。我不能保留我的对象,因为如果存储发生变化(例如,获取更多数据),我的对象可能会变得陈旧。如果我的对象是嵌套的,但我的存储是规范化的,我会在需要它们时(从选择器)实例化它们,并确保不要保留它们......

【问题讨论】:

只想说OOP不强调继承。 GoF 中的大多数设计模式都使用组合 + 接口(模板方法除外) 如果您正在处理您的个人项目,并且您觉得您编写的代码易于阅读/维护,那么您当然可以使用您喜欢的任何方法 但我仍然鼓励你尝试以函数式的方式思考。如果你阅读 React 的源代码和 React 16 重写(React Fiber),你会注意到当前的 React 代码库更多的是 OOP 风格,而在 React Fiber 中根本没有类 语言在变化,人们编写javascript的方式也在变化。功能轻巧灵活。你可以花一些时间阅读 Redux 文档和示例,看看代码有多漂亮。 另见medium.com/@ustunozgur/… 【参考方案1】:

这个问题有点基于观点,但让我们专注于核心点。

函数式编程和OOP之间没有矛盾。您只需要使用相同的编程模式。在函数式编程中使用类(带有继承)是没有问题的,如果你保持它们不可变的话。

为了证明我的观点,许多人用来在 redux 中保持状态的流行库 Immutable.js 是由 组成的。并且这些类具有继承性(例如OrderedSet extends Set)。

另请注意,大多数React 组件都是,它们也使用继承 (... extends React.Component)。

【讨论】:

谢谢。但这并不能完全回答我的问题。 React 组件继承自 React.Component,但不建议它们相互继承。如果我正在编写一个可重用的库,我会像您的示例一样使用 OOP,但我的问题是关于存储和操作 Redux 存储中保存的数据以用于 React 组件。抱歉,我不太清楚。 @Sigfried 为什么不建议他们互相继承?组件的继承没有错。在 redux 中使用类也没有错(例如Date)。唯一的要求是你不要直接改变它们(保持它们不可变)。 facebook.github.io/react/docs/composition-vs-inheritance.html 说:“我们建议使用组合而不是继承来重用组件之间的代码。”我的问题更多关于 Redux。我引用的讨论 (***.com/questions/32352982/…) 似乎对此不以为然。我见过的所有示例代码都使用了选择器,这是一种非常不同的从 OOP 类中构造数据的方式。我想使用 OOP 类,但我不确定如何将它们与商店联系起来。我将再次编辑我的问题并添加一个用例。 @Sigfried 这就是为什么我说它是基于意见的。有些人会告诉你,在函数式编程中任何使用继承都是邪恶的。有人会告诉你,OOP 和函数是正交的,可以一起使用。 是的,尤其是我的标题措辞方式。我想我要问/说的是:鉴于它是邪恶的(正如我自己的经验所证明的那样),而且,如果不是正交的,至少是可能的和痛苦的,有没有办法将它们结合起来(在redux 的情况),将邪恶最小化。我刚刚添加的答案给出了我目前组合它们的方法。所以,我真正想要的不是意见,而是如何在不完全牺牲使用方法负载、可组合对象(如 OOP)组织代码的优势的情况下进行 redux 编程的想法......【参考方案2】:

答案是可能,但非常不鼓励和不习惯。

React 确实依赖于 React.Component 的类和单级继承来实现具有生命周期的有状态组件,但官方不鼓励您在组件中进行更多级别的继承。

Redux 是围绕函数式编程原则构建的。出于各种原因,我们鼓励您将状态保留为纯 JS 对象和数组,并使用纯函数访问/操作它。

我当然见过许多试图在 Redux 之上添加 OOP 层的库(例如其方法变成动作创建者和归约器的类)。那些工作,但肯定违背了 Redux 的整体精神。

我确实使用了一个名为Redux-ORM 的库,确实允许您定义模型类,作为商店中普通 JS 对象的外观。然而,与我见过的许多其他库不同,它 Redux 一起工作,而不是试图改变 Redux 的行为方式。我在我的博客文章 Practical Redux, Part 1: Redux-ORM Basics 和 Practical Redux, Part 2: Redux-ORM Concepts and Techniques 中讨论了 Redux-ORM 的工作原理、使用方式以及为什么它仍然相当地道。总体而言,它是帮助管理 Redux 存储中的关系和规范化数据的出色工具。

最后,我目前正在撰写一篇博文,讨论 Redux 需要的实际技术限制(以及原因),与您打算如何使用 Redux,以及它是如何可能使用 Redux。我希望在接下来的一周左右能够完成 - 请关注http://blog.isquaredsoftware.com。

【讨论】:

Redux-ORM 非常有趣,但是如果我们想遵循将状态保持为普通 JS 对象和数组的建议,并使用普通函数访问和操作它,我们将在其中定义创建例如逻辑?以及在哪里实现检索相关对象的逻辑(如书的作者)?【参考方案3】:

我将通过描述我最终做了什么来回答我自己的问题,尽管它并不完美。

首先,我开始使用stampit,而不是常规的 ES6 类语法,它比 ES6 类更丑陋,但更灵活。

不过,我的复杂对象主要以两种形式存在:

商店的普通 JS 对象 类(实际加盖)实例,以方便和强大地使用实例方法。

我使用的约定是在对普通对象的所有引用前面放置一个 _underscore。由于很多原因,我的“解决方案”很笨拙而且很糟糕,但我认为尝试对所有事情都使用选择器会更糟。如果你好奇,这里是我的代码中我将普通存储对象“膨胀”为实例的地方:https://github.com/Sigfried/vocab-pop/blob/localstorage/src/ducks/conceptSet.js#L292

更新

将 redux 状态 POJO 转换为类实例(常规或 stampit)是一个糟糕的想法,早就应该有人阻止我。

我可能应该接受@markerikson 的回答,也许 Redux-ORM 的东西值得一看,但我只想明确地说,不要做我做过的事。 (我一直认为我很聪明地用聪明的黑客来填补我正在学习的技术的“空白”——然后,一旦我明白了为什么该技术没有将我的黑客包含在第一名。)

另一个更新

来自Composing Software: An Introduction:

我们不会说函数式编程比 面向对象编程,或者您必须选择其中之一 其他。 OOP 与 FP 是错误的二分法。每一个真正的 Javascript 我近年来看到的应用程序广泛混合了 FP 和 OOP。

看起来有很好的方法来考虑将 FP 和 OOP 结合起来,毫无疑问,它将使用一些不可变的类和组合,而无需大量继承。这个作文系列看起来像是我需要学习的。

【讨论】:

“一旦我明白了为什么这项技术一开始没有包括我的黑客攻击,我就会花费数月的时间来清理混乱”——这是一个真正的人生教训 仅适用于真正学习它的人 :) 对我来说,现实生活中的教训是当我的大脑对做某事感到兴奋时,我会继续轻信,无论我多久被烧伤通过过去类似的聪明想法。

以上是关于在 redux 中有没有面向对象的地方?的主要内容,如果未能解决你的问题,请参考以下文章

Java 面向对象

对于面向对象的部分详解

面向对象编程介绍,类和对象初始

python面向对象类

葵花宝典读书笔记-面向对象的应用范围

面向对象设计-精通随笔-值对象和引用对象