За последние 24 часа нас посетили 46659 программистов и 1272 робота. Сейчас ищут 1018 программистов ...

Почему «родитель» не должен знать про «детей»?

Тема в разделе "PHP для новичков", создана пользователем Walk, 13 ноя 2017.

  1. Walk

    Walk Активный пользователь

    С нами с:
    7 сен 2008
    Сообщения:
    452
    Симпатии:
    86
    Речь про наследование классов. От одного опытного программиста слышал, что класс-родитель не должен знать о классах которые от него наследуются. Т.е., как понимаю, в классе-родителе нельзя создавать объекты классов наследующихся от этого же класса.

    А вот спросить почему, я в тот момент забыл. Собственно вопрос - если правило:
    класс-«родитель» не должен знать про «детей»

    Верно, то почему так нельзя делать?
     
    TeslaFeo нравится это.
  2. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.598
    Симпатии:
    1.764
    Ну всё объектно-ориентированное программирование строится на том, что ты передаёшь объект потомка в функцию, которая думает, что работает с родителем (полиморфизм в действии). И можешь подменять одного наследника другим в каких-то случаях (например, для тестирования). Плюс в идеале классы должны легко вытаскиваться из системы, и встраиваться в другой проект для повторного использования без изменений. Если класс-родитель завязан на наследниках, то его уже хрен перенесёшь без них в другой проект. В целом, единственный инструмент, позволяющий требовать какого-то поведения от наследников - абстрактные методы. Объявив метод абстрактным, ты обязываешь наследников его реализовать.
    --- Добавлено ---
    По хорошему, внутри класса вообще не должно быть операторов new, разве что new self или new static, все зависимости должны приходить из внешнего мира. Ты же на yii пишешь, вот, глянь видос с конференции по Yii2:
    --- Добавлено ---
    (это не значит, что я всегда сам так делаю, но, чем крупнее проект, тем более болезненны нарушения правил)
     
  3. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    @Walk
    должен не должен - это всё писами по воде виляно
    главный вопрос - зачем ему знать или не знать про его детей? ответишь?
     
    mkramer нравится это.
  4. Maputo

    Maputo Активный пользователь

    С нами с:
    30 июл 2015
    Сообщения:
    1.136
    Симпатии:
    173
    @mkramer, наличие new внутри класса - это агрегация. Без этого никак не обойтись.
    @Walk, по поводу создания потомков внутри родителей - получается рекурсия. Родительский класс должен содержать общий для всех потомков функционал, тогда как потомки частный случай реализации родителя. Если родительский функционал расширяется за счет потомков - значит диаграмма классов построена неверно. Скорее всего следует сделать рефракторинг классов.
    Либо объеденить родительский класс с дочерним, либо выделить иной функционал для родительского класса и от него наследовать прежний родительский и дочерний классы.
     
    Walk и Fell-x27 нравится это.
  5. Chushkin

    Chushkin Активный пользователь

    С нами с:
    17 дек 2010
    Сообщения:
    1.062
    Симпатии:
    91
    Адрес:
    Мещёра, Центр, Болото N3
    Коллеги, вы зря стараетесь - ТС-у для начала надо объяснить, чем отличаются понятие "класс" от понятия "объект". Если разберётся, то возможно вопрос станет не актуальным для него.
     
  6. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.598
    Симпатии:
    1.764
    Инверсия зависимостей, передача зависимостей через конструктор.
     
    Maputo нравится это.
  7. Maputo

    Maputo Активный пользователь

    С нами с:
    30 июл 2015
    Сообщения:
    1.136
    Симпатии:
    173
    Так это не мне Вы, наверное, хотели ответить, а @igordata и @Walk
    По поводу внедрения через конструктор - вопрос спорный.
     
  8. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    не спорный
    это просто один из способов закидывать всякое во внутрь класса в явном виде, а не через всякую черезжопную сложноотлавливаемую муть
     
  9. Maputo

    Maputo Активный пользователь

    С нами с:
    30 июл 2015
    Сообщения:
    1.136
    Симпатии:
    173
    Речь не о том как оно попадет в класс, а о том где же будет оператор new? Не в этом классе так в другом.
     
  10. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.598
    Симпатии:
    1.764
    Ну где-то будет, да, это понятно. Совсем не обойтись без него.
     
    Maputo нравится это.
  11. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    @Walk код в студию
     
  12. Walk

    Walk Активный пользователь

    С нами с:
    7 сен 2008
    Сообщения:
    452
    Симпатии:
    86
    На Yii2 таких вопросов не возникает. Вот когда свои велосипеды придумываю - такой вопрос появляется.

    Вперед - объясните.

    Там тысячи строк кода.
     
  13. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    А более простого способа изобразить сабж, не существует? =)
     
  14. Walk

    Walk Активный пользователь

    С нами с:
    7 сен 2008
    Сообщения:
    452
    Симпатии:
    86
    Так я его и задал в простом виде:
     
  15. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    @Walk мы же о программировании говорим. Мне не совсем понятно, что ты имеешь ввиду. А если приложишь код для наглядности, иллюстрирующий ситуацию, станет всем понятно, о чем речь.
     
  16. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.598
    Симпатии:
    1.764
    Если ты пишешь что-то такое:
    PHP:
    1. class A {
    2.      function ddd()
    3.     {
    4.         $b = new B();
    5.         // ...
    6.     }
    7. }
    8.  
    9. class B extends A {
    10.     /* .... */
    11. }
    То ты класс A уже из этого проекта не можешь выпилить. Это всё трудно отлаживать очень (получилась двунаправленная зависимость между A и B, которой всеми силами надо избегать). В конечном итоге все эти вещи замедляют отладку, тестирование, доработку.
     
  17. Sail

    Sail Старожил

    С нами с:
    1 ноя 2016
    Сообщения:
    1.593
    Симпатии:
    362
    @Walk, если, например, как-то так:
    PHP:
    1. interface actions {
    2.     public function action_one();
    3.     public function action_two();
    4. }
    5.  
    6. abstract class A implements actions {
    7.    
    8.     /**
    9.      * @return A;
    10.      */
    11.     abstract public function getMe();
    12.     public function doSomeThisg() {
    13.         $foo = $this->getMe();
    14.         $foo->action_one();
    15.     }
    16. }
    17.  
    18. class B extends A {
    19.    
    20.     public function action_one() {
    21.         echo "One";
    22.     }
    23.  
    24.     public function action_two() {
    25.         echo "Two";
    26.     }
    27.  
    28.     public function getMe() {
    29.         return new B();
    30.     }
    31. }
    32.  
    33. $foo = new B();
    34. $foo->doSomeThisg();
     
  18. Walk

    Walk Активный пользователь

    С нами с:
    7 сен 2008
    Сообщения:
    452
    Симпатии:
    86
    В общем понял (для себя), почему не стоит так делать:

    PHP:
    1. // Пример, как не надо делать
    2.  
    3. class Main
    4. {
    5.     // 50-100 свойств - переменные, массивы, многомерные массивы, объекты
    6.     public $name = 'Masha';
    7.  
    8.     // 100500 методов, хотим часть вынести в дочерний класс
    9.     public function rename($name)
    10.     {
    11.         $rename = new Rename;
    12.         $rename->newname($name);
    13.     }
    14. }
    15.  
    16. class Rename extends Main
    17. {
    18.     // Представим, что это какой-то объемный метод на 500 строк
    19.     public function newname($name)
    20.     {
    21.         $this->name = $name;
    22.         echo $this->name.'<br />'; // Olya
    23.     }
    24. }
    25.  
    26. $obj = new Main;
    27. echo $obj->name.'<br />'; // Masha
    28. $obj->rename('Olya');
    29. echo $obj->name.'<br />'; // Masha
    Дочерний класс создает копию родительского, и копию его свойств. Получается:

    1. Бессмысленность в дочернем свойстве - все равно свойства родителя придется менять через return
    2. Можно банально запутаться.
     
  19. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.598
    Симпатии:
    1.764
    В одном классе 50-100 свойств - это перебор. Значит класс надо разбивать классов на 10
     
  20. Walk

    Walk Активный пользователь

    С нами с:
    7 сен 2008
    Сообщения:
    452
    Симпатии:
    86
    Это все понятно. Вопрос в том, КАК ЛУЧШЕ это сделать.

    Вот размышлял над вариантом через дочерние классы - но, как уже писал, слышал, что так делать нельзя. Разбирался, почему именно нельзя.
     
    #20 Walk, 14 ноя 2017
    Последнее редактирование: 14 ноя 2017
  21. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.598
    Симпатии:
    1.764
    Наследование надо применять, только если можно сказать, что объект наследника является также объектом родителя. Вот у меня в проекте есть форма для введения параметров услуги "продажа нерудных материалов", от неё унаследована форма "продажа нерудных материалов с доставкой", которая добавляет поля, соответствующие доставке. Но про второю форму можно сказать, что она всё равно форма по продаже нерудных материалов, поэтому наследование применимо.
     
  22. Walk

    Walk Активный пользователь

    С нами с:
    7 сен 2008
    Сообщения:
    452
    Симпатии:
    86
    В вашем примере в родительском классе не создается дочерний класс, а также вы не создаете объект родительского класса.
    Как понимаю, вы показываете пример как надо. Меня же интересует пример как не надо, с разбором почему именно так не надо.

    Мне ваша формулировка не совсем ясна.

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

    Но меня интересует, есть ли другие сферы применения наследования. Например, в целях вынесения части функционала из родительского класса (но, как показывает практика - такой вариант не применим).
     
    #22 Walk, 14 ноя 2017
    Последнее редактирование: 14 ноя 2017
  23. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.598
    Симпатии:
    1.764
    Это неверно. ООП - именно про абстрактное мышление, когда вы переводите программу в ряд взаимодействующих механизмов. Объекты должны отображать, большей частью, понятия предметной области. Поэтому отношения и выражаются словами "является" (наследование), "содержит" (агрегирование), "использует" и т.п.
     
  24. Walk

    Walk Активный пользователь

    С нами с:
    7 сен 2008
    Сообщения:
    452
    Симпатии:
    86
    Все говорят про эти абстракции, но что-то в том же Yii2 я их не наблюдаю - все вполне конкретно и решает вполне конкретные задачи - работа с бд, валидация данных, обработка входящего запроса, выдача результата и т.д. - никаких абстракций, одна конкретика.
     
  25. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.598
    Симпатии:
    1.764
    Yii - это реализация низкого уровня задач. Твоё приложение - более высокий уровень. И то, берём Yii. Там есть интерфейс UrlRuleInterface, нечто, что должно разбирать запросы и строить url-ы, есть UrlManager, который использует массив объектов классов, реализующих UrlRuleInterface, чтоб разбирать запросы, есть также стандартная реализация интерфейса UrlRule, которой хватает на большинство задач. Но, UrlManager может работать с любой реализацией UrlRule, ты можешь написать свой класс по разбору запросов, подставить его в UrlManager, и всё будет в порядке. Это и есть абстракция - мы говорим, что UrlManager-у нужна некая хрень, которая умеет разбирать запросы и строить url-ы. UrlManager-у всё равно, как и кем это будет реализовано. Т.е. он работает с некоей абстрактной разбиралкой запросов, а не конкретной реализацией UrlRule.