Pharo框架下如何用随机生成值替换通用类初始化方法参数以实例化对象
针对你的问题,我们可以利用Smalltalk的动态消息发送机制,结合实例变量与初始化参数的映射,轻松实现随机值替换初始化参数的需求。下面分两种场景给出具体解决方案:
一、已知初始化方法直接调用(简单场景)
如果已经明确知道目标类的初始化方法选择器(比如Person的withName:andBirthDate:andLastName:),且随机值的顺序和方法参数顺序完全匹配,直接用perform:withArguments:方法动态发送消息即可:
| newPerson | newPerson := Person perform: #withName:andBirthDate:andLastName: withArguments: {'toto'. '13 sept 2022'. 'tata'}.
这个方法的核心是perform:withArguments:——它会根据传入的方法选择器和参数数组,自动匹配并调用对应的初始化方法,效果和你手动写Person withName:'toto' andBirthDate:'13 sept 2022' andLastName:'tata'完全一致。
二、通用场景:自动匹配参数与实例变量
如果需要适配任意类,不想硬编码方法选择器和参数顺序,可以通过解析初始化方法的参数名,自动匹配实例变量对应的随机值:
步骤1:准备实例变量与随机值的映射
先把生成的随机值和对应的实例变量绑定成字典:
| varToValueMap | varToValueMap := Dictionary new at: #name put: 'toto'; at: #birthDate put: '13 sept 2022'; at: #lastName put: 'tata'; yourself.
步骤2:解析初始化方法的参数名
获取目标类的初始化方法,提取它的参数列表:
| personClass initSelector initMethod paramNames | personClass := Person. initSelector := #withName:andBirthDate:andLastName:. initMethod := personClass methodDict at: initSelector. paramNames := initMethod argumentNames. "返回 #('aName' 'aBirthDate' 'aLastName')"
步骤3:匹配参数与随机值
根据参数名的命名约定(比如aName对应实例变量name),把参数转换为实例变量名,再从字典中取出对应随机值:
| args | args := paramNames collect: [ :param | | varName | varName := param allButFirst asSymbol. "去掉参数名的前缀'a',得到实例变量名" varToValueMap at: varName ].
步骤4:动态创建实例
最后用perform:withArguments:调用初始化方法:
| newPerson | newPerson := personClass perform: initSelector withArguments: args.
三、备选方案:直接赋值实例变量
如果不想依赖类的初始化方法,也可以跳过初始化方法,直接给实例变量赋值(注意用basicNew创建未初始化的实例):
| newPerson | newPerson := Person basicNew. "basicNew不会自动调用initialize方法" newPerson instVarNamed: #name put: 'toto'. newPerson instVarNamed: #lastName put: 'tata'. newPerson instVarNamed: #birthDate put: '13 sept 2022'. "如果类有必要的initialize逻辑,记得手动调用:newPerson initialize."
这种方式更灵活,但要注意:如果类的初始化方法包含参数验证、默认值设置等逻辑,直接赋值会绕过这些步骤,需要根据实际场景权衡使用。
内容的提问来源于stack exchange,提问作者Mansour Faouziel

