From b73e39eeeee3ed0a3edd46ca6b63f7897fab8744 Mon Sep 17 00:00:00 2001 From: Misaka_Company Date: Thu, 19 Mar 2026 15:04:24 +0800 Subject: [PATCH] Refactor strategy pattern to implement separation of concerns - Update IModuleProcessor interface to accept targetBOMs collection instead of targetItemName string - Remove filtering logic from strategy implementations (AppendOnly, CloneNew) - Add LoadData method to mSpecAdditionManager for UI initialization - Rename ExecuteAddition to ExecutePipeline with refined parameters - Enhance test suite to simulate fine-grained UI selection behavior - Enable precise row-level control by delegating filtering to UI layer This refactoring achieves better separation of concerns where: - Strategy layer focuses purely on execution logic - UI layer handles selection and filtering - Manager layer coordinates the pipeline flow Co-Authored-By: Claude Sonnet 4.6 --- VBA/ClassModules/IModuleProcessor.cls | 20 +++-- VBA/ClassModules/cProcessor_AppendOnly.cls | 16 ++-- VBA/ClassModules/cProcessor_CloneNew.cls | 32 +++----- VBA/Modules/mSpecAdditionManager.bas | 48 +++++++----- VBA/Modules/mTestEngine.bas | 85 +++++++++------------- 5 files changed, 92 insertions(+), 109 deletions(-) diff --git a/VBA/ClassModules/IModuleProcessor.cls b/VBA/ClassModules/IModuleProcessor.cls index b3af3f4..f845a3f 100644 --- a/VBA/ClassModules/IModuleProcessor.cls +++ b/VBA/ClassModules/IModuleProcessor.cls @@ -1,16 +1,22 @@ ' ============================================================================== ' 模块名称: IModuleProcessor ' 模块类别: 类模块 (用作接口 Interface) -' 模块职责: 定义统一的业务处理接口,基于UI用户选择的物料名称进行精准处理 +' 模块职责: 定义统一的业务处理接口。此时的策略层已完全解耦,不关心过滤逻辑。 ' ============================================================================== Option Explicit ' 方法名: ProcessSpec -' allBOMs: 包含所有物料信息的集合 (库里的所有数据) -' targetItemName: 要被处理的物料的名称字段 (瞄准镜) -' paramName: 参数名(如 lcfw) -' newValue: 新规格代码(如 M19) -' engine: 条件注入引擎实例 -Public Sub ProcessSpec(ByRef allBOMs As Collection, ByVal targetItemName As String, ByVal paramName As String, ByVal newValue As String, ByRef engine As cConditionEngine) +' allBOMs: 总库集合 (主要用于克隆策略追加新行) +' targetBOMs: 由UI层(或中枢)精挑细选出来的目标行集合 (手术对象) +' paramName: 参数名(如 lcfw) +' newValue: 新规格代码(如 M19) +' engine: 条件注入引擎实例 +Public Sub ProcessSpec( _ + ByRef allBOMs As Collection, _ + ByRef targetBOMs As Collection, _ + ByVal paramName As String, _ + ByVal newValue As String, _ + ByRef engine As cConditionEngine) + ' 接口中不写任何实现代码 End Sub \ No newline at end of file diff --git a/VBA/ClassModules/cProcessor_AppendOnly.cls b/VBA/ClassModules/cProcessor_AppendOnly.cls index 387865f..cfce13e 100644 --- a/VBA/ClassModules/cProcessor_AppendOnly.cls +++ b/VBA/ClassModules/cProcessor_AppendOnly.cls @@ -1,28 +1,24 @@ ' ============================================================================== ' 模块名称: cProcessor_AppendOnly ' 模块类别: 类模块 -' 模块职责: 通用追加策略。适用于接头、封口片、机芯、盘止钉等。 -' 逻辑描述: 遍历所有数据,凡是名称与目标匹配的变种,一律注入新条件。 +' 模块职责: 通用追加策略。 +' 逻辑描述: 无脑遍历传入的 targetBOMs 集合,利用引擎进行条件追加。 ' ============================================================================== Option Explicit Implements IModuleProcessor Private Sub IModuleProcessor_ProcessSpec( _ ByRef allBOMs As Collection, _ - ByVal targetItemName As String, _ + ByRef targetBOMs As Collection, _ ByVal paramName As String, _ ByVal newValue As String, _ ByRef engine As cConditionEngine) Dim rowObj As cBOMRow - ' 遍历整个库 - For Each rowObj In allBOMs - ' 只要名称完全匹配 (忽略大小写进行比对) - If StrComp(rowObj.ItemName, targetItemName, vbTextCompare) = 0 Then - ' 利用引擎进行外科手术级别的追加 - rowObj.Condition = engine.InjectOr(rowObj.Condition, paramName, newValue) - End If + ' 不再需要判断名字,因为 UI 层传过来的 targetBOMs 就是已经挑好的 + For Each rowObj In targetBOMs + rowObj.Condition = engine.InjectOr(rowObj.Condition, paramName, newValue) Next rowObj End Sub \ No newline at end of file diff --git a/VBA/ClassModules/cProcessor_CloneNew.cls b/VBA/ClassModules/cProcessor_CloneNew.cls index 05dc275..bd50ff1 100644 --- a/VBA/ClassModules/cProcessor_CloneNew.cls +++ b/VBA/ClassModules/cProcessor_CloneNew.cls @@ -1,39 +1,29 @@ ' ============================================================================== ' 模块名称: cProcessor_CloneNew ' 模块类别: 类模块 -' 模块职责: 通用克隆新增策略。适用于弹性元件等需要完全独立成行的规格。 -' 逻辑描述: 找到第一个匹配的母版行,克隆它并赋予纯粹的新规格条件。 +' 模块职责: 通用克隆新增策略。 +' 逻辑描述: 遍历传入的 targetBOMs 母版集合,每遇到一个就克隆一行追加到总库,并赋新值。 ' ============================================================================== Option Explicit Implements IModuleProcessor Private Sub IModuleProcessor_ProcessSpec( _ ByRef allBOMs As Collection, _ - ByVal targetItemName As String, _ + ByRef targetBOMs As Collection, _ ByVal paramName As String, _ ByVal newValue As String, _ ByRef engine As cConditionEngine) Dim rowObj As cBOMRow - Dim templateRow As cBOMRow - Set templateRow = Nothing + Dim newRow As cBOMRow - ' 1. 寻找母版 (找到同名的第一条记录即可) - For Each rowObj In allBOMs - If StrComp(rowObj.ItemName, targetItemName, vbTextCompare) = 0 Then - Set templateRow = rowObj - Exit For - End If + ' 为 targetBOMs 里的每一个母版克隆出一个新对象 + For Each rowObj In targetBOMs + ' 调用数据访问层的克隆方法,自动加入 allBOMs 总集合 + Set newRow = mBOMRepository.InsertNewBOMRow(allBOMs, rowObj) + + ' 新行的选择条件直接赋予纯粹的新规格 (例如:lcfw=M19) + newRow.Condition = paramName & "=" & newValue Next rowObj - ' 2. 如果找到了母版,进行克隆 - If Not templateRow Is Nothing Then - Dim newRow As cBOMRow - ' 调用数据访问层的克隆方法,它会自动把新对象加入到 allBOMs 集合中 - Set newRow = mBOMRepository.InsertNewBOMRow(allBOMs, templateRow) - - ' 新行的选择条件不再是追加,而是直接等于新规格 (例如:lcfw=M19) - newRow.Condition = paramName & "=" & newValue - End If - End Sub \ No newline at end of file diff --git a/VBA/Modules/mSpecAdditionManager.bas b/VBA/Modules/mSpecAdditionManager.bas index fe6694a..757e969 100644 --- a/VBA/Modules/mSpecAdditionManager.bas +++ b/VBA/Modules/mSpecAdditionManager.bas @@ -6,45 +6,53 @@ Option Explicit ' ------------------------------------------------------------------------------ -' 过程名称: ExecuteAddition +' 过程名称: LoadData +' 过程功能: 供 UI 初始化时调用,加载总库数据 +' ------------------------------------------------------------------------------ +Public Function LoadData(ByVal ws As Worksheet) As Collection + Set LoadData = mBOMRepository.LoadAllBOMs(ws) +End Function + +' ------------------------------------------------------------------------------ +' 过程名称: ExecutePipeline ' 过程功能: 执行完整的新增规格流水线 ' 参数说明: -' ws - 目标工作表对象 -' targetItemName - 用户瞄准的物料名称 (如 "径向高压接头") -' paramName - 要修改的参数名 (如 "lcfw") -' newValue - 新增的规格值 (如 "M19") -' strategyType - UI选择的执行策略 (如 "APPEND" 或 "CLONE") +' ws - 目标工作表对象 (保存时需要) +' allBOMs - 内存中的总库集合 +' targetBOMs - UI层过滤组装好的【精准目标集合】 +' paramName - 要修改的参数名 (如 "lcfw") +' newValue - 新增的规格值 (如 "M19") +' strategyType - UI选择的执行策略 (如 "APPEND" 或 "CLONE") ' ------------------------------------------------------------------------------ -Public Sub ExecuteAddition( _ +Public Sub ExecutePipeline( _ ByVal ws As Worksheet, _ - ByVal targetItemName As String, _ + ByRef allBOMs As Collection, _ + ByRef targetBOMs As Collection, _ ByVal paramName As String, _ ByVal newValue As String, _ ByVal strategyType As String) + ' 如果没有选中任何目标,直接中断退出 + If targetBOMs Is Nothing Then Exit Sub + If targetBOMs.count = 0 Then Exit Sub + Dim engine As cConditionEngine - Dim bomCollection As Collection Dim processor As IModuleProcessor ' [步骤 1] 初始化核心手术刀引擎 Set engine = New cConditionEngine - ' [步骤 2] 通过数据层将 Excel 读入内存变为对象集合 - Set bomCollection = mBOMRepository.LoadAllBOMs(ws) - - ' [步骤 3] 从工厂获取用户指定的策略 + ' [步骤 2] 从工厂获取用户指定的策略 Set processor = mProcessorFactory.GetProcessor(strategyType) - ' [步骤 4] 将数据集合与目标扔给策略对象执行 - ' 提示:所有的脏标记 (IsDirty) 会在这一步由对象自己触发记录 - processor.ProcessSpec bomCollection, targetItemName, paramName, newValue, engine + ' [步骤 3] 将总库、精准目标集合扔给策略对象执行 + processor.ProcessSpec allBOMs, targetBOMs, paramName, newValue, engine - ' [步骤 5] 将修改过的数据 (IsDirty=True) 批量写回 Excel - mBOMRepository.SaveAll ws, bomCollection + ' [步骤 4] 将修改过的数据批量写回 Excel (只写 IsDirty=True 的对象) + mBOMRepository.SaveAll ws, allBOMs - ' 释放资源 (VBA 虽然有垃圾回收,但良好习惯可以防止 Excel 卡死) + ' 释放资源 Set processor = Nothing - Set bomCollection = Nothing Set engine = Nothing End Sub \ No newline at end of file diff --git a/VBA/Modules/mTestEngine.bas b/VBA/Modules/mTestEngine.bas index 31c6a80..a3d65a4 100644 --- a/VBA/Modules/mTestEngine.bas +++ b/VBA/Modules/mTestEngine.bas @@ -167,7 +167,7 @@ End Sub Private Sub Test_Processors() Debug.Print "" - Debug.Print "========== 4. 开始测试: 业务策略层 (Strategy) 新架构 ==========" + Debug.Print "========== 4. 开始测试: 业务策略层 (Strategy) 细粒度新架构 ==========" Dim engine As cConditionEngine Set engine = New cConditionEngine @@ -175,47 +175,45 @@ Private Sub Test_Processors() Dim paramName As String: paramName = "lcfw" Dim newValue As String: newValue = "M19" - Dim mockBOMs As New Collection + Dim allBOMs As New Collection + Dim targetBOMs As New Collection Dim row1 As New cBOMRow, row2 As New cBOMRow, row3 As New cBOMRow - row1.ItemName = "径向高压接头" - row1.Condition = "lcfw=M12" - mockBOMs.Add row1 + row1.ItemName = "径向高压接头": row1.Condition = "lcfw=M12": allBOMs.Add row1 + row2.ItemName = "径向高压接头": row2.Condition = "lcfw=M13": allBOMs.Add row2 + row3.ItemName = "弹簧管": row3.Condition = "lcfw=M01 AND gclj!=M20": allBOMs.Add row3 - row2.ItemName = "径向高压接头" - row2.Condition = "lcfw=M13" - mockBOMs.Add row2 - - row3.ItemName = "弹簧管" - row3.Condition = "lcfw=M01 AND gclj!=M20" - mockBOMs.Add row3 + ' 【核心逻辑】模拟 UI 层仅将用户勾选的具体行 (比如 row1) 加入 targetBOMs 集合 + targetBOMs.Add row1 + ' 测试 1: 追加策略 (AppendOnly) Dim strategyAppend As IModuleProcessor Set strategyAppend = New cProcessor_AppendOnly - strategyAppend.ProcessSpec mockBOMs, "径向高压接头", paramName, newValue, engine + strategyAppend.ProcessSpec allBOMs, targetBOMs, paramName, newValue, engine - AssertEqual "追加策略_变种1被更新", "lcfw=M12 OR lcfw=M19", row1.Condition - AssertEqual "追加策略_变种2被更新", "lcfw=M13 OR lcfw=M19", row2.Condition + AssertEqual "追加策略_选中的变种1被更新", "lcfw=M12 OR lcfw=M19", row1.Condition + AssertEqual "追加策略_未选中的变种2保持不变", "lcfw=M13", row2.Condition AssertEqual "追加策略_不影响其他物料", "lcfw=M01 AND gclj!=M20", row3.Condition + ' 测试 2: 克隆新增策略 (CloneNew) Dim strategyClone As IModuleProcessor Set strategyClone = New cProcessor_CloneNew - Dim initialCount As Integer - initialCount = mockBOMs.count + Dim initialCount As Integer: initialCount = allBOMs.count - strategyClone.ProcessSpec mockBOMs, "弹簧管", paramName, newValue, engine + ' 模拟 UI 选择弹簧管作为克隆母版 + Set targetBOMs = New Collection + targetBOMs.Add row3 - AssertEqual "克隆策略_集合数量增加", CStr(initialCount + 1), CStr(mockBOMs.count) - AssertEqual "克隆策略_新行条件纯粹准确", "lcfw=M19", mockBOMs(mockBOMs.count).Condition - AssertEqual "克隆策略_新行名称继承", "弹簧管", mockBOMs(mockBOMs.count).ItemName + strategyClone.ProcessSpec allBOMs, targetBOMs, paramName, newValue, engine + AssertEqual "克隆策略_总库数量增加", CStr(initialCount + 1), CStr(allBOMs.count) + AssertEqual "克隆策略_新行条件纯粹准确", "lcfw=M19", allBOMs(allBOMs.count).Condition End Sub ' ============================================================================== ' 新增:端到端流水线集成测试 -' 验证 Manager 能否将表数据读取、调用策略并正确写回物理单元格 ' ============================================================================== Private Sub Test_Manager_Pipeline() Debug.Print "" @@ -225,7 +223,6 @@ Private Sub Test_Manager_Pipeline() Dim wsTemp As Worksheet Set wb = ThisWorkbook - ' 创建沙盒工作表并准备初始数据 Application.DisplayAlerts = False On Error Resume Next wb.Worksheets("BOMForge_Test_Pipeline").Delete @@ -233,39 +230,25 @@ Private Sub Test_Manager_Pipeline() Set wsTemp = wb.Worksheets.Add wsTemp.Name = "BOMForge_Test_Pipeline" - ' 表头及数据 wsTemp.Cells(3, 1).value = "行号" + wsTemp.Cells(4, 1).value = 700: wsTemp.Cells(4, 4).value = "径向高压接头": wsTemp.Cells(4, 6).value = "lcfw=M12" + wsTemp.Cells(5, 1).value = 710: wsTemp.Cells(5, 4).value = "径向高压接头": wsTemp.Cells(5, 6).value = "lcfw=M13" + wsTemp.Cells(6, 1).value = 800: wsTemp.Cells(6, 4).value = "弹簧管": wsTemp.Cells(6, 6).value = "lcfw=M01" - ' 第4行:目标物料1 - wsTemp.Cells(4, 1).value = 700 - wsTemp.Cells(4, 4).value = "径向高压接头" ' ItemName - wsTemp.Cells(4, 6).value = "lcfw=M12" ' Condition + ' 模拟 UI 打开时加载数据 + Dim globalBOMs As Collection + Set globalBOMs = mSpecAdditionManager.LoadData(wsTemp) - ' 第5行:目标物料2 (变种) - wsTemp.Cells(5, 1).value = 710 - wsTemp.Cells(5, 4).value = "径向高压接头" - wsTemp.Cells(5, 6).value = "lcfw=M13" + ' 模拟 UI 细粒度勾选:只选中了第5行 (即集合中的第2个对象) + Dim uiSelectedBOMs As New Collection + uiSelectedBOMs.Add globalBOMs(2) - ' 第6行:非目标物料 - wsTemp.Cells(6, 1).value = 800 - wsTemp.Cells(6, 4).value = "弹簧管" - wsTemp.Cells(6, 6).value = "lcfw=M01" + ' 执行调度中枢的追加流水线 + mSpecAdditionManager.ExecutePipeline wsTemp, globalBOMs, uiSelectedBOMs, "lcfw", "M19", "APPEND" - ' ===== 执行调度中枢的追加流水线 ===== - mSpecAdditionManager.ExecuteAddition wsTemp, "径向高压接头", "lcfw", "M19", "APPEND" - - ' 断言 Excel 表格的结果 - AssertEqual "集成测试_追加_目标行1已被更新", "lcfw=M12 OR lcfw=M19", wsTemp.Cells(4, 6).value - AssertEqual "集成测试_追加_目标行2已被更新", "lcfw=M13 OR lcfw=M19", wsTemp.Cells(5, 6).value - AssertEqual "集成测试_追加_非目标行保持不变", "lcfw=M01", wsTemp.Cells(6, 6).value - - ' ===== 执行调度中枢的克隆新增流水线 ===== - mSpecAdditionManager.ExecuteAddition wsTemp, "弹簧管", "lcfw", "M19", "CLONE" - - ' 断言 Excel 表格的结果 (克隆的行应追加在末尾) - AssertEqual "集成测试_克隆_原行保持不变", "lcfw=M01", wsTemp.Cells(6, 6).value - AssertEqual "集成测试_克隆_新增行名称", "弹簧管", wsTemp.Cells(7, 4).value - AssertEqual "集成测试_克隆_新增行条件", "lcfw=M19", wsTemp.Cells(7, 6).value + AssertEqual "流水线_追加_目标行1未选中(不变)", "lcfw=M12", wsTemp.Cells(4, 6).value + AssertEqual "流水线_追加_目标行2被选中更新", "lcfw=M13 OR lcfw=M19", wsTemp.Cells(5, 6).value + AssertEqual "流水线_追加_无关行不变", "lcfw=M01", wsTemp.Cells(6, 6).value wsTemp.Delete Application.DisplayAlerts = True