南风天Acell 南风天Accel
究竟!~
关注数: 52 粉丝数: 22 发帖数: 334 关注贴吧数: 61
装饰器、组合模式 装饰器模式:动态地给对象添加额外功能。常用于需要在不改变类结构的情况下扩展功能。 组合模式:用于将对象组合成树形结构,表示“部分-整体”层次结构,通常用来表示对象的整体-部分关系。 装饰器模式 // Coffee 接口,定义了咖啡的基本行为 type Coffee interface { Description() string } // SimpleCoffee 是基础的咖啡类 type SimpleCoffee struct{} func (c *SimpleCoffee) Description() string { return "Simple coffee" } // MilkDecorator 是装饰器,用于为咖啡添加牛奶 type MilkDecorator struct { coffee Coffee } func (d *MilkDecorator) Description() string { return d.coffee.Description() + ", with milk" } 组合模式 // Component 接口,所有的组件(叶子和组合)都需要实现这个接口 type Component interface { Render() // Render 方法,所有组件都需要实现这个方法 } // Button 类型,表示具体的叶子组件 type Button struct { name string } func (b *Button) Render() { fmt.Printf("Rendering Button: %s\n", b.name) } // TextBox 类型,表示具体的叶子组件 type TextBox struct { content string } func (t *TextBox) Render() { fmt.Printf("Rendering TextBox with content: %s\n", t.content) } // Composite 类型,可以包含其他 Component 类型的元素 type Composite struct { children []Component } func (c *Composite) Add(component Component) { c.children = append(c.children, component) } func (c *Composite) Render() { fmt.Println("Rendering Composite Component with children:") for _, child := range c.children { child.Render() // 调用每个子组件的 Render 方法 } }
设计模式 2.1-2.6 2.1-2.6 通过设计一个文档器的介绍来解释了如何拆解一个设计方案 ,其中通过思考列举了几个设计模式 组合模式:实现了灵活的文档结构,允许嵌套和递归组合。 策略模式:封装了不同的格式化算法,提供了灵活的文档布局。 装饰器模式:动态为文档元素添加功能,增强了用户界面。 抽象工厂模式:支持了多种视感标准和不同的操作系统。 桥接模式:解耦了窗口操作与实现,增强了系统的可移植性。 以下来个例子 装饰器模式 // Coffee 接口,定义了咖啡的基本行为 type Coffee interface { Description() string } // SimpleCoffee 是基础的咖啡类 type SimpleCoffee struct{} func (c *SimpleCoffee) Description() string { return "Simple coffee" } // MilkDecorator 是装饰器,用于为咖啡添加牛奶 type MilkDecorator struct { coffee Coffee } func (d *MilkDecorator) Description() string { return d.coffee.Description() + ", with milk" } 组合模式 // Component 接口,所有的组件(叶子和组合)都需要实现这个接口 type Component interface { Render() // Render 方法,所有组件都需要实现这个方法 } // Button 类型,表示具体的叶子组件 type Button struct { name string } func (b *Button) Render() { fmt.Printf("Rendering Button: %s\n", b.name) } // TextBox 类型,表示具体的叶子组件 type TextBox struct { content string } func (t *TextBox) Render() { fmt.Printf("Rendering TextBox with content: %s\n", t.content) } // Composite 类型,可以包含其他 Component 类型的元素 type Composite struct { children []Component } func (c *Composite) Add(component Component) { c.children = append(c.children, component) } func (c *Composite) Render() { fmt.Println("Rendering Composite Component with children:") for _, child := range c.children { child.Render() // 调用每个子组件的 Render 方法 } }
《设计模式》2.1-2.6 2.1-2.6 通过设计一个文档器的介绍来解释了如何拆解一个设计方案 ,其中通过思考列举了几个设计模式 组合模式:实现了灵活的文档结构,允许嵌套和递归组合。 策略模式:封装了不同的格式化算法,提供了灵活的文档布局。 装饰器模式:动态为文档元素添加功能,增强了用户界面。 抽象工厂模式:支持了多种视感标准和不同的操作系统。 桥接模式:解耦了窗口操作与实现,增强了系统的可移植性。 以下来个例子 装饰器模式 // Coffee 接口,定义了咖啡的基本行为 type Coffee interface { Description() string } // SimpleCoffee 是基础的咖啡类 type SimpleCoffee struct{} func (c *SimpleCoffee) Description() string { return "Simple coffee" } // MilkDecorator 是装饰器,用于为咖啡添加牛奶 type MilkDecorator struct { coffee Coffee } func (d *MilkDecorator) Description() string { return d.coffee.Description() + ", with milk" } 组合模式 // Component 接口,所有的组件(叶子和组合)都需要实现这个接口 type Component interface { Render() // Render 方法,所有组件都需要实现这个方法 } // Button 类型,表示具体的叶子组件 type Button struct { name string } func (b *Button) Render() { fmt.Printf("Rendering Button: %s\n", b.name) } // TextBox 类型,表示具体的叶子组件 type TextBox struct { content string } func (t *TextBox) Render() { fmt.Printf("Rendering TextBox with content: %s\n", t.content) } // Composite 类型,可以包含其他 Component 类型的元素 type Composite struct { children []Component } func (c *Composite) Add(component Component) { c.children = append(c.children, component) } func (c *Composite) Render() { fmt.Println("Rendering Composite Component with children:") for _, child := range c.children { child.Render() // 调用每个子组件的 Render 方法 } }
《重构》第十一章- 重构API 1. 将查询函数和修改函数分离 - 目的:提高函数的单一职责 - 方法:将同时包含查询和修改操作的函数拆分为两个独立函数 - 示例:将`getTotalOutstandingAndSendBill()`拆分为`getTotalOutstanding()`和`sendBill()` 2. 函数参数化 - 目的:增加函数的灵活性和复用性 - 方法:将函数中的硬编码值替换为参数 - 示例:将`tenPercentRaise()`改为`raise(percentage)` 3. 移除标记参数 - 目的:简化函数调用,提高代码可读性 - 方法:用多个专门的函数替代使用标记参数的通用函数 - 示例:将`setDimension(name, value)`改为`setHeight(value)`和`setWidth(value)` 4. 保持对象完整 - 目的:减少参数列表,简化函数调用 - 方法:传递整个对象,而不是从对象中抽取多个值作为参数 - 示例:将`circum(radius)`改为`circum(circle)` 5. 以查询取代参数 - 目的:简化函数调用,减少参数依赖 - 方法:将参数替换为对参数提供者的查询 - 示例:将`availableVacation(employee, month)`改为`employee.availableVacation(month)` 6. 以参数取代查询 - 目的:减少函数的副作用,提高函数的纯粹性 - 方法:将函数内部的查询替换为参数 - 示例:将`targetTemperature()`中的`thermostat.currentTemperature`替换为参数 7. 移除设值函数 - 目的:增强对象的不可变性 - 方法:移除不必要的设值函数,仅在构造函数中设置字段 - 示例:移除`setId()`方法,只允许在构造函数中设置id 8. 以工厂函数取代构造函数 - 目的:提供更灵活的对象创建方式 - 方法:使用工厂函数封装复杂的对象创建逻辑 - 示例:用`createEmployee(name, type)`替代`new Employee(name, type)` 9. 以命令取代函数 - 目的:将复杂函数封装为对象,便于扩展和自定义 - 方法:创建一个包含execute方法的命令对象 - 示例:将`score(candidate, medicalExam, scoringGuide)`改为`Scorer`类 10. 以函数取代命令 - 目的:简化不需要复杂封装的操作 - 方法:将简单的命令对象替换为普通函数 - 示例:将`ChargeCalculator`类替换为简单的`calculateCharge()`函数
《重构》 第十章 简化条件逻辑 10.1 分解条件表达式 (Decompose Conditional) 目的:将复杂的条件表达式分解成更简单、更具描述性的独立函数。 方法:1.将复杂条件的每个部分提取到一个独立的布尔函数中。 2.使用这些布尔函数代替原有的复杂条件表达式。 10.2 合并条件表达式 (Consolidate Conditional Expression) 目的:将多个条件表达式合并成一个条件表达式,从而减少代码重复。 方法:1.识别重复的条件表达式。 2.使用逻辑操作符(如&&、||)将它们合并。 10.3 合并重复的条件片段 (Consolidate Duplicate Conditional Fragments) 目的:将条件表达式中重复执行的代码合并到条件外,从而减少重复代码。 方法:1.识别条件表达式中重复的代码片段。 2.将这些代码片段移到条件表达式之外。 10.4 移除控制标记 (Remove Control Flag) 目的:通过使用循环和函数返回值等手段,移除用于控制流程的标记变量,使代码更加简洁。 方法:1.找出代码中的控制标记变量。 2.通过调整逻辑来消除对该变量的依赖。 10.5 用卫语句取代嵌套条件表达式 (Replace Nested Conditional with Guard Clauses) 目的:通过使用卫语句(早期返回)来替代嵌套的条件表达式,使代码更易读。 方法:1.识别嵌套的条件表达式。 2.将它们转换为一系列卫语句,每个卫语句处理一种特定情况,避免深层嵌套。 10.6 用多态取代条件表达式 (Replace Conditional with Polymorphism) 目的:通过使用多态来替代条件表达式,使代码更具扩展性和可维护性。 方法:1.识别使用类型码或其他条件分支的代码。 2.将这些分支转换为子类或策略模式,通过多态来处理不同的情况。 这一章节介绍了几种优化逻辑表达的情况,实际应用中,我们可以视情况将重复、复杂且不易阅读的部分 来做一个简化,最终的目标是易于阅读和迭代。
《重构》 第7章节 - 封装 封装的概念和重要性 - 封装是面向对象编程的核心原则之一 - 目的是隐藏内部实现细节, 只暴露必要的接口 - 有助于降低代码耦合度, 提高可维护性和灵活性 封装记录(Encapsulate Record) - 将原始数据结构(如数组或哈希表)封装成对象 - 好处:可以更容易地添加行为, 控制数据访问 - 实现步骤: a.创建一个空类来表示记录 b.添加一个构造函数, 接受原始数据结构 c.为每个数据项添加访问方法 d.逐步替换代码中直接访问数据的地方, 改用新的访问方法 封装集合(Encapsulate Collection) - 对集合类型的字段进行封装 - 目的:控制对集合的修改,防止外部直接操作集合 - 实现方法: a.提供只读的访问方法(例如,返回集合的不可变副本) b.提供添加和删除元素的方法 c.避免直接返回集合引用 - 以对象取代基本类型(Replace Primitive with Object) - 将简单的数据类型(如字符串、数字)替换为专门的类 - 优点:可以添加更多行为和验证逻辑 - 适用场景:当基本类型承载了更复杂的概念时(如电话号码、邮政编码等) - 实现步骤: a.创建一个简单的值对象类 b.创建一个类型转换函数 c.逐步替换原始类型的使用 以查询取代临时变量(Replace Temp with Query) - 将临时变量替换为一个查询方法 - 好处:提高代码的清晰度, 消除重复计算 - 适用于:被多次使用的复杂表达式结果 - 步骤: a.提取计算逻辑到一个新方法 b.用方法调用替换临时变量 提炼类(Extract Class) - 将相关的字段和方法移到一个新类中 - 目的:当一个类承担了太多责任时, 将其拆分 - 步骤: a.决定如何分割类的责任 b. 创建一个新类来表示提炼出的责任 c.建立从旧类到新类的链接 d.逐一搬移字段和方法 e.审视并调整两个类的接口 内联类(Inline Class) - 与提炼类相反, 将一个类的内容合并到另一个类中 - 适用于:一个类不再承担足够的责任时 - 步骤: a.在目标类中声明源类的公共接口 b.将源类的所有特性搬移到目标类 c.调整所有引用源类的代码 隐藏委托关系(Hide Delegate) - 在服务类上创建方法, 委托给委托类 - 目的:降低客户端对委托对象的了解, 减少依赖 - 步骤: a.对每个委托方法, 在服务对象上创建一个简单的委托方法 b.调整客户端, 让它只调用服务对象 c.如果没有客户端使用委托类, 可以删除服务类的相应访问方法 移除中间人(Remove Middle Man) - 与隐藏委托关系相反,当委托方法过多时使用 - 目的:减少不必要的委托, 提高性能 - 步骤: a.为委托对象创建一个访问函数 b.对于每个委托方法, 让客户端直接调用委托对象 替换算法(Substitute Algorithm) - 用新算法替换现有算法 - 适用于:发现一个更清晰的算法时 - 步骤: a.准备好新算法 b.执行静态检查 c.运行测试以检查两个算法是否有相同的输出 d.如果测试失败, 调试并比较算法的行为
《重构》第四章 构建测试 第四章主要讲测试。 作者从测试的各个角度来分析验证测试环节对软件开发及重构的作用 1. 测试的重要性 测试是重构的基石。在进行任何重构之前,我们需要先编写测试来验证现有代码的行为是否符合预期。这些测试为我们的重构提供了保障,使我们能够在改进代码结构的同时,确保功能不被破坏。 2. 测试的类型 重构过程中主要使用的是自动化测试。以下是几种常见的测试类型: •单元测试:测试代码的最小功能单元,如函数或方法,确保其行为正确。单元测试应该是快速且独立的。 •集成测试:测试多个模块或组件之间的交互,确保它们能正确协同工作。 •端到端测试:模拟用户操作,测试整个系统的行为,确保从前端到后端的所有流程都能正常运行。 3. 高质量测试的特征 编写高质量的测试是保证测试有效性的关键。高质量测试应具备以下特征: •快速:测试执行速度要快,以便能够频繁运行,及时反馈。 •独立:每个测试应独立运行,不依赖于其他测试的执行结果。 •可读:测试代码应清晰易读,能够准确表达测试目的和预期结果。 4. 边界条件测试 在编写测试时,特别要关注边界条件。边界条件是指代码在处理极端情况时的表现,如输入的最大值、最小值、零值、空值或超出预期范围的值。通过测试边界条件,我们可以发现和修复潜在的问题,确保代码在各种情况下都能正常工作。 5. 测试的持续性 测试并不是一次性的工作。随着代码的演进和重构,我们需要不断更新和扩展测试,以覆盖新的功能和改动。确保测试的持续有效性是维持代码质量的关键。 6. 测试驱动开发(TDD) 测试驱动开发是一种开发方法,其中测试在代码编写之前完成。通过先编写测试,我们可以更好地理解需求,并驱动代码的设计和实现。TDD的核心步骤包括: •编写失败的测试:根据需求编写一个未通过的测试。 •实现功能使测试通过:编写最小量的代码使测试通过。 •重构代码:优化代码结构,确保代码质量,同时保持测试通过。 第四章强调了在重构过程中构建测试的重要性。通过编写和维护高质量的自动化测试,我们可以确保代码在重构过程中保持正确性和稳定性。特别是对边界条件的测试,可以帮助我们捕捉极端情况下的潜在问题,提升代码的健壮性。此外,测试驱动开发作为一种有效的开发方法,可以帮助我们更好地理解需求,驱动代码设计,并在整个开发过程中保持高质量的代码。
1 下一页