4

这篇博文中,这张励志海报描述了依赖倒置原则:

海报

我不明白海报的意思:将灯直接焊接到墙上如何违反依赖倒置原则以及插头如何遵循依赖倒置原则。也许一些关于灯和电源插座的骨架 Java 或 C# 代码可能会有所帮助。

4

2 回答 2

6

从您的链接:

SOLID 中的“D”代表依赖倒置原则,它指出高级模块不应该依赖于低级模块,但两者都应该依赖于共享抽象。

我不知道“共享”抽象是什么意思。在代码中,灯将是“高级模块”,它的电源来自它的依赖项或“低级模块”。

你的问题:

将灯直接焊接到墙上如何违反依赖倒置原则

海报中的灯违反了依赖倒置原则,因为它不依赖于抽象的电源。它依赖于一个非常具体的实现,即直接焊接到墙上的电线上。如果这种关系在代码中建模,它可能看起来像这样:

public class Lamp {
    // This part is the equivalent of "soldering a lamp directly to the electrical wiring in a wall"
    private ElectricalWiringInBobsWall electricalWiring = new ElectricalWiringInBobsWall();
    private boolean isOn; 

    public void turnOn() {
        electricalWiring.useElectricityInWatts(60);
        isOn = true;
    }

    public void turnOff() {
        electricalWiring.turnOffElectricityInWatts(60);
        isOn = false;
    }   
}

这样做的后果是灯泡很难在其他情况下使用。在现实生活中,如果你想把灯移到房子的不同地方,在发电机上使用它,或者甚至把它带到国外并插在不同的插座上,它本身就需要大量的返工。类似地,在代码库中,如果您想使用不同的电源重新使用此灯,则必须更改灯本身的代码,这可能需要重新测试或引入新的错误。

插件如何遵循依赖倒置原则

插座更抽象,因为作为用户,您实际上不必知道灯是如何获得电源的。你只需要知道,如果你把灯插上,它就可以工作。插座是对电源的抽象。也就是说,它隐藏了力量实际来自何处的细节。墙上、发电机、海外适配器、太阳能电池板电源等上可能有一个插座。

在上面的类中使用 DIP,我们希望Lamp依赖于抽象,并将实现作为依赖项传入。在代码中,这可能如下所示:

public interface Outlet {
    void useElectricityInWatts(int watts);
    void turnOffElectricityInWatts(int watts);
}   

public class Lamp {
    private Outlet outlet;
    private boolean isOn; 

    // We pass in the dependency instead of instantiating it and rely on an interface (abstraction)
    public void plugInto(Outlet outlet) {
        this.outlet = outlet;
    }

    public void turnOn() {
        outlet.useElectricityInWatts(60);
        isOn = true;
    }

    public void turnOff() {
        outlet.turnOffElectricityInWatts(60);
        isOn = false;
    }   
}

现在,一盏灯可以使用任何类型的插座:BobsOutletInHisWall、GeneratorOutlet、EuropeanAdapterOutlet 等。它可以很容易地在其他情况下移动和使用,根本不需要改变。

我知道这个例子远非完美,但我希望能解释海报的含义。

于 2014-10-19T20:47:37.937 回答
0

这类似于非硬编码的常见做法,例如控制器(控制流的类)和模型(数据的来源)。

例如,这会很糟糕,不可测试或容易“拔掉”:

public function index()
{
     return $some->sql()->logic()->where('this', '=', 'that');
}

相反,您会将存储库注入到您的控制器中:

public function __construct(Somerepo $repo)
{ 
    $this->repo = $repo;
}

然后,您可以继续通过 repo 中的方法访问所需的 SQL 语句。

public function index()
{
     return $this->repo->getStuff();
}

这样,您可以轻松更改或“拔出”数据的来源,而无需触及控制器逻辑。

于 2014-10-19T05:13:50.143 回答