问题描述
在学习了带有典型Coffee示例的Decorator模式后,Decorator Pattern使我们从类爆炸问题中解脱出来,然后,我编写了一些代码供自己使用和查看.首先让我们看一下UML ...
After learning about the Decorator Pattern with typical Coffee example where Decorator Pattern saves us from the class explosion problem, I wrote some code to use and see it myself. Lets take a look at the UML first...
这是代码:
组件
ICofee
定义:
public interface ICoffee
{
string Name { get; }
decimal Cost { get; }
}
LatteCoffee
定义:
public class LatteCoffee : ICoffee
{
public string Name { get; } = "Latte";
public decimal Cost => 2.00m;
}
装饰器
IAddOnDecorator
定义:
public interface IAddOnDecorator : ICoffee
{
ICoffee BaseCoffee { set; }
}
CaramelDecorator
定义:
public class CaramelDecorator : IAddOnDecorator
{
public ICoffee BaseCoffee { private get; set; }
public string Name { get; } = "Caramel";
public decimal Cost => BaseCoffee.Cost + 0.5m;
}
AlmondSyrupDecorator
定义:
public class AlmondSyrupDecorator : IAddOnDecorator
{
public ICoffee BaseCoffee { private get; set; }
public string Name { get; } = "AlmondSyrup";
public decimal Cost => BaseCoffee.Cost + 0.3m;
}
您会看到装饰器没有使用构造器中注入的 ICoffee
,而是有一个setter属性 ICoffee BaseCoffee
.
You can see that the decorators are not taking ICoffee
injected in the constructor instead, there is a setter property ICoffee BaseCoffee
.
我想将构造函数注入到组件( ICoffee
)的装饰器( IAddOnDecorator
)中,但这是推荐的方法,但是,我无法通过单元测试方法传递具体对象.
I would like to use the constructor injection into the decorator (IAddOnDecorator
)for the component (ICoffee
) which is the recommended way, however, I am then unable to pass in the concrete object in the unit test method.
用法
[TestFixture]
public class CoffeeTests
{
private IServiceProvider provider;
private IServiceCollection services;
private IDictionary<string, ICoffee> coffeeMapper;
private IDictionary<string, IAddOnDecorator> addonMapper;
[SetUp]
public void Setup()
{
services = new ServiceCollection();
services.AddTransient<ICoffee, LatteCoffee>();
services.AddTransient<IAddOnDecorator, CaramelDecorator>();
services.AddTransient<IAddOnDecorator, AlmondSyrupDecorator>();
provider = services.BuildServiceProvider();
}
[Test]
public void LatteWithCaramelAndAlmodSyrupShouldReturnTheTotalPriceOfCoffeeAndItsAddOns()
{
string coffee = "Latte";
IEnumerable<string> addOns = new List<string> { "Caramel", "AlmondSyrup" };
IEnumerable<ICoffee> allCoffees = provider.GetServices<ICoffee>();
coffeeMapper = allCoffees.ToDictionary(c => c.Name, c => c);
ICoffee selectedCoffee = coffeeMapper[coffee];
IEnumerable<IAddOnDecorator> resolvedDecorators = provider.GetServices<IAddOnDecorator>();
IList<IAddOnDecorator> selectedDecorators = new List<IAddOnDecorator>();
addonMapper = resolvedDecorators .ToDictionary(a => a.Name, a => a);
IAddOnDecorator firstAddon = addonMapper[addOns.First()];
firstAddon.BaseCoffee = selectedCoffee;
selectedDecorators.Add(firstAddon);
foreach (string nextAddon in addOns.Where(a => a != firstAddon.Name))
{
IAddOnDecorator decorator = addonMapper[nextAddon];
decorator.BaseCoffee = selectedDecorators.Last();
selectedDecorators.Add(decorator);
}
// Act.
decimal totalCost = selectedDecorators.Last().Cost;
// Assert.
Assert.That(2.80m, Is.EqualTo(totalCost));
}
}
我的问题:
如何使用传递到.net核心Decorator类的构造函数中的 ICoffee
对象的特定实例来解析 IAddOnDecorator
?我不想使用 ICoffee BaseCoffee {private get;放;}
属性.
How can I resolve IAddOnDecorator
using a particular instance of ICoffee
object passing into the constructor of Decorator class in .net core? I do not want to use ICoffee BaseCoffee { private get; set; }
property.
推荐答案
不幸的是,.Net核心中的默认IoC容器不支持装饰,因此我不得不将注意力转向其他可用选项.因为我已经使用过结构图,并且我喜欢它的"Convention over Configuration"策略,所以我决定尝试一下.下面的代码实现了我一直在寻找的...它不是完美的,但是我允许我通过注入另一个装饰器或组件的实例来实例化装饰器.
Unfortunately, the default IoC Container in .Net core doesnt support decoration so I had to turn my attention to other available options. Since I have already used Structure Map and that I like its "Convention over Configuration" strategy I decided to try it. Following code achieves that I was looking for... Its not perfect but I allows me to instantiate decorator by inject an instance of another decorator or component.
注意:我添加了另一个装饰器 SaltedCaramelDecorator
只是为了使其更加有趣...
Note: I have added another decorator SaltedCaramelDecorator
just to keep it more interesting...
// Arrange.
Container container = new Container();
container.Configure(config =>
{
// register coffees / components
config.For<ICoffee>().Use<LatteCoffee>().Named("Latte");
config.For<ICoffee>().Use<CappuccinoCoffee>().Named("Cappuccino");
// register addOns / decorators
config.For<IAddOnDecorator>().Use<CaramelDecorator>().Named("Caramel");
config.For<IAddOnDecorator>().Use<AlmondSyrupDecorator>().Named("Almond");
config.For<IAddOnDecorator>().Use<SaltedCaramelDecorator>().Named("SaltedCaramel");
});
const string coffeeName = "Latte";
IEnumerable<string> coffeeDecoratorNames = new List<string> { "SaltedCaramel", "Almond", "Caramel" };
// Act.
ICoffee theCoffee = container.GetInstance<ICoffee>(coffeeName);
if (coffeeDecoratorNames.Any())
{
// set the baseCofee as argument to the next decorator / addon.
ExplicitArguments baseCoffee = new ExplicitArguments();
baseCoffee.Set<ICoffee>(theCoffee);
foreach (string nextDeco in coffeeDecoratorNames)
{
ExplicitArguments addOn = new ExplicitArguments();
addOn.Set<ICoffee>(theCoffee);
theCoffee = container.GetInstance<IAddOnDecorator>(addOn, nextDeco);
}
}
// Assert.
Assert.That(3.20m, Is.EqualTo(theCoffee.Cost));
感谢@Steven提供帮助.我希望其他人对这篇文章有帮助.
Thanks for @Steven for helping comments. I hope someone else would find this post helpful.
这篇关于如何使用.net核心中组件的特定实例或另一个装饰器对象解析装饰器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!