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 <noreply@anthropic.com>
This commit is contained in:
Misaka_Company
2026-03-19 15:04:24 +08:00
parent d17d7c5864
commit b73e39eeee
5 changed files with 92 additions and 109 deletions

View File

@@ -1,16 +1,22 @@
' ============================================================================== ' ==============================================================================
' 模块名称: IModuleProcessor ' 模块名称: IModuleProcessor
' 模块类别: 类模块 (用作接口 Interface) ' 模块类别: 类模块 (用作接口 Interface)
' 模块职责: 定义统一的业务处理接口基于UI用户选择的物料名称进行精准处理 ' 模块职责: 定义统一的业务处理接口。此时的策略层已完全解耦,不关心过滤逻辑。
' ============================================================================== ' ==============================================================================
Option Explicit Option Explicit
' 方法名: ProcessSpec ' 方法名: ProcessSpec
' allBOMs: 包含所有物料信息的集合 (库里的所有数据) ' allBOMs: 总库集合 (主要用于克隆策略追加新行)
' targetItemName: 要被处理的物料的名称字段 (瞄准镜) ' targetBOMs: 由UI层(或中枢)精挑细选出来的目标行集合 (手术对象)
' paramName: 参数名(如 lcfw) ' paramName: 参数名(如 lcfw)
' newValue: 新规格代码(如 M19) ' newValue: 新规格代码(如 M19)
' engine: 条件注入引擎实例 ' engine: 条件注入引擎实例
Public Sub ProcessSpec(ByRef allBOMs As Collection, ByVal targetItemName As String, ByVal paramName As String, ByVal newValue As String, ByRef engine As cConditionEngine) 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 End Sub

View File

@@ -1,28 +1,24 @@
' ============================================================================== ' ==============================================================================
' 模块名称: cProcessor_AppendOnly ' 模块名称: cProcessor_AppendOnly
' 模块类别: 类模块 ' 模块类别: 类模块
' 模块职责: 通用追加策略。适用于接头、封口片、机芯、盘止钉等。 ' 模块职责: 通用追加策略。
' 逻辑描述: 遍历所有数据,凡是名称与目标匹配的变种,一律注入新条件 ' 逻辑描述: 无脑遍历传入的 targetBOMs 集合,利用引擎进行条件追加
' ============================================================================== ' ==============================================================================
Option Explicit Option Explicit
Implements IModuleProcessor Implements IModuleProcessor
Private Sub IModuleProcessor_ProcessSpec( _ Private Sub IModuleProcessor_ProcessSpec( _
ByRef allBOMs As Collection, _ ByRef allBOMs As Collection, _
ByVal targetItemName As String, _ ByRef targetBOMs As Collection, _
ByVal paramName As String, _ ByVal paramName As String, _
ByVal newValue As String, _ ByVal newValue As String, _
ByRef engine As cConditionEngine) ByRef engine As cConditionEngine)
Dim rowObj As cBOMRow Dim rowObj As cBOMRow
' 遍历整个库 ' UI targetBOMs
For Each rowObj In allBOMs For Each rowObj In targetBOMs
' 只要名称完全匹配 (忽略大小写进行比对) rowObj.Condition = engine.InjectOr(rowObj.Condition, paramName, newValue)
If StrComp(rowObj.ItemName, targetItemName, vbTextCompare) = 0 Then
'
rowObj.Condition = engine.InjectOr(rowObj.Condition, paramName, newValue)
End If
Next rowObj Next rowObj
End Sub End Sub

View File

@@ -1,39 +1,29 @@
' ============================================================================== ' ==============================================================================
' 模块名称: cProcessor_CloneNew ' 模块名称: cProcessor_CloneNew
' 模块类别: 类模块 ' 模块类别: 类模块
' 模块职责: 通用克隆新增策略。适用于弹性元件等需要完全独立成行的规格。 ' 模块职责: 通用克隆新增策略。
' 逻辑描述: 找到第一个匹配的母版行,克隆它并赋予纯粹的新规格条件 ' 逻辑描述: 遍历传入的 targetBOMs 母版集合,每遇到一个就克隆一行追加到总库,并赋新值
' ============================================================================== ' ==============================================================================
Option Explicit Option Explicit
Implements IModuleProcessor Implements IModuleProcessor
Private Sub IModuleProcessor_ProcessSpec( _ Private Sub IModuleProcessor_ProcessSpec( _
ByRef allBOMs As Collection, _ ByRef allBOMs As Collection, _
ByVal targetItemName As String, _ ByRef targetBOMs As Collection, _
ByVal paramName As String, _ ByVal paramName As String, _
ByVal newValue As String, _ ByVal newValue As String, _
ByRef engine As cConditionEngine) ByRef engine As cConditionEngine)
Dim rowObj As cBOMRow Dim rowObj As cBOMRow
Dim templateRow As cBOMRow Dim newRow As cBOMRow
Set templateRow = Nothing
' 1. 寻找母版 (找到同名的第一条记录即可) ' 为 targetBOMs 里的每一个母版克隆出一个新对象
For Each rowObj In allBOMs For Each rowObj In targetBOMs
If StrComp(rowObj.ItemName, targetItemName, vbTextCompare) = 0 Then ' 调用数据访问层的克隆方法,自动加入 allBOMs 总集合
Set templateRow = rowObj Set newRow = mBOMRepository.InsertNewBOMRow(allBOMs, rowObj)
Exit For
End If ' (lcfw=M19)
newRow.Condition = paramName & "=" & newValue
Next rowObj 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 End Sub

View File

@@ -6,45 +6,53 @@
Option Explicit Option Explicit
' ------------------------------------------------------------------------------ ' ------------------------------------------------------------------------------
' 过程名称: ExecuteAddition ' 过程名称: LoadData
' 过程功能: 供 UI 初始化时调用,加载总库数据
' ------------------------------------------------------------------------------
Public Function LoadData(ByVal ws As Worksheet) As Collection
Set LoadData = mBOMRepository.LoadAllBOMs(ws)
End Function
' ------------------------------------------------------------------------------
' 过程名称: ExecutePipeline
' 过程功能: 执行完整的新增规格流水线 ' 过程功能: 执行完整的新增规格流水线
' 参数说明: ' 参数说明:
' ws - 目标工作表对象 ' ws - 目标工作表对象 (保存时需要)
' targetItemName - 用户瞄准的物料名称 (如 "径向高压接头") ' allBOMs - 内存中的总库集合
' paramName - 要修改的参数名 (如 "lcfw") ' targetBOMs - UI层过滤组装好的【精准目标集合】
' newValue - 新增的规格值 (如 "M19") ' paramName - 要修改的参数名 (如 "lcfw")
' strategyType - UI选择的执行策略 (如 "APPEND" 或 "CLONE") ' newValue - 新增的规格值 (如 "M19")
' strategyType - UI选择的执行策略 (如 "APPEND" 或 "CLONE")
' ------------------------------------------------------------------------------ ' ------------------------------------------------------------------------------
Public Sub ExecuteAddition( _ Public Sub ExecutePipeline( _
ByVal ws As Worksheet, _ ByVal ws As Worksheet, _
ByVal targetItemName As String, _ ByRef allBOMs As Collection, _
ByRef targetBOMs As Collection, _
ByVal paramName As String, _ ByVal paramName As String, _
ByVal newValue As String, _ ByVal newValue As String, _
ByVal strategyType 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 engine As cConditionEngine
Dim bomCollection As Collection
Dim processor As IModuleProcessor Dim processor As IModuleProcessor
' [步骤 1] 初始化核心手术刀引擎 ' [步骤 1] 初始化核心手术刀引擎
Set engine = New cConditionEngine Set engine = New cConditionEngine
' [步骤 2] 通过数据层将 Excel 读入内存变为对象集合 ' [步骤 2] 从工厂获取用户指定的策略
Set bomCollection = mBOMRepository.LoadAllBOMs(ws)
' [步骤 3] 从工厂获取用户指定的策略
Set processor = mProcessorFactory.GetProcessor(strategyType) Set processor = mProcessorFactory.GetProcessor(strategyType)
' [步骤 4] 将数据集合与目标扔给策略对象执行 ' [步骤 3] 将总库、精准目标集合扔给策略对象执行
' 提示:所有的脏标记 (IsDirty) 会在这一步由对象自己触发记录 processor.ProcessSpec allBOMs, targetBOMs, paramName, newValue, engine
processor.ProcessSpec bomCollection, targetItemName, paramName, newValue, engine
' [步骤 5] 将修改过的数据 (IsDirty=True) 批量写回 Excel ' [步骤 4] 将修改过的数据批量写回 Excel (只写 IsDirty=True 的对象)
mBOMRepository.SaveAll ws, bomCollection mBOMRepository.SaveAll ws, allBOMs
' 释放资源 (VBA 虽然有垃圾回收,但良好习惯可以防止 Excel 卡死) ' 释放资源
Set processor = Nothing Set processor = Nothing
Set bomCollection = Nothing
Set engine = Nothing Set engine = Nothing
End Sub End Sub

View File

@@ -167,7 +167,7 @@ End Sub
Private Sub Test_Processors() Private Sub Test_Processors()
Debug.Print "" Debug.Print ""
Debug.Print "========== 4. 开始测试: 业务策略层 (Strategy) 新架构 ==========" Debug.Print "========== 4. 开始测试: 业务策略层 (Strategy) 细粒度新架构 =========="
Dim engine As cConditionEngine Dim engine As cConditionEngine
Set engine = New cConditionEngine Set engine = New cConditionEngine
@@ -175,47 +175,45 @@ Private Sub Test_Processors()
Dim paramName As String: paramName = "lcfw" Dim paramName As String: paramName = "lcfw"
Dim newValue As String: newValue = "M19" 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 Dim row1 As New cBOMRow, row2 As New cBOMRow, row3 As New cBOMRow
row1.ItemName = "径向高压接头" row1.ItemName = "径向高压接头": row1.Condition = "lcfw=M12": allBOMs.Add row1
row1.Condition = "lcfw=M12" row2.ItemName = "径向高压接头": row2.Condition = "lcfw=M13": allBOMs.Add row2
mockBOMs.Add row1 row3.ItemName = "弹簧管": row3.Condition = "lcfw=M01 AND gclj!=M20": allBOMs.Add row3
row2.ItemName = "径向高压接头" ' 【核心逻辑】模拟 UI 层仅将用户勾选的具体行 (比如 row1) 加入 targetBOMs 集合
row2.Condition = "lcfw=M13" targetBOMs.Add row1
mockBOMs.Add row2
row3.ItemName = "弹簧管"
row3.Condition = "lcfw=M01 AND gclj!=M20"
mockBOMs.Add row3
' 测试 1: 追加策略 (AppendOnly)
Dim strategyAppend As IModuleProcessor Dim strategyAppend As IModuleProcessor
Set strategyAppend = New cProcessor_AppendOnly 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 "追加策略_选中的变种1被更新", "lcfw=M12 OR lcfw=M19", row1.Condition
AssertEqual "追加策略_变种2被更新", "lcfw=M13 OR lcfw=M19", row2.Condition AssertEqual "追加策略_未选中的变种2保持不变", "lcfw=M13", row2.Condition
AssertEqual "追加策略_不影响其他物料", "lcfw=M01 AND gclj!=M20", row3.Condition AssertEqual "追加策略_不影响其他物料", "lcfw=M01 AND gclj!=M20", row3.Condition
' 测试 2: 克隆新增策略 (CloneNew)
Dim strategyClone As IModuleProcessor Dim strategyClone As IModuleProcessor
Set strategyClone = New cProcessor_CloneNew Set strategyClone = New cProcessor_CloneNew
Dim initialCount As Integer Dim initialCount As Integer: initialCount = allBOMs.count
initialCount = mockBOMs.count
strategyClone.ProcessSpec mockBOMs, "弹簧管", paramName, newValue, engine ' 模拟 UI 选择弹簧管作为克隆母版
Set targetBOMs = New Collection
targetBOMs.Add row3
AssertEqual "克隆策略_集合数量增加", CStr(initialCount + 1), CStr(mockBOMs.count) strategyClone.ProcessSpec allBOMs, targetBOMs, paramName, newValue, engine
AssertEqual "克隆策略_新行条件纯粹准确", "lcfw=M19", mockBOMs(mockBOMs.count).Condition
AssertEqual "克隆策略_新行名称继承", "弹簧管", mockBOMs(mockBOMs.count).ItemName
AssertEqual "克隆策略_总库数量增加", CStr(initialCount + 1), CStr(allBOMs.count)
AssertEqual "克隆策略_新行条件纯粹准确", "lcfw=M19", allBOMs(allBOMs.count).Condition
End Sub End Sub
' ============================================================================== ' ==============================================================================
' 新增:端到端流水线集成测试 ' 新增:端到端流水线集成测试
' 验证 Manager 能否将表数据读取、调用策略并正确写回物理单元格
' ============================================================================== ' ==============================================================================
Private Sub Test_Manager_Pipeline() Private Sub Test_Manager_Pipeline()
Debug.Print "" Debug.Print ""
@@ -225,7 +223,6 @@ Private Sub Test_Manager_Pipeline()
Dim wsTemp As Worksheet Dim wsTemp As Worksheet
Set wb = ThisWorkbook Set wb = ThisWorkbook
' 创建沙盒工作表并准备初始数据
Application.DisplayAlerts = False Application.DisplayAlerts = False
On Error Resume Next On Error Resume Next
wb.Worksheets("BOMForge_Test_Pipeline").Delete wb.Worksheets("BOMForge_Test_Pipeline").Delete
@@ -233,39 +230,25 @@ Private Sub Test_Manager_Pipeline()
Set wsTemp = wb.Worksheets.Add Set wsTemp = wb.Worksheets.Add
wsTemp.Name = "BOMForge_Test_Pipeline" wsTemp.Name = "BOMForge_Test_Pipeline"
' 表头及数据
wsTemp.Cells(3, 1).value = "行号" 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 ' 模拟 UI 打开时加载数据
wsTemp.Cells(4, 1).value = 700 Dim globalBOMs As Collection
wsTemp.Cells(4, 4).value = "径向高压接头" ' ItemName Set globalBOMs = mSpecAdditionManager.LoadData(wsTemp)
wsTemp.Cells(4, 6).value = "lcfw=M12" ' Condition
' 第5行目标物料2 (变种) ' 模拟 UI 细粒度勾选只选中了第5行 (即集合中的第2个对象)
wsTemp.Cells(5, 1).value = 710 Dim uiSelectedBOMs As New Collection
wsTemp.Cells(5, 4).value = "径向高压接头" uiSelectedBOMs.Add globalBOMs(2)
wsTemp.Cells(5, 6).value = "lcfw=M13"
' 第6行非目标物料 ' 执行调度中枢的追加流水线
wsTemp.Cells(6, 1).value = 800 mSpecAdditionManager.ExecutePipeline wsTemp, globalBOMs, uiSelectedBOMs, "lcfw", "M19", "APPEND"
wsTemp.Cells(6, 4).value = "弹簧管"
wsTemp.Cells(6, 6).value = "lcfw=M01"
' ===== 执行调度中枢的追加流水线 ===== AssertEqual "流水线_追加_目标行1未选中(不变)", "lcfw=M12", wsTemp.Cells(4, 6).value
mSpecAdditionManager.ExecuteAddition wsTemp, "径向高压接头", "lcfw", "M19", "APPEND" AssertEqual "流水线_追加_目标行2被选中更新", "lcfw=M13 OR lcfw=M19", wsTemp.Cells(5, 6).value
AssertEqual "流水线_追加_无关行不变", "lcfw=M01", wsTemp.Cells(6, 6).value
' 断言 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
wsTemp.Delete wsTemp.Delete
Application.DisplayAlerts = True Application.DisplayAlerts = True