首先,我想提一下,我不太清楚描述当前问题的术语是什么,但我希望我能用一些代码来说明它。
问题:共享类库定义了一个接口,引用这个库的每个应用程序都提供了它自己的接口实现。现在,在共享库中,我想调用该方法的实际实现。
为了解决这个问题,我设想了一个简单的场景,即两个特定于平台的应用程序Windows,Android共享一些代码,除了需要为每个系统单独实现的一些特定于平台的功能。在运行时,我需要透明地调用该实现,因为它已经由实现者提供。
三个控制台应用程序被创建为共享的,Windows,Android
共享项目定义接口IShared
namespace Project
{
public interface IShared
{
void Speak(string word);
}
}安卓系统实现IShared
namespace Project.Android
{
public class SpeakAndroid: IShared
{
public void Speak(string word)
{
Console.WriteLine("Android Speaks");
}
}
}Windows实现IShared
namespace Project.Windows
{
public class SpeakWindows: IShared
{
public void Speak(string word)
{
Console.WriteLine("Speak Windows");
}
}
}现在,为了使共享项目能够调用每个特定于平台的Speak函数,我决定声明
public static IShared SharedDefinition { get; set; }在Windows和Android项目中分配一个新的实现,如下所示
Windows:
Project.Program.SharedDefinition = new SpeakWindows();安多伊德:
Project.Program.SharedDefinition = new SpeakAndroid();这样,在共享项目中,我可以调用Program.SharedDefinition.Speak();,根据运行平台的不同,我将得到不同的Speak实现。
问题(S):
发布于 2018-09-02 00:48:07
这是解决抽象问题的有效方法:它确保库不依赖于具体的实现。
这项技术的一般术语称为未定义。但是,您使用的不是注入依赖性进入需要它的对象或函数,而是使用全局中介。这样的中介是一个未定义。
服务定位器通常封装对几种不同类型的服务的访问,以及对每个服务的访问。然后,这个场景比您的情况要复杂得多:
但是在您的例子中,定位器被简化到极致,因为您只公开了一个服务:
IShared服务是通过全局静态对象的setter使用-context注册的。在这篇文章中,您将发现这两个极端之间的服务定位器的另一个例子。(我在这里更多地提到这一点,因为它非常明确地区分了依赖反转、依赖注入和控制反转)
服务定位器的主要不便在于,所有库类都可能依赖它,可能会在库中创建比必要的更高的耦合。
您还可能会失去一些灵活性,因为您不能对不同的对象使用不同的抽象实现。您可能不需要它,但在其他情况下,这种模式可能会适得其反。
发布于 2018-09-02 13:10:09
因此,您已经很好地抽象了您的“ISpeaker”(让我们不称它为“共享”),但是将其放入一个静态Program.Speaker中。
现在,如果您的应用程序中有另一个类,比如LoginController,它希望使用它将拥有的扬声器。
public class LoginController
{
public void Login()
{
Program.Speaker.Speak("you have logged in!");
}
}虽然您的具体扬声器类不再耦合到应用程序,您的LoginController是不必要的耦合到您的程序。
另一种更好的选择是:
public class LoginController
{
public LoginController(ISpeaker speaker)
{
this.speaker = speaker;
}
public void Login()
{
this.speaker.Speak("you have logged in!");
}
}现在,您的代码甚至更少耦合,带来的所有好处。
服务定位器将是一个对象,它根据请求返回ISpeaker的一个具体实例。例如
public class LoginController
{
public LoginController(ILocator locator)
{
this.locator= locator;
}
public void Login()
{
var speaker = this.locator.Get<ISpeaker>();
speaker.Speak("you have logged in!");
}
}这可以绕过第一个示例中的紧密耦合的静态字段,但是如果定位器没有使用ISpeaker正确设置,则可能会导致运行时错误。
这尤其糟糕,因为如果不查看LoginController代码,您就不知道它需要一个ISpeaker。而显式要求ISpeaker作为构造参数,则强制调用代码传递某些内容。
讨论的解决方案称为依赖注入。我不确定在两个应用程序之间共享代码的问题是否有名称。
发布于 2018-09-02 03:18:49
据我从这链接中可以看到,Service意味着还有另一个类ShardProvider,它会激发所有其他代码-- IShared的正确实现。它被实例化为Singleton。
本质上,这与使用Sigleton模式有相同的优点和缺点,它只是将实例化IShared的责任从IShard本身转移到了一个独立的类。
在代码中,如果在某些实用程序类设置中定义了SharedDifinition属性,则它看起来类似于Service,但有以下区别:
internal。如果SharedDifinition属性位于实际使用它的问题特定类中,则它似乎与Service或Singleton很远。请注意,特定于问题的代码的用户必须始终将注意力集中在记住这段代码从何处提取IShared时,同时仍然会遇到Singleton/Service的缺点,因为该属性是静态的。
https://softwareengineering.stackexchange.com/questions/377794
复制相似问题