Наследование, родитель-ребенок и переопределение - PullRequest
1 голос
/ 29 ноября 2008

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

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

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

Ответы [ 6 ]

4 голосов
/ 29 ноября 2008

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

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

3 голосов
/ 29 ноября 2008

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

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

1 голос
/ 29 ноября 2008

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

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

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

Это возможно

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
     }
 }
1 голос
/ 29 ноября 2008

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

0 голосов
/ 24 марта 2010

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

например.


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 (), но это не гарантирует выполнение «операции A» в нем.


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

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


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

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

Правило большого пальца для наследования: «Поместите дополнительные функции в существующую».

0 голосов
/ 29 ноября 2008

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

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

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

Представьте, что вы разрабатываете CompanyA Тип (класс) Телефон

namespace CompanyA {
   class Phone {
       public void Dial() {
        // do work to dial the phone here
       }
   }
}

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

...