请注意,我已更改了问题中的代码。
请参阅下面的服务器端代码(WCF服务):
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
namespace WcfService1
{
public class WindsorInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IGreeting, Greeting>(),
Component.For<IGreetingService, GreetingService>());
}
}
public interface ILanguage
{
string SayHello();
}
public class Spanish : ILanguage
{
public string SayHello()
{
return "Hola";
}
}
public interface IGreeting
{
string SayHello();
}
public class Greeting: IGreeting
{
ILanguage Language;
public Greeting (ILanguage language)
{
Language = language;
}
public string SayHello()
{
return Language.SayHello();
}
}
public interface IGreetingFactory
{
IGreeting Create(ILanguage Language);
}
[ServiceContract]
public interface IGreetingService
{
[OperationContract]
string SayHello(string strLanguage);
}
public class GreetingService : IGreetingService
{
private readonly IGreetingFactory greetingFactory;
private IGreeting greeting;
public GreetingService()
{
}
public GreetingService(IGreetingFactory greetingFactory)
{
// store the factory until we need it
this.greetingFactory = greetingFactory;
}
public string SayHello (string strLanguage)
{
if (strLanguage == "S")
{
ILanguage Language = new Spanish();
Language = new Spanish();
greeting = new Greeting(Language);
}
return greeting.SayHello();
}
}
}
以及下面的客户端代码:
ServiceReference1.GreetingServiceClient s1 = new ServiceReference1.GreetingServiceClient();
string greeting = s1.SayHello("S");
ServiceReference1.GreetingServiceClient
是服务参考。
代码按照我的预期工作,即Castle Windsor允许我向服务的构造函数注入一个Greeting。但是,Greeting类本身有一个参数化构造函数(它需要一个语言)。在上面的代码中,我必须在服务的Say Hello
方法中初始化Greeting(使用语言)。如何在服务的构造函数中初始化Greeting(使用语言)?
答案 0 :(得分:1)
提供运行时,用户驱动或其他动态依赖关系的一个主要方法是使用factories来创建对象。 Castle Windsor提供了几种不同的设施来帮助您,或者您可以使用内核并自己实施工厂。
Windsor的工具允许您提供delegate-based工厂,这些工厂只是创建对象的方法。你可以在这里使用它,但是你在创建的内容上失去了一些灵活性(如果你要将ICalculator
的实现替换为其他类,你必须更新这个方法)。
为了获得最大的灵活性,您需要使用Windsor的interface-based工厂。通过这些,您可以提供工厂的界面,然后Windsor将自动生成它的实现。
让我们使用上面代码的简化版本作为示例。如果你有这个对象:
public class Calculator : ICalculator
{
string Operator;
public Calculator(string operator)
{
Operator=operator;
}
}
你想在创建对象时传递operator
,你就像这样定义一个工厂:
public interface ICalculatorFactory
{
ICalculator Create(string operator);
}
然后您在安装程序中注册它:
kernel.Register(Component.For<ICalulatorFactory>().AsFactory());
现在,无论你想在哪里使用计算器,都要为它注入工厂,然后只需调用Create
:
public class CalculatorUseExample
{
private readonly ICalculator addCalculator;
private readonly ICalculator subCalculator;
public CalculatorUseExample(ICalculatorFactory calculatorFactory)
{
addCalculator = calculatorFactory.Create("+");
subCalculator = calculatorFactory.Create("-");
}
}
请注意operator
参数的名称很重要;默认情况下(如果需要,可以更改此项),Windsor按名称匹配参数。
如果我们将您的CalculatorService
课程添加回混音中,您可以使用相同的模式:
public interface ICalculatorServiceFactory
{
ICalculatorService Create(string operator);
}
public class CalculatorService : ICalculatorService
{
private readonly ICalculator Calculator;
public CalculatorService(string operator, ICalculatorFactory calculatorFactory)
{
Calculator=calculatorFactory.Create(operator);
}
}
但我不是那么喜欢,因为服务为什么要关心运营商呢?这是计算器的细节。相反,请将工厂更改为仅接受ICalculator
并将对象组合在一起,以便您创建此服务:
public interface ICalculatorServiceFactory
{
ICalculatorService Create(ICalculator calculator);
}
public class CalculatorService : ICalculatorService
{
private readonly ICalculator Calculator;
public CalculatorService(ICalculator calculator)
{
Calculator=calculator;
}
}
public class CalculatorServiceUseExample
{
public CalculatorServiceUseExample(ICalculatorServiceFactory calculatorServiceFactory, ICalculatorFactory calculatorFactory)
{
var addCalculator = calculatorFactory.Create("+");
var service = calculatorServiceFactory.Create(addCalculator);
// TODO: use the service
}
}
使用此模式有优点和缺点,我在my answer here中查看。一些优点是您可以保护自己免受未来的更改并避免服务定位器模式。缺点包括界面对象的激增和工厂的潜在病毒使用(参见我上面的第一个解决方案,我们必须创建另一个工厂)。
*当然还有其他一些,这就是我解决这种特殊情况的方式,因为对我而言,它表明了意图,是您代码读者最容易被发现的。
根据您对WCF的修改以及我理解您正在尝试做的事情,我会像这样实施服务合同:
public class CalculatorService : ICalculatorService
{
private readonly ICalculatorFactory calculatorFactory;
private ICalculator calculator;
public CalculatorService(ICalculatorFactory calculatorFactory)
{
// store the factory until we need it
this.calculatorFactory = calculatorFactory;
}
public void ChangeCalculatorServiceClient(string operator)
{
// A new operator, we'll need a new calculator
calculator = calculatorFactory.Create(operator);
}
}
嗯,你再次改变了你的问题,包括另一个皱纹;现在您想基于参数实例化不同的类型。您可以而且应该仍然使用工厂,这就是我要做的事情:
using Castle.Facilities.TypedFactory;
public class WindsorInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IGreeting, Greeting>(),
Component.For<IGreetingFactory>().AsFactory(),
Component.For<IGreetingService, GreetingService>(),
Component.For<ILanguageFactory, LanguageFactory>());
}
}
public interface ILanguageFactory
{
ILanguage Create(string language);
}
public class LanguageFactory : ILanguageFactory
{
private readonly IKernel kernel;
public LanguageFactory(IKernel kernel)
{
this.kernel = kernel;
}
public ILanguage Create(string language)
{
switch (language)
{
case "S":
return kernel.Resolve<Spanish>();
default:
throw new ArgumentException();
}
}
}
public class GreetingService : IGreetingService
{
private readonly IGreetingFactory greetingFactory;
private readonly ILanguageFactory languageFactory;
private IGreeting greeting;
public GreetingService(IGreetingFactory greetingFactory, ILanguageFactory languageFactory)
{
// store the factory until we need it
this.greetingFactory = greetingFactory;
}
public string SayHello (string strLanguage)
{
var language = languageFactory.Create(strLanguage);
greeting = greetingFactory.Create(language);
return greeting.SayHello();
}
}