首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用静态接口的解耦设计

使用静态接口的解耦设计
EN

Software Engineering用户
提问于 2018-09-01 21:27:01
回答 3查看 837关注 0票数 4

首先,我想提一下,我不太清楚描述当前问题的术语是什么,但我希望我能用一些代码来说明它。

问题:共享类库定义了一个接口,引用这个库的每个应用程序都提供了它自己的接口实现。现在,在共享库中,我想调用该方法的实际实现。

为了解决这个问题,我设想了一个简单的场景,即两个特定于平台的应用程序Windows,Android共享一些代码,除了需要为每个系统单独实现的一些特定于平台的功能。在运行时,我需要透明地调用该实现,因为它已经由实现者提供。

三个控制台应用程序被创建为共享的,Windows,Android

共享项目定义接口IShared

代码语言:javascript
复制
namespace Project
{
    public interface IShared
    {
        void Speak(string word);
    }
}

安卓系统实现IShared

代码语言:javascript
复制
namespace Project.Android
{
    public class SpeakAndroid: IShared
    {
        public void Speak(string word)
        {
            Console.WriteLine("Android Speaks");
        }
    }
}

Windows实现IShared

代码语言:javascript
复制
namespace Project.Windows
{
    public class SpeakWindows: IShared
    {
        public void Speak(string word)
        {
            Console.WriteLine("Speak Windows");
        }
    }
}

现在,为了使共享项目能够调用每个特定于平台的Speak函数,我决定声明

代码语言:javascript
复制
public static IShared SharedDefinition { get; set; }

在Windows和Android项目中分配一个新的实现,如下所示

Windows:

代码语言:javascript
复制
Project.Program.SharedDefinition = new SpeakWindows();

安多伊德:

代码语言:javascript
复制
Project.Program.SharedDefinition = new SpeakAndroid();

这样,在共享项目中,我可以调用Program.SharedDefinition.Speak();,根据运行平台的不同,我将得到不同的Speak实现。

问题(S):

  • 这是否解决上述问题的正确方法?服务定位器模式与此有任何关系吗?
  • 对于进一步的阅读,上面的问题是否有一个最能解决它的名称或模式?
EN

回答 3

Software Engineering用户

回答已采纳

发布于 2018-09-02 00:48:07

In

这是解决抽象问题的有效方法:它确保库不依赖于具体的实现。

这项技术的一般术语称为未定义。但是,您使用的不是注入依赖性进入需要它的对象或函数,而是使用全局中介。这样的中介是一个未定义

详细信息:服务定位器不是更复杂吗?

服务定位器通常封装对几种不同类型的服务的访问,以及对每个服务的访问。然后,这个场景比您的情况要复杂得多:

  • SL被初始化。原则上这是个单身
  • 然后将服务注册到SL (可选地带有一些属性以找到它们)
  • 然后客户端从SL获得服务(可以选择一些查找以选择最合适的服务)。

但是在您的例子中,定位器被简化到极致,因为您只公开了一个服务:

  • SL只是一个全局静态对象。
  • 库要使用的单个IShared服务是通过全局静态对象的setter使用-context注册的。
  • 库通过全局对象的getter访问配置的服务。

这篇文章中,您将发现这两个极端之间的服务定位器的另一个例子。(我在这里更多地提到这一点,因为它非常明确地区分了依赖反转、依赖注入和控制反转)

更多细节:这是个好主意吗?

服务定位器的主要不便在于,所有库类都可能依赖它,可能会在库中创建比必要的更高的耦合。

您还可能会失去一些灵活性,因为您不能对不同的对象使用不同的抽象实现。您可能不需要它,但在其他情况下,这种模式可能会适得其反。

票数 4
EN

Software Engineering用户

发布于 2018-09-02 13:10:09

re:为什么静态坏?

因此,您已经很好地抽象了您的“ISpeaker”(让我们不称它为“共享”),但是将其放入一个静态Program.Speaker中。

现在,如果您的应用程序中有另一个类,比如LoginController,它希望使用它将拥有的扬声器。

代码语言:javascript
复制
public class LoginController 
{
    public void Login() 
    {
        Program.Speaker.Speak("you have logged in!");
    }
}

虽然您的具体扬声器类不再耦合到应用程序,您的LoginController是不必要的耦合到您的程序。

另一种更好的选择是:

代码语言:javascript
复制
public class LoginController 
{
    public LoginController(ISpeaker speaker)
    {
        this.speaker = speaker;
    }
    public void Login() 
    {
        this.speaker.Speak("you have logged in!");
    }
}

现在,您的代码甚至更少耦合,带来的所有好处。

re:为什么服务定位器不好?

服务定位器将是一个对象,它根据请求返回ISpeaker的一个具体实例。例如

代码语言:javascript
复制
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作为构造参数,则强制调用代码传递某些内容。

re:问题有名字吗?

讨论的解决方案称为依赖注入。我不确定在两个应用程序之间共享代码的问题是否有名称。

票数 2
EN

Software Engineering用户

发布于 2018-09-02 03:18:49

据我从链接中可以看到,Service意味着还有另一个类ShardProvider,它会激发所有其他代码-- IShared的正确实现。它被实例化为Singleton。

本质上,这与使用Sigleton模式有相同的优点和缺点,它只是将实例化IShared的责任从IShard本身转移到了一个独立的类。

在代码中,如果在某些实用程序类设置中定义了SharedDifinition属性,则它看起来类似于Service,但有以下区别:

  • 它似乎可以写任何东西,这是危险的。设置者至少应该是internal
  • Service模式会指定属性为非静态属性,而不是设置一个单例。

如果SharedDifinition属性位于实际使用它的问题特定类中,则它似乎与Service或Singleton很远。请注意,特定于问题的代码的用户必须始终将注意力集中在记住这段代码从何处提取IShared时,同时仍然会遇到Singleton/Service的缺点,因为该属性是静态的。

票数 1
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/377794

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档