构建软件系统时,管理代码库复杂性至关重要。
Clean Code 的第 11 章讨论了设计模块化系统,这些系统随着时间的推移更容易维护和适应。
我们可以使用 JavaScript 示例来说明这些概念。
随着系统的发展,它们自然会变得更加复杂。这种复杂性可能会导致难以:
设计良好的系统应该易于修改、可测试和可扩展。实现这一目标的秘诀在于模块化和仔细的关注点分离。
清洁系统设计的核心是模块化原则。通过将大型系统分解为更小的独立模块,每个模块都有明确的职责,可以使系统更易于管理。
每个模块应该封装特定的功能,使整个系统更容易理解和更改。
示例:组织购物车系统
让我们想象一下 JavaScript 中的购物车系统。您可以将系统分成几个模块,而不是将所有逻辑集中到一个文件中:
// cart.js export class Cart { constructor() { this.items = []; } addItem(item) { this.items.push(item); } getTotal() { return this.items.reduce((total, item) => total item.price, 0); } } // item.js export class Item { constructor(name, price) { this.name = name; this.price = price; } } // order.js import { Cart } from './cart.js'; import { Item } from './item.js'; const cart = new Cart(); cart.addItem(new Item('Laptop', 1000)); cart.addItem(new Item('Mouse', 25)); console.log(`Total: $${cart.getTotal()}`);
职责划分如下:Cart 管理商品,Item 代表产品,order.js 协调交互。
这种分离确保每个模块都是独立的,并且更容易独立测试和更改。
模块化的目标之一是封装——向系统的其余部分隐藏模块的内部工作原理。
外部代码只能通过其定义良好的接口与模块交互。
这使得更改模块的内部实现变得更容易,而不会影响系统的其他部分。
示例:封装购物车逻辑
假设我们想要更改计算购物车总数的方式。也许现在我们需要考虑销售税。我们可以将这个逻辑封装在 Cart 类中:
// cart.js export class Cart { constructor(taxRate) { this.items = []; this.taxRate = taxRate; } addItem(item) { this.items.push(item); } getTotal() { const total = this.items.reduce((sum, item) => sum item.price, 0); return total total * this.taxRate; } } // Now, the rest of the system does not need to know about tax calculations.
系统的其他部分(例如 order.js)不受总计计算方式变化的影响。这使您的系统更加灵活且更易于维护。
大型系统中的一个常见问题是系统的不同部分相互纠缠。
当一个模块开始承担太多职责时,在不同的上下文中更改或重用就会变得更加困难。
关注点分离原则确保每个模块都有一个特定的职责。
示例:单独处理付款
在购物车示例中,付款处理应在单独的模块中处理:
// payment.js export class Payment { static process(cart) { const total = cart.getTotal(); console.log(`Processing payment of $${total}`); // Payment logic goes here } } // order.js import { Cart } from './cart.js'; import { Payment } from './payment.js'; const cart = new Cart(0.07); // 7% tax rate cart.addItem(new Item('Laptop', 1000)); cart.addItem(new Item('Mouse', 25)); Payment.process(cart);
现在,支付逻辑与购物车管理分离。这使得以后可以轻松修改支付流程(例如,与不同的支付提供商集成),而不会影响系统的其余部分。
模块化的最大好处之一是您可以独立测试每个模块。
在上面的示例中,您可以为 Cart 类编写单元测试,而无需担心付款的处理方式。
示例:对购物车进行单元测试
// cart.test.js import { Cart } from './cart.js'; import { Item } from './item.js'; test('calculates total with tax', () => { const cart = new Cart(0.05); // 5% tax cart.addItem(new Item('Book', 20)); expect(cart.getTotal()).toBe(21); });
通过清晰的关注点分离,每个模块都可以单独测试,使调试更容易,开发更快。
当模块彼此过于依赖时,系统某一部分的变化可能会在其他地方产生意想不到的后果。
为了最大限度地减少这种情况,目标是模块之间的松散耦合。
这允许每个模块独立发展。
示例:注入依赖项
不要将依赖项硬编码在模块内,而是将它们作为参数传递:
// cart.js export class Cart { constructor(taxRateCalculator) { this.items = []; this.taxRateCalculator = taxRateCalculator; } addItem(item) { this.items.push(item); } getTotal() { const total = this.items.reduce((sum, item) => sum item.price, 0); return total this.taxRateCalculator(total); } }
这种方法使 Cart 类更加灵活,并且更容易使用不同的税收计算进行测试。
快乐编码! ?
免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。
Copyright© 2022 湘ICP备2022001581号-3