刚刚在我正在阅读的一本关于 OOP 的书中看到这句话,
只允许孩子增加功能和添加功能。永远不允许孩子删除功能。如果您确实发现子项需要删除功能,这表明子项应该在继承层次结构中出现在父项之前!
但我的问题是,这不是压倒一切吗?
刚刚在我正在阅读的一本关于 OOP 的书中看到这句话,
只允许孩子增加功能和添加功能。永远不允许孩子删除功能。如果您确实发现子项需要删除功能,这表明子项应该在继承层次结构中出现在父项之前!
但我的问题是,这不是压倒一切吗?
您可以通过覆盖删除功能。但通常你用它来改变行为。让班级按应有的方式行事。
如果行为被删除,那么它通常是一个糟糕的类设计的标志。
孩子不能删除功能——它可以改变它,但你不能,比如说,将公共方法设为私有。
继承的重点是您可以像处理父级一样处理子级。如果您有一个“Person”超类和一个“Employee”子类,那么 Employee 类没有 Breath() 方法是没有意义的。
覆盖方法时,可以在覆盖期间的某个时刻调用父实现,因此使用覆盖向父实现添加功能。
不。实际上你会增加功能(以消极的方式)
假设您的新功能是“什么都不做”,但方法是,您的代码的客户端看到的仍然是相同的界面
您不能拥有删除其父方法的子类。
这个有可能
class Parent {
public void method_one(){
print "Hello";
}
}
class Child extends Parent {
public void method_one(){
// do nothing
}
}
但这不是:
class Parent {
public void method_one(){
print "Hello";
}
}
class Child extends Parent {
// Attempt remove the method visibility, thus remove funcionality
private void method_one(){
// do nothing
}
}
这就是为什么重写(通常是任何虚拟成员)应该非常小心地完成的原因......事实上,通常,在重写时,您应该尝试同时编写基类和派生类,以便派生类实现首先调用基实现,然后执行它的附加功能......
但是这个原则并没有在 OO 语言中强制执行,并且经常被违反......
为什么这不好的例子
假设您有 CompanyA 设计的类型(类)电话
namespace CompanyA {
class Phone {
public void Dial() {
// do work to dial the phone here
}
}
}
没有 iagine 公司 B 定义了另一种类型 BetterPhone,它使用公司 A 的电话作为基本类型......
namespace CompanyB {
class BetterPhone: CompanyA.Phone {
public void Dial() {
Console.WriteLine("BetterPhoneDial");
EstablishConenction();
base.Dial();
}
}
}
现在 CompanyA,其 Phone 类正在被其他公司(公司 C、D 等)使用,决定建立连接是该类中有用的东西,并修改 CompanyA.Phone,同时添加 EsatblishCONnection() 方法,也许有不同的实现......在我们有了“new”关键字之前,这种情况会破坏 CompanyB 的 BetterPhone 类......当他们第一次尝试使用新的基类时。
如果您的孩子需要删除父级的功能,则必须将父级声明为接口。因为接口是定义必须由其实现者遵守的契约的机制。
例如
public interface IContract
{
void DoWork();
}
public class BaseContract: IContract
{
public virtual void DoWork()
{
//perform operation A
}
}
现在,如果您想声明新的 EnhancedContract 类,您可以根据要求从 BaseContract 或 IContract 派生它。如果你想对base的操作A做额外的操作,你可以从BaseContract继承它,如下所示。
public class EnhancedContract: BaseContract
{
public override void DoWork()
{
//perform operation B
base.DoWork();
//perform operation C
}
}
但是如果你对EnhancedContract的DoWork方法中的操作A不感兴趣,那么从IContract继承它。
这确保了 EnhancedWork 将执行 DoWork(),但不能保证在其中执行“操作 A”。
public class EnhancedWork:IContract
{
public void DoWork()
{
//perform operation D
}
}
这对于理解很重要,因为它会阻止用户进行以下投射。
EnhancedContract e = new EnhancedContract();
BaseContract b = e;
我相信所有这些操作在理解Open Closed 原则,Liskov 替换原则时都很重要。
继承的经验法则是“将附加功能放入现有功能中”。