我在大多数控制器中使用至少两个服务(例如,IVendorService和IFooService)(使用Unity.Mvc5)。 我想为这些控制器创建一个通用帮助器。我正在考虑使用Lazy加载和Unity创建单例类。 我在其中一个博客中读到,统一要求构造函数是公开的。
这样可行,但这并不是真的使用单例模式。
AppHelper.cs
public sealed class AppHelper
{
private static IList<Vendor> _vendorList;
private static IList<Foo> _fooList;
public AppHelper(IFooService fooService, IVendorService vendorService)
{
if (fooService != null) _vendorList = vendorService.GetVendors();
if (vendorService != null) _fooList = fooService.GetFoo();
}
public static IList<Vendor> VendorList
{
get { return _vendorList; }
}
public static IList<Foo> FooList
{
get { return _fooList; }
}
}
我已经尝试过这种方式,它给了我一个编译错误。
AppHelper.cs
public sealed class AppHelper
{
private static readonly Lazy<AppHelper> instance = new Lazy<AppHelper>(() => new AppHelper(fooService, vendorService));
private static IList<Vendor> _vendorList;
private static IList<Foo> _fooList;
private AppHelper(IFooService fooService, IVendorService vendorService)
{
_vendorList = vendorService.GetVendors();
_fooList = fooService.GetFoo();
}
public static AppHelper Instance()
{
return instance.Value;
}
public static IList<Vendor> VendorList
{
get { return _vendorList; }
}
public static IList<Foo> FooList
{
get { return _fooList; }
}
}
有什么建议吗?
答案 0 :(得分:2)
如果对这些属性或方法的需求仅在您的控制器中,那么我将它们作为这些控制器的公共基类的方法(它来自System.Web.Mvc.Controller
,并且您的控制器来自它而不是直接来自Controller
)。
如果需要仅仅是在意见中,我也同样有这些意见。
但你有两个。
一种方法是要么考虑一个可接受的重复(即将它放在两者的基础上),要么让一个类与你的第一个类很相似,这是由那些基类调用的,所以唯一的重复是非常薄的包装方法。
无论你是这样做的,还是只是通过实际需要它们的方法调用了帮助器,你可能比你更懒惰:
private static class CachedElements
{
private static IList<Vendor> _vendorList;
private static IList<Foo> _fooList;
public static IList<Vendor> VendorList(IVendorService vendorService)
{
return _vendorList = _vendorList ?? vendorService.GetVendors();
}
public static IList<Foo> FooList(IFooService fooService)
{
return _fooList = _fooList ?? fooService.GetFoos();
}
}
(我确定你已经知道了,但值得指出的是,这与你问题中的原件分享了一个短暂的时期,其中不止一个可以创建和使用_vendorlist
,每个都替换另一个,因为发现_vendorList
为空,并设置它之间的竞争。由于实际设置是原子的,从长远来看这可能比阻止这种情况发生的努力,但重要的是要知道有几次真的只有一个这样的列表被创造出来。)
这里的优点是:
我们还可以相对轻松地处理以下更改:
public static IList<Foo> FooList(IFooService fooService)
{
//some change to the application has meant this is no longer safe to cache, but
//we only needed to change one piece of code:
return fooService.GetFoos();
}
或者复杂化如下:
private static ConcurrentDictionary<Territory, IList<Vendor>> _vendors = new ConcurrentDictionary<Territory, IList<Vendor>>();
public static IList<Vendor> VendorList(IVendorService vendorSerice, Territory territory)
{
// We now have different lists of vendors for different countries, states, etc.
// We were able to make all of our changes through this place, and keep a similar
// type of caching happening.
return _vendors.GetOrAdd(territory, () => vendorSerice.GetVendors(territory));
}
另外,请注意,根本没有理由在调用代码中明确地处理这个类。 VendorList
和FooList
都可以创建为扩展方法。例如。用:
public static IList<Vendor> VendorList(this IVendorService vendorService)
{
return _vendorList = _vendorList ?? vendorService.GetVendors();
}
现在我们可以调用它,就像它是实现IVendorService
的任何实例方法一样,这是理想的,因为它毕竟只是一种GetVendors
形式提供了一些全局缓存无论如何。同样,实现的全局伪像被隐藏起来,降低了全局依赖性在代码中被进一步推动的风险,或者在未来需求需要时难以改变。