首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >没有静态方法的依赖反转?

问没有静态方法的依赖反转?
EN

Software Engineering用户
提问于 2015-05-30 13:17:04
回答 3查看 879关注 0票数 1

显然,依赖反转原理和接口的使用使软件组件更少耦合,并提高了可维护性。另一方面,这些原则使得使用静态方法和工厂成为必要。我认为静态方法应该脱离任何好的面向对象的设计,因为它们污染了与纯OOP无关的命令式编程实践的代码。

我的问题是:是否有一种方法可以完成DIP并有效地使用接口来解耦组件而不使用静态工厂方法?用纯的、简单的面向对象程序设计可维护的软件有那么困难吗?封装、继承和多态性真的不够吗?

EN

回答 3

Software Engineering用户

回答已采纳

发布于 2015-05-30 13:55:13

另一方面,这些原则使得使用静态方法和工厂成为必要。

什么时候开始的?

我认为静态方法应该脱离任何好的面向对象的设计,因为它们污染了与纯OOP无关的命令式编程实践的代码。

你可以但你错了。现代面向对象设计通常包括静态方法,以便在适当的情况下结合功能编程概念。记住,你的工作是把事情做完,而不是纯洁。有时候,解决方案需要一个独立的(纯)函数,所以做其他的事情会使代码变得更糟。

封装、继承和多态性真的不够吗?

这些不是OO。它们是Java学校教授的OO,这是十多年前人们一直重申的基础。它们实际上是不够的,因为它们对代码质量的关键度量耦合影响很小。地狱,继承本身在许多情况下都是不受欢迎的。

无论如何,回到您的代码问题:

是否有一种方法可以完成DIP并有效地使用接口来解耦组件而不使用静态工厂方法?

是的,它非常简单。您将创建一个类,该类在其构造函数中接受其依赖项(作为接口)。使用该类的代码要么知道要创建什么具体类型,要么具有接口本身的实例(因为它是一个依赖项)。使用该类的代码将依赖项作为参数传递。这样,类本身就可以完全不了解具体类型,从而获得所需的解耦。没有不必要的工厂,没有静态的方法。

票数 10
EN

Software Engineering用户

发布于 2015-05-30 14:00:21

静态方法是不好的,并不是因为它们是“预OOP”,而是因为它们本身是不可颠倒的依赖关系。最近,我试图测试一个代码库,其中包含一个调用,例如

代码语言:javascript
复制
void foo() {
    ...
    Time now = Clock::getCurrentSystemTime();
    ...
}

这是有意义的,因为这是静态的,因为(a)这段代码对性能敏感,(b)只能有一个当前时间。不幸的是,这意味着我不能为这些测试注入我自己的Clock,这样我就可以在某些时候测试系统的行为。换句话说,这个代码库违反了依赖反转原则。

依赖性反转很简单:我只需将依赖项显式化,并将其作为参数传递给当前代码单元(例如,对象构造函数或方法参数):

代码语言:javascript
复制
type CurrentTimeProvider = () => Time;

void foo(CurrentTimeProvider currentTime) {
    ...
    Time now = currentTime();
    ...
}

// in production:
foo(Clock::getCurrentSystemTime);

// in testing:
foo(() => Time("2000-01-01T00:00:00.000Z"));

这里没有任何有意义的静态方法。在更复杂的情况下,我们可能会引入一个成熟的依赖注入容器,负责解决所有依赖关系。DI容器本身就是一个抽象工厂对象。理想情况下,它将显式地传递。然后:

代码语言:javascript
复制
type CurrentTimeProvider = () => Time;

void foo(DIContainer deps) {
    ...
    Time now = deps.get(CurrentTimeProvider)();
    ...
}

DiContainer deps;

// in production:
deps.set(CurrentTimeProvider, Clock::getCurrentSystemTime);
foo(deps);

// in testing:
deps.set(CurrentTimeProvider, () => Time("2000-01-01T00:00:00.000Z"));
foo(deps);

但是人们很快就厌倦了任何一种手动依赖注入。因此,假设依赖注入系统上的不可逆反式依赖是可以的,我们可以使DI容器API是静态的(或者使容器成为单例,这本质上是一样的)。上述示例可能会更改为:

代码语言:javascript
复制
type CurrentTimeProvider = () => Time;

void foo() {
    ...
    Time now = DIContainer.get(CurrentTimeProvider)();
    ...
}

// in production:
DIContainer.set(CurrentTimeProvider, Clock::getCurrentSystemTime);
foo();

// in testing:
DIContainer.set(CurrentTimeProvider, () => Time("2000-01-01T00:00:00.000Z"));
foo();

实际上,这是相当优雅和足够理智的。这是令人眼花缭乱的OOP设计吗?可能不会,但您不会遇到任何问题(除非您试图模拟依赖注入系统本身)。如果您不喜欢这样的全局DI系统(我也不喜欢它),您可以选择DI可用的任何其他可用策略(同样:显式参数/构造函数注入,或者DI容器/工厂对象的注入)。在语言无关的视图中,程序中唯一不能静态的东西是程序的入口点/main函数。

票数 2
EN

Software Engineering用户

发布于 2015-05-30 14:05:35

如果您真的希望使类解耦,那么您将忽略所有静态地将方法链接在一起的编译时构造,然后转到方法调用的原始OO定义,即消息传递。

当然,这将使您的代码更难以管理(可能,我在使用消息传递架构的分布式系统方面取得了巨大成功),或者通过编译器错误进行调试。

IoC要求您通过其构造函数将“全局”配置对象传递给每个对象,而不是需要时获取此配置对象的对象。用于IoC和DI的各种容器框架可能使用静力学、工厂等等,但您不必使用它们。无论您做什么,不要让框架的设计选择与所使用的模式的基本方面相混淆。

所以,考虑到你所使用的系统,你必须用理想主义来换取实用性。如果这意味着使用静态方法,或全局或(恐怖!)即使是单身人士,也要使用他们。您的工作不是编写完美的代码,而是使易于理解、易于维护的代码在生产对人们有用的产品方面发挥其作用。

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

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

复制
相关文章

相似问题

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