我有一个类,我注入了两个服务依赖项。我正在使用Unity容器。
public interface IOrganizer
{
void Method1();
void Method2();
void Method3();
}
public class Organizer : IOrganizer
{
private IService1 _service1;
private IService2 _service2;
public Organizer(Iservice1 service1, IService2 service2)
{
_service1 = service1;
_service2 = service2;
}
public void Method1()
{
/*makes use of _service1 and _service2 both to serve the purpose*/
}
public void Method2()
{
/*makes use of only _service1 to serve the purpose*/
}
public void Method3()
{
/*makes use of only _service2 to serve the purpose*/
}
}
虽然一切正常,但不知怎的,它闻起来因为当我只调用Method2
和Method3
时,unity会不必要地创建另一个不需要的服务的实例。这里的代码片段只是用于解释目的的示例。在实际情况下,这些注入服务的对象图本身就很深。
有没有更好的方法来设计和解决这种情况?
答案 0 :(得分:5)
我认为你的嗅觉很有能力。大多数人都乐意在没有经过深思熟虑的情况下编写代码。不过,我同意在OP中概述的设计中有一些代码气味。
我想指出我在Refactoring中使用术语代码嗅觉。它表明可能不正确,可能值得进一步调查。有时候,这样的调查表明,有充分的理由让代码保持原样,然后继续前进。
OP中至少有两个独立的气味。他们是无关的,所以我会分别对待每一个。
面向对象设计的一个基本但经常被遗忘的概念是凝聚力。将其视为分离关注点的反制力量。正如肯特贝克曾经说过的那样(确切的来源让我逃避,所以我解释),一起变化的东西属于一起,而独立变化的东西应该分开。
没有'力量'凝聚力,力量'关注点分离将使代码分开,直到你有非常小的类,甚至简单的业务逻辑分布在多个文件中。
寻找凝聚力或缺乏凝聚力的一种方法是计算'一个类的每个方法使用了多少个类字段。虽然只是一个粗略的指标,它确实触发了OP代码中的嗅觉。
Method1
使用两个类字段,因此无需担心。另一方面,Method2
和Method3
都只使用了一半的类字段,因此我们可以将其视为内聚力差的指示 - 如果您愿意,还会产生代码气味。
你怎么解决这个问题?我再次强调,气味并不能保证是坏的。这只是调查的理由。
尽管如此,如果你想解决这个问题,除了将课程分成几个较小的课程外,我无法想到任何其他方式。
OP中的Organizer
类实现了IOrganizer
接口,所以从技术上讲,分解Organizer
只有在你还可以分解界面时才有可能 - 尽管你可以写一个{ {3}},然后将每个方法委托给实现该特定方法的单独类。
但是,界面的存在强调了Facade的重要性。我经常看到代码库出现这个特殊问题,因为接口太大了。如果可能的话,使接口尽可能小。我倾向于把它带到极端,并在每个接口上只定义一个成员。
来自Interface Segregation Principle的另一个SOLID principles,接下来应该由使用它们的客户端定义接口,而不是实现它们的类。设计这样的界面通常可以使它们保持小巧,并且重点突出。
回想一下,单个类可以实现多个接口。
关于OP中的设计的另一个问题是性能,尽管我同意NightOwl888的评论,即您可能处于微优化领域。
一般来说,你可以Dependency Inversion Principle。正如NightOwl888在上面的评论中也暗示的那样,如果一个依赖项具有Singleton生命周期,那么如果你注入它会产生一些差别,但最后却没有使用它。
即使你不能给出_service2
Singleton生命周期之类的依赖关系,我再次同意NightOwl888,即.NET中的对象创建速度很快到你无法衡量它的程度。正如他也指出的那样,compose even large object graphs with confidence。
即使在极少数情况下,依赖项必须具有Transient生存期,并且无论出于何种原因创建实例都很昂贵,您仍然可以将该依赖项隐藏在Injection Constructors should be simple之后,正如我在关于对象图的文章中所描述的那样
如何配置Unity中的所有内容,我不再记得,但如果Unity无法处理,请选择其他合成方法,最好是Virtual Proxy。
答案 1 :(得分:2)
只要您使用Unit 3 or higher,就不需要做任何特殊的事情来解决懒惰问题。
您可以像往常一样注册您的类型:
container.RegisteryType<IMyInterface>()...;
然后将构造函数更改为require lazy:
public class MyClass
{
public Lazy<IMyInterface> _service1;
public MyClass(Lazy<IMyInterface> service1)
{
_serivce1 = service1;
}
}
然后打电话给你需要的任何方法:
_service1.Value.MyMethod();