许多开源项目使用Configuration类和lambda来阐明配置复杂对象。以Mass Transit为例。一个简单的配置就是这样。
Bus.Initialize(sbc =>
{
sbc.UseMsmq();
sbc.VerifyMsmqConfiguration();
sbc.VerifyMsDtcConfiguration();
sbc.UseMulticastSubscriptionClient();
sbc.ReceiveFrom("msmq://localhost/test");
});
当您将鼠标悬停在Visual Studio中的Initialize
上时,它表示方法调用的参数为Action<ServiceBusConfigurator>
。我想知道是否有人能够展示如何在课堂上使用这种模式的简单例子。我甚至不知道该怎么称呼这种模式,而我的“GoogleFu”尚未发挥作用。在这种特殊情况下,我意识到该方法是在单例模式下运行。但我很好,它是一个类的实例方法。
答案 0 :(得分:7)
Action<ServiceBusConfigurator>
是一种方法,它接受ServiceBusConfigurator
类型的单个参数,对该实例执行“操作”操作,并且不返回任何内容(void
)。
.NET BCL(从3.5开始)带有预定义的通用委托签名:Action<T>
,Action<T1, T2>
(等),用于不返回值的方法,Func<Tresult>
, Func<T, Tresult>
(等)用于接受零个更多参数并返回类型Tresult
的单个结果实例的方法。
当您创建一个接受委托的方法时,您允许方法的调用者传递的不仅仅是数据参数 - 您的方法实际上将一部分责任委托给外部代码。在您的情况下,Bus.Initialize
创建ServiceBusConfigurator
(sbc
)的实例,然后使用sbc
实例作为参数调用指定的操作。
这基本上允许您的方法控制配置类实例的生命周期。由呼叫者填写详细信息,但实际的实例由您的班级提供:
// this is not actual mass transit source code
public class BusCreator
{
public static IBus Initialize(Action<IConfiguration> action)
{
// create the config instance here
IConfiguration config = CreateDefaultConfig();
// let callers modify it
action(config);
// use the final version to build the result
return config.Build()
}
}
好处是您的构建实例(在这种情况下是虚构的IBus
)无法进一步修改。配置实例仅在短时间内创建,传递给外部方法,然后用于创建不可变的最终对象:
IBus result = BusCreator.Configure(cfg => cfg.BusType = BusType.MSMQ);
上面两行要注意的事项:
匿名方法中的代码包含在传递给方法的委托中。在Configure
方法实际调用它之前,它不会执行。
cfg
参数由Configure
方法创建并传递给lambda。方法返回后,此对象不再存在(或包含在生成的对象中)。
答案 1 :(得分:1)
要添加其他人所说的内容,这是fluent interface的“切入点”。使用Action回调来实现这一目标的方法是一种以同时非常可扩展的方式隔离流畅接口的好方法。
答案 2 :(得分:0)
这类似于工作单元模式,它通常与事务和持久性方案相关联,但似乎适合您的示例。
以下引文来自Martin Fowler's definition of the pattern:
工作单元会跟踪您在业务事务中可能影响数据库的所有操作。完成后,它会计算出因工作原因需要更改数据库的所有内容。
如果为配置更改初始化和数据库的业务事务,您可以更好地了解这是怎么回事。另外,将动作(代理情况)视为原子操作:新配置完全应用,或者当前配置保持不变。
如上所述,该操作本身并未明确触及Bus
。即使不知道所涉及的类的细节,我们也可以猜测这种交互是如何发生的:
ServiceBusConfigurator
方法返回(最有可能)之前,可以在调用操作后读取Initializa()
; Bus
可能会实现/扩展ServiceBusConfigurator
,以便Initialize()
可以将this
作为被调用操作的参数传递(不太可能); Bus
可能是静态的,ServiceBusConfigurator
可见,而Bus
可能会在调用ReceiveFrom()
时更改{{1}}的配置属性(非常复杂,我希望,非常不可能)。这些是刚刚出现在我脑海中的一些策略。可能会建议许多其他人!