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:
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user