解释这个关于依赖倒置原则的动机海报

时间:2014-10-19 05:06:04

标签: oop dependencies solid-principles dependency-inversion

this博客文章中,这个动机海报描述了依赖性倒置原则:

poster

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

2 个答案:

答案 0 :(得分:3)

从您的链接:

  

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等。它可以在其他情况下轻松移动和使用,而不必改变。

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

答案 1 :(得分: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();
}

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