今天,我们有许多好的实践、原则(SOLID、DRY、KISS)、GoF 模式等等。 他们都在努力帮助我们这些开发人员编写好的、干净、可维护和可理解的代码。 GRASP 是 General Responsibility Assignment Software Patterns 的缩写。 这是一组非常好的建议、原则和模式,可以让我们的代码变得更好。让我们看一下这个列表: 信息专家 创作者 控制器 低耦合 高凝聚力 纯制造 间接的 受保护的变体 多态性 今天,我们将学习前两个原则:信息专家和创造者。 信息专家 信息专家可能是所有 GRASP 模式中最重要的。该模式表示所有处理数据(变量、字段)的方法都应该位于数据(变量或字段)存在的同一位置。 我知道,我知道,这听起来不太清楚,所以让我们看一个例子。我们想创建一个功能来计算我们订购的物品的总和。 假设我们有 4 个文件:main.js、OrderList、OrderItem 和 Product。 产品可能包含 id、name 和 price(以及许多其他字段,与我们的示例无关): class Product { constructor(name, price) { this.name = name; this.price = price; } } OrderItem 将是一个包含产品和计数的简单对象,如下所示: class OrderItem { constructor(product, count) { this.product = product, this.count = count } } OrderList 文件将包含处理 orderItems 数组的逻辑。 class OrderList { constructor(items) { this.items = items; } } 而且,main.js 只是那个可以包含一些初始逻辑的文件,可以导入 OrderList,并对这个列表做一些事情。 import { OrderItem } from './OrderItem'; import { OrderList } from './OrderList'; import { Product } from './Product'; const samsung = new Product('Samsung', 200); const apple = new Product('Apple', 300); const lg = new Product('Lg', 150); const samsungOrder = new OrderItem(samsung, 2); const appleOrder = new OrderItem(samsung, 3); const lgOrder = new OrderItem(samsung, 4); const orderList = new OrderList([samsungOrder, appleOrder, lgOrder]); 应该在哪里创建计算总和的方法?至少有 2 个文件,每个文件都可以用于此目的,对吗?但是哪个地方更适合我们的目标呢? 让我们考虑一下 main.js。 我们可以这样写: const totalSum = orderList.reduce((res, order) => { return res + order.product.price * order.count }, 0) 它会起作用的。但是,orderItem 文件包含没有方法的数据,orderList 文件也包含没有方法的数据,主文件包含一个处理订单项目和订单列表的方法。 听起来不太好。如果我们想以某种方式添加更多与订单一起使用的逻辑,我们是否也将其放在主文件中?而且,一段时间后,我们的主文件会有很多不同的逻辑,有数千行代码,这真的很糟糕。这种反模式称为 ,其中 1 个文件包含所有内容。 God object 如果我们想使用 的方法应该怎么做?让我们尝试重复: 信息专家 所有处理数据(变量、字段)的方法都应该在数据(变量或字段)存在的地方。 这意味着: orderItem 应该包含可以计算特定项目总和的逻辑: class OrderItem { constructor(product, count) { this.product = product, this.count = count } getTotalPrice() { return this.product.price * this.count; } } 并且 orderList 应该包含可以计算所有订单项目总和的逻辑: class OrderList { constructor(items) { this.items = items; } getTotalPrice() { return this.items.reduce((res, item) => { return res + item.getTotalPrice(); }, 0); } } 而且,我们的主文件将很简单,不会包含该功能的逻辑;它将尽可能简单(除了许多导入,我们将很快删除)。 因此,任何仅与一个订单项相关的逻辑都应放置在 orderItem 中。 如果某些东西相对地使用一组 orderItems,我们应该把这个逻辑放在 orderItems 中。 我们的主文件应该只是一个入口点;做一些准备和导入,并将一些逻辑与其他逻辑联系起来。 这种分离为我们提供了代码组件之间的少量依赖关系,这就是我们的代码更易于维护的原因。 我们不能总是在我们的项目中使用这个原则,但它是一个非常好的原则。如果你可以使用它,你应该这样做。 创作者 在我们之前的示例中,我们有 4 个文件:Main、OrderList、OrderItem 和 Product。信息专家说方法应该在哪里:在数据所在的同一个地方。 但问题是:应该在谁和哪里创建对象?谁将创建 orderList,谁将创建 orderItem,谁将创建 Product? Creator 说每个对象(类)都应该只在使用它的地方创建。还记得我们在包含许多导入的主文件中的示例吗?让我们检查: import { OrderItem } from './OrderItem'; import { OrderList } from './OrderList'; import { Product } from './Product'; const samsung = new Product('Samsung', 200); const apple = new Product('Apple', 300); const lg = new Product('Lg', 150); const samsungOrder = new OrderItem(samsung, 2); const appleOrder = new OrderItem(samsung, 3); const lgOrder = new OrderItem(samsung, 4); const orderList = new OrderList([samsungOrder, appleOrder, lgOrder]); const totalSum = orderList.getTotalPrice(); 正如我们所见,几乎所有的导入和对象创建都在 main.js 中。 但是,让我们考虑一下它的真正使用对象和位置。 该产品仅在 OrderItem 中使用。 OrderItem 仅在 OrderList 中使用。 OrderList 用于 Main。它看起来像这样: Main → OrderList → OrderItem → 产品 但是如果Main只使用OrderList,为什么我们要在Main中创建OrderItem呢?为什么我们还要在这里创建一个产品?此刻,我们的 Main.js 创建(和导入)几乎所有东西。这不好。 遵循 原则,我们应该只在使用这些对象的地方创建对象。想象一下,使用我们的应用程序,我们将产品添加到购物车。这就是它的样子: 造物主 Main.js:我们在这里只创建(和导入)OrderList: import { OrderList } from './OrderList'; const cartProducts = [{ name: 'Samsung', price: 200, count: 2 }, { name: 'Apple', price: 300, count: 3 }, {name: 'Lg', price: 150, count: 4 }]; const orderList = new OrderList(cartProducts); const totalPrice = orderList.getTotalPrice(); OrderList.js:我们在这里只创建(和导入)OrderItem: import { OrderItem } from './OrderItem'; class OrderList { constructor(items) { this.items = items.map(item => new OrderItem(item)); } getTotalPrice() { return this.items.reduce((res, item) => { return res + item.getPrice(); }, 0); } } OrderItem.js:我们在这里只创建(和导入)产品: import { Product } from './Product'; class OrderItem { constructor(item) { this.product = new Product(item.name, item.price); this.count = item.count; } } 产品.js: class Product { constructor(name, price) { this.name = name; this.price = price; } } 我们有一个简单的依赖: 主 → OrderList → OrderItem → 产品 现在,每个对象只在使用它的地方创建。这就是 原则所说的。 造物主 我希望这个介绍对你有用,在下一个系列的 我们将介绍其他原则。 GRASP 中, 在 上拍摄的照片 Gabriel Heinzer Unsplash