为什么软件开发中的开放/封闭原则是使用抽象类实现的?

Why is the Open/Closed Principle in software development implemented using abstract classes?

提问人:bokabokaboka 提问时间:10/20/2023 最后编辑:bokabokaboka 更新时间:11/7/2023 访问量:95

问:

enter image description here

OCR码

using System;
namespace Example027 {
  class Program {
   static void Main(stringll args) {
   Vehicle v = new Truck();
    v.Run()
   }
}
abstract class Vehicle {
   public void Stop() {
      Console.WriteLinel'Stopped!");
   }

   public void Fill() {
   Console.WriteLinel' Pay and fill...");
}

   public abstract void Run();
}
class Car : Vehicle {
   public override void Run() {
   Console.WriteLinel('Car is running ...");
   }
}

class Truck : Vehicle {

请查看上面的代码。老师告诉我们,抽象类可以作为在软件开发中实现开放/封闭原则的工具。

上面的代码作为示例来说明这一点。老师说,所有车辆的常用方法,如停止和填充,都封装在车辆类中。这些方法的不同实现是通过重写在 Car 和 Truck 类中实现的。我理解这种方法,但我认为没有必要使用抽象类。我们可以在常规课程中实现同样的目标,对吧?

将 Vehicle 设置为常规类,并在其中仅实现 Stop 和 Fill 方法。然后,让 Car 和 Truck 继承自 Vehicle 并实现它们各自的 Run 方法。在程序开始时,使用

Var v = new Truck()

Truck v = new Truck() 

并且仍然能够使用

v.Run()

我不明白使用抽象类的必要性?

C# OOP 设计模式 接口 抽象类

评论

0赞 Piotr Szuflicki 10/20/2023
好吧,抽象类不是实现这一目标的必要条件。但是,它们非常有用,因为允许我们避免定义非抽象类的情况,该类具有通过抛出异常实现的虚拟方法。尽管它需要用户实现它们,但使用者不必编写一些额外的代码来检查你是否在证明基类的实例,这只是一个异常抛出器。也并不总是能提供基本实现。你的例子很好。您无法为所有汽车提供 run 的基本实现。
0赞 Ross Bush 10/20/2023
使用抽象类(如 Interface)的一个优点是它需要重写抽象方法。在常规课程中,覆盖是可选的,而不是强制性的。
6赞 Mark Seemann 10/20/2023
请不要上传代码/数据/错误的图片。
0赞 bokabokaboka 10/21/2023
@MarkSeemann 谢谢你的建议。这张图片来自视频截图。我也同意共享代码图像并不理想。我曾尝试寻找用于代码 OCR 的软件或网站,但没有成功。您是否有任何推荐的代码 OCR 工具运行良好?

答:

3赞 Ninotter 10/20/2023 #1

你是对的。没有必要使用抽象类来实现此行为。但是,使用常规类,您将能够实例化 ,这取决于您的目标,而不是您想要允许的。声明一个类的要点是确保该类被用于其目的,即在本例中被继承。Vehicleabstract

评论

0赞 Community 10/23/2023
您的答案可以通过其他支持信息进行改进。请编辑以添加更多详细信息,例如引文或文档,以便其他人可以确认您的答案是正确的。您可以在帮助中心找到有关如何写出好答案的更多信息。
2赞 spc16670 10/20/2023 #2

你认为一个常规类可以用来说明开放/封闭原则是正确的。

然而,抽象类更适合说明这一原则,因为从本质上讲,它们被设计为扩展,即“开放”扩展。

相比之下,常规类可以设置为“最终”(JAVA)或“密封”(C#),事实上,与客户端(使用您的类的代码)清楚地传达您提供的构造是开放的还是关闭的扩展被认为是好的编程。

2赞 JonasH 10/20/2023 #3

你的想法的一个问题是它缺乏抽象。你不能写一个像这样的方法

public static void FillAndRun(Vehicle vehicle){
    vehicle.Fill(); // Fine
    vehicle.Run(); // Not fine, No such method on Vehicle
}

你可以创建一个虚拟的运行方法。但是,你有一个问题,基类应该如何实现它。抽象类提供了一个公共接口,并强制每个派生类实现自己的 run-method。

但是,将代码划分为可重用的组件是很困难的,抽象类可能不是最好的方法。

抽象类的一个问题是它混合了两个相关但不同的概念。接口继承和实现继承

接口继承用于为类的用户提供公共接口,通常使用接口完成。这很常见,如果使用得当,完全没问题。

实现继承是为了重用实现,这更成问题。你只能从一个类继承,所以如果你想重用来自多个类的实现,你就不走运了。还有脆弱的基类问题和许多其他潜在问题。因此,通常建议首选“组合而不是继承”,即将通用实现移动到其他类或静态方法进行重用。

对于新开发人员来说,过度使用继承也很常见,这通常会导致“泄漏抽象”。因此,对于新开发人员,我建议不要太担心开放/关闭、抽象、接口等。这是一个非常困难的话题,即使是有经验的开发人员也很难解决。

评论

0赞 bokabokaboka 10/20/2023
感谢您的回复。我觉得你的想法有点难以理解,但我渴望找到一个学习的方向。那么,您讨论的主题属于哪个领域?设计模式?软件工程?
1赞 JonasH 10/22/2023
@bokabokaboka 我通常推荐 Eric Lipperts Wizards and warriors 来很好地介绍多态性和常见错误。