开闭原则与继承的区别

difference between open closed principle and inheritance

我知道开闭原则就是对扩展开放,对修改关闭。考虑一个例子如下

public class Vehicle{
    public void service(){
        //vehicle servicing code
    }
}

public class Bike extends Vehicle{

    public void service(){
        // bike specific servicing 
    }
}

现在我明白了 Bike class 扩展了 Vehicle 并使用开闭原则添加了新功能。

考虑我创建 Vehicle class 的 jar 文件,然后 Bike class 从 jar 扩展 Vehicle class。在这种情况下,我们不能修改 Vehicle class 和 Bike 扩展它。它是开放封闭原则的一个很好的例子吗? 我想知道OCP和继承有什么不同

OCP 与 Inheritance 没有区别,OCP 的 "Open" 部分对扩展开放,但对修改应该关闭。 IE。代码应该只针对 errors/bugs 进行修改,但对于新的扩展或对功能的更改,它应该被扩展。

作为旁注,我认为将其放在 programmers.stackexchange.com 网站中会更好。

以下是我对OCP的解读:

OCP 指出代码可以根据其更改频率进行分类,“相同”代码的各个部分可以有不同的更改频率,我所说的更改不仅是指随时间变化,而且是在运行时发生变化- 例如选择这段或那段特定的代码来执行某些操作。

OCP 要求将更稳定的代码与更可能更改的代码分开。但它并不止于此,它还要求更改频率较低的代码能够与更改频率较高的代码一起工作。

那么OCP==继承?不是。继承只是实现OCP的技术之一。策略模式、装饰器模式、普通组合、参数多态性(又名泛型)和其他技术也可以用来实现这些目标。

我所说的不同变化频率的例子。让我们想象一些集合实现。如果每次将某种原始类型添​​加到语言中,集合代码也需要更新,那不是很糟糕吗?因此集合将它们持有的项目视为不透明的,从而满足 OCP。下一个示例,让我们从上一个示例中获取相同的集合实现,并想象我们想要打印它的排序元素。简单的。我们只是将排序添加到集合中……以及其他 10 种集合类型?每个新系列也必须实现这种排序吗?可怕。如果我们的排序算法只是将集合视为不透明类型,如果被要求按顺序提供它的项目,那不是更好吗?这是我们的排序需要集合做的唯一事情,一旦给定元素列表,它就可以进行实际排序。行。所以现在集合只需要支持一个连续 returns 所有项目的操作。很简单...如果我们考虑一下,它是一个非常有用的行为,可以用于过滤、转换、....

希望通过上面的示例,我展示了继承之外的一些 OCP 用法,并表明 OCP 还鼓励我们将代码视为不同抽象级别的组合(具有不同更改频率的代码组合)。

问题:没有继承

让我们根据问题中给定的代码构建一个示例。不同的车辆以不同的方式进行维修。因此,我们对 BikeCar 有不同的 classes,因为服务 Bike 的策略与服务 Car 的策略不同。

Garage class 接受各种车辆进行维修。观察代码,看看 Garage class 是如何违反 open-closed 原则的:

class Bike {
    public void service() {
        System.out.println("Bike servicing strategy performed.");
    }
}

class Car {
    public void service() {
        System.out.println("Car servicing strategy performed.");
    }
}

class Garage {
    public void serviceBike(Bike bike) {
        bike.service();
    }

    public void serviceCar(Car car) {
        car.service();
    }
}

您可能已经注意到,每当 TruckBus 等新车辆需要维修时,都需要修改 Garage 以定义新方法 serviceTruck()serviceBus()。这意味着 Garage class 必须知道所有可能的车辆,例如 BikeCarBusTruck 等等。因此,它通过开放修改违反了 open-closed 原则。而且它没有开放扩展,因为要扩展新功能,我们需要更改它。


解决方案:使用继承

抽象

为了解决上述代码中的问题,满足open-closed原则,我们需要抽象出每种车辆的服务策略的实现细节。这意味着我们需要 abstraction of Bike and Car classes.

多态

我们还希望 Garage class 接受车辆的 多种形式 ,例如 BusTruck 和依此类推,而不仅仅是 BikeCar。这意味着我们需要多态性(多种形式)。

继承

所以,要满足open-closed原则,最重要的机制就是抽象和多态。在Java、C#等静态类型语言中,提供抽象和多态性的重要工具是继承.

为了抽象各种类型车辆的服务策略的实施细节,我们使用一个名为 Vehicleinterface 和一个抽象方法 service()

而对于Garage class接受多种形式的Vehicle,我们将其方法的签名更改为service(Vehicle vehicle) { }以接受接口Vehicle 而不是像 BikeCar 等实际实现。我们还从 class 中删除了多个方法,因为只有一个方法可以接受多种形式。

interface Vehicle {
    void service();
}

class Bike implements Vehicle {
    @Override
    public void service() {
        System.out.println("Bike servicing strategy performed.");
    }
}

class Car implements Vehicle {
    @Override
    public void service() {
        System.out.println("Car servicing strategy performed.");
    }
}

class Garage {
    public void service(Vehicle vehicle) {
        vehicle.service();
    }
}

关闭修改

在上面的代码中可以看到,现在Garage class已经关闭修改,因为现在它不知道各种类型车辆的服务策略的实现细节并且可以接受任何类型的新Vehicle。我们只需要从 Vehicle 接口扩展新车辆并将其发送到 Garage。我们不需要更改 Garage class.

中的任何代码

另一个因修改而关闭的实体是我们的 Vehicle 界面。 我们不必更改界面来扩展我们软件的功能。

开放扩展

Garage class 现在开放扩展,因为它将支持 Vehicle 的新类型,无需修改。

我们的 Vehicle 接口可以扩展,因为要引入任何新车辆,我们可以从 Vehicle 接口扩展并提供一个新的实现,其中包含为特定车辆提供服务的策略。


因此,如您所见,继承是我们用来遵守open-closed原则规则的编程语言提供的公正工具。

就是这样!希望对您有所帮助。