Наследование, родитель-потомок и переопределение

StackOverflow https://stackoverflow.com/questions/326686

Вопрос

Только что наткнулся на эту цитату в книге по ООП, которую я читаю:

Ребенку разрешено только увеличивать функциональность и добавлять функциональность.Ребенку никогда не разрешается удалять функциональность.Если вы обнаружите, что ребенку нужно удалить функциональность, это свидетельствует о том, что ребенок должен появиться перед родителем в иерархии наследования!

Но мой вопрос: разве не это делает переопределение?

Это было полезно?

Решение

Вы можете удалить функциональность с переопределением. Но обычно вы используете это, чтобы изменить поведение. Чтобы позволить классу вести себя так, как должно быть.

Если поведение удалено, то это очень часто признак плохого дизайна класса.

Другие советы

Дочерний объект не может удалить функциональность - он может изменить его, но вы не можете, скажем, сделать публичный метод закрытым.

Смысл наследования в том, что вы можете обращаться с дочерним элементом так, как если бы он был родительским. Если у вас есть суперкласс «Персона» и подкласс «Сотрудник», то для класса «Сотрудник» не будет никакого смысла не иметь метода дышать ().

При переопределении метода можно вызвать родительскую реализацию в какой-то момент во время переопределения, поэтому используйте переопределение для добавления функциональности в родительскую реализацию.

Нет.На самом деле вы бы расширили функциональность (в отрицательном смысле).

Допустим, ваша новая функциональность — «ничего не делать», кроме метода, что клиенты вашего кода, видите, все тот же интерфейс

У вас не может быть подкласса, который удаляет метод своего родителя.

Это возможно

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
     }
 }

И вот почему переопределение (и вообще любых виртуальных членов) - это то, что должно быть сделано очень осторожно ... На самом деле, как правило, при переопределении вы должны пытаться кодировать как базовый класс, так и производный класс, чтобы реализация производного класса сначала вызывала базовую реализацию, а затем выполняла дополнительные функции ...

но этот принцип не применяется в ОО-языках и часто нарушается ...

Пример того, почему это плохо

Представьте, что вы разрабатываете 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 (). метод, возможно, с другой реализацией ... Пока у нас не было " нового " ключевое слово, этот сценарий сломал бы класс BetterPhone CompanyB ... в первый раз, когда они попытались использовать новый базовый класс.

Если вашему дочернему элементу требуется удалить функциональность родителя, то родительский элемент должен быть объявлен как Интерфейс.Потому что интерфейс — это механизм, определяющий контракты, которые должен соблюдать его разработчик.

Например.


public interface IContract
{
  void DoWork();
}

public class BaseContract: IContract
{
 public virtual void DoWork()
 {
  //perform operation A
 }
}

Теперь, если вы хотите объявить новый класс EnhancedContract, вы можете получить его либо от BaseContract, либо от IContract, в зависимости от требований.Если вы хотите выполнить дополнительную операцию к операции A базы, вы можете унаследовать ее от BaseContract, как показано ниже.


public class EnhancedContract: BaseContract
{
  public override void DoWork()
  {
   //perform operation B
   base.DoWork();
   //perform operation C
  }
}

Но если вы не заинтересованы в выполнении операции A в методе DoWork EnhancedContract, унаследуйте ее от IContract.

Это гарантирует, что EnhancedWork выполнит DoWork(), но не гарантируется выполнение в нем «операции А».


public class EnhancedWork:IContract
{
  public void DoWork()
  {
   //perform operation D
  }
}

Это важно для понимания, потому что это не позволит пользователю выполнять приведенное ниже приведение.


EnhancedContract e = new EnhancedContract();
BaseContract b = e;

Я считаю, что все эти операции важны при понимании Открытый Закрытый принцип, Принцип замены Лискова.

Основное правило наследования: «Добавьте дополнительную функциональность в существующую».

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top