class - типом - чем отличается объект от класса php




Разница между экземпляром класса и классом, представляющим экземпляр уже? (4)

отредактированный

Прежде всего : мы знаем определение наследования, и мы можем найти много примеров в SO и интернете. Но я думаю, что мы должны выглядеть более подробно и немного более научными .

Примечание 0 :
Разъяснение терминологии наследования и экземпляров .
Сначала позвольте мне назвать « Область разработки» для жизненного цикла разработки, когда мы моделируем и программируем нашу систему, и « Область выполнения» для некоторых случаев, когда наша система работает.

У нас есть классы и моделирование и разработка их в области разработки. И объекты во время выполнения. В области разработки нет объектов .

И в «Объектно-ориентированном» определение экземпляра : создание объекта из класса.

С другой стороны, когда мы говорим о классах и объектах, мы должны прояснить нашу точку зрения на область разработки и область выполнения .

Итак, с этим введением я хочу уточнить наследование :
Наследование - это связь между классами, а не объектами .
Наследование может существовать в области разработки, а не в среде выполнения. В Runtime Scope нет наследования.

После запуска нашего проекта между родителем и потомком не возникает никаких отношений (если существует только наследование между дочерним классом и родительским классом). Итак, вопрос: что такое super.invokeMethod1() или super.attribute1 ? они не являются отношениями между ребенком и родителем. Все атрибуты и методы родителя передаются дочернему элементу, и это просто обозначение для доступа к частям, переданным от родителя.

Кроме того, нет никаких объектов в области разработки. Таким образом, в области разработки нет никаких экземпляров . Это просто отношения Is-A и Has-A .

Поэтому, когда мы сказали:

Я бы сказал, FileNotFoundException является экземпляром IOException

Мы должны уточнить о нашей области (разработка и время выполнения).
Например, если FileNotFoundException является экземпляром IOException , то какова связь между конкретным исключением FileNotFoundException во время выполнения (Объект) и FileNotFoundException . Это экземпляр экземпляра?

Примечание 1 :
Почему мы использовали Наследование? Целью наследования является расширение функциональных возможностей родительского класса (на основе того же типа).

  • Это расширение может происходить путем добавления новых атрибутов или новых методов.
  • Или переопределение существующих методов.
  • Кроме того, расширяя родительский класс, мы также можем достичь возможности повторного использования.
  • Мы не можем ограничивать функциональность родительского класса (принцип Лискова)
  • Мы должны иметь возможность заменить ребенка в качестве родителя в системе (принцип Лискова)
  • и так далее.

Примечание 2 :
Ширина и глубина наследования иерархий
Ширина и глубина наследования могут быть связаны со многими факторами:

  • Проект: Сложность проекта (Type Complexity) и его архитектура и дизайн. Размер проекта, количество классов и т. Д.
  • Команда: опыт команды в управлении сложностью проекта.
  • и так далее.

Тем не менее, у нас есть некоторые эвристики об этом. (Эвристика объектно-ориентированного проектирования, Артур Дж. Риэль)

В теории иерархии наследования должны быть глубокими - чем глубже, тем лучше.

На практике иерархии наследования не должны быть глубже, чем средний человек может хранить в своей краткосрочной памяти. Популярное значение для этой глубины - шесть.

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

Примечание 3 :
Когда мы используем неправильное наследование?
На основании:

  • Примечание 1: цель Наследования (Расширение функциональных возможностей родительского класса)
  • Примечание 2: ширина и глубина наследования

В этих условиях мы используем неправильное наследование:

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

  2. У нас много классов в иерархии наследования. Это вызывает чрезмерную специализацию. Некоторые причины могут быть причиной этого:

    • Может быть, мы не учли примечание 1 (Расширение родительских функций)
    • Возможно, наша Модуляризация (упаковка) не верна. И мы собрали много вариантов использования системы в одном пакете, и мы должны сделать Design Refactoring.
  3. Это и другие причины, но не совсем связанные с этим ответом.

Примечание 4 :
Что нам делать? Когда мы используем неправильное наследование?

Решение 1: Мы должны выполнить Рефакторинг проекта, чтобы проверить значение классов для Расширения родительской функциональности . В этом рефакторинге может быть удалено много классов системы.

Решение 2: Мы должны выполнить Design Refactoring to modularization. В этом рефакторинге, возможно, некоторые классы нашего пакета передаются другим пакетам.

Решение 3: Использование композиции поверх наследования .
Мы можем использовать эту технику по многим причинам. Динамическая иерархия - одна из популярных причин, по которой мы предпочитаем Composition вместо Inheritance.
см. примечания Тима Будро (Солнца) here :

Иерархии объектов не масштабируются

Решение 4: использовать экземпляры над подклассами

Этот вопрос об этой технике. Позвольте мне назвать его экземплярами над подклассами .

Когда мы можем использовать это:

  1. (Совет 1): обратите внимание на Примечание 1, когда мы точно не расширяем функциональные возможности родительского класса. Или расширения не являются разумными и достаточно.

  2. (Совет 2 :) Обратите внимание на примечание 2. Если у нас много подклассов (полу или идентичных классов), которые немного расширяют родительский класс, и мы можем управлять этим расширением без наследования. Обратите внимание, что это не так легко сказать. Мы должны доказать, что он не нарушает другие объектно-ориентированные принципы, такие как принцип Open-Close.

Что нам делать?
Мартин Фаулер рекомендует ( Книга 1 страница 232 и Книга 2 страница 251):

Замените Подкласс Полями , Измените методы на поля суперкласса и исключите подклассы.

Мы можем использовать другие методы, такие как enum в качестве упомянутого вопроса.

Я использую Java в качестве примера, но это больше общий вопрос, связанный с дизайном ООП.

Возьмем в качестве примера IOException в Java. Почему, например, существует класс FileNotFoundException ? Разве это не должно быть экземпляром IOException где причиной является FileNotFound ? Я бы сказал, FileNotFoundException является экземпляром IOException . Где это заканчивается? FileNotFoundButOnlyCheckedOnceException , FileNotFoundNoMatterHowHardITriedException ..?

Я также видел код в проектах, в которых работал, где существовали такие классы, как FirstLineReader и LastLineReader . Для меня такие классы на самом деле представляют собой экземпляры, но я вижу такой дизайн во многих местах. Посмотрите на исходный код Spring Framework, например, он поставляется с сотнями таких классов, где каждый раз, когда я вижу один, я вижу экземпляр вместо чертежа. Разве классы не должны быть чертежами?

Я пытаюсь спросить, как можно принять решение между этими двумя очень простыми вариантами:

Опция 1:

enum DogBreed {
    Bulldog, Poodle;
}

class Dog {
    DogBreed dogBreed;
    public Dog(DogBreed dogBreed) {
        this.dogBreed = dogBreed;
    }
}

Вариант 2:

class Dog {}

class Bulldog extends Dog {
}

class Poodle extends Dog {
}

Первый вариант дает вызывающей стороне требование настроить экземпляр, который он создает. Во втором варианте класс уже представляет сам экземпляр (на мой взгляд, что может быть совершенно неверно ..).

Если вы согласны с тем, что эти классы представляют экземпляры, а не чертежи, скажите, что это хорошая практика - создавать классы, представляющие экземпляры, или это совершенно неверно, как я смотрю на это, и мое утверждение «классы, представляющие экземпляры» - это просто загрузка ерунда?


Вариант 1 должен перечислить все известные причины во время объявления.

Вариант 2 можно расширить, создав новые классы, не затрагивая исходное объявление.

Это важно, когда базовое / оригинальное объявление выполняется структурой. Если бы было 100 известных, фиксированных причин проблем ввода-вывода, перечисление или что-то подобное могло бы иметь смысл, но если могут появиться новые способы связи, которые также должны быть исключениями ввода-вывода, то иерархия классов имеет больше смысла. Любая библиотека классов, которую вы добавляете в свое приложение, может расширяться за счет большего числа исключений ввода-вывода, не затрагивая исходное объявление.

Это в основном O в SOLID , открытый для расширения, закрытый для модификации.

Но именно поэтому, например, перечисления типа DayOfWeek существуют во многих средах. Крайне маловероятно, что западный мир внезапно просыпается однажды и решает пойти на 14 уникальных дней, или 8, или 6. Так что занятия для них, вероятно, излишни. Эти вещи более закреплены в камне (удар по дереву).


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

Композиция работает так:

class Poodle {
    Legs legs;
    Tail tail;
}

class Bulldog {
    Legs legs;
    Tail tail;
}

Оба имеют общий набор характеристик, которые мы можем объединить, чтобы «составить» класс. Мы можем специализировать компоненты там, где это необходимо, но можем просто ожидать, что «Ноги» в основном работают как другие ноги.

Java выбрала наследование вместо композиции для IOException и FileNotFoundException .

То есть FileNotFoundException является своего рода (то есть extends ) IOException и разрешает обработку на основе только идентификатора суперкласса (хотя вы можете указать специальную обработку, если захотите).

Аргументы для выбора композиции вместо наследования хорошо репетируются другими и могут быть легко найдены путем поиска «композиция против наследования».


Первый код, который вы цитируете, включает исключения.

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

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

(Кроме того, исключения должны быть расширяемыми через границы DLL (компиляции). Когда мы используем перечисления, у нас могут возникнуть проблемы с расширением дизайна без изменения источника, который вводит (и другой, который потребляет) перечисление.)

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

Ваш вариант 1 - это скорее пример композиции, тогда как ваш вариант 2 явно является примером наследования.

Если вы согласны с тем, что эти классы представляют экземпляры, а не чертежи, скажите, что это хорошая практика - создавать классы, представляющие экземпляры, или это совершенно неверно, как я смотрю на это, и мое утверждение «классы, представляющие экземпляры» - это просто загрузка ерунда?

Я согласен с вами и не сказал бы, что это хорошая практика. Эти классы, как показано, не особенно настраиваемы и не представляют добавленной стоимости.

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

Чтобы было ясно, однако, есть много худших примеров наследования, например, когда (до) занятие, такое как Учитель или Студент, наследует от Person. Это означает, что Учитель не может быть Студентом одновременно, если мы не будем добавлять еще больше классов, например, TeacherStudent, возможно, используя множественное наследование.

Мы можем назвать этот класс взрывом, так как иногда нам в конечном итоге нужна матрица классов из-за неуместных отношений is-a. (Добавьте один новый класс, и вам понадобится целая новая строка или столбец разнесенных классов.)

Работа с дизайном, который подвергается взрыву классов, на самом деле создает больше работы для клиентов, потребляющих эти абстракции, так что это свободная ситуация.
Здесь речь идет о нашем доверии к естественному языку, потому что когда мы говорим, что кто-то является студентом, это, с логической точки зрения, не является тем же самым постоянным отношением «есть» / «экземпляр» (подкласса), а скорее потенциально временная роль, которую играет Человек: одна из многих возможных ролей, которые Человек может играть одновременно с этим. В этих случаях состав явно превосходит наследование.

Однако в вашем сценарии BullDog вряд ли сможет быть чем-то иным, чем BullDog, поэтому постоянное отношение-подклассы сохраняется, и, хотя добавление небольшого значения, по крайней мере, эта иерархия не рискует взрывом класса.

Обратите внимание, что основным недостатком подхода enum является то, что enum может не расширяться в зависимости от используемого вами языка. Если вам нужна произвольная расширяемость (например, другими пользователями и без изменения вашего кода), у вас есть выбор: использовать что-то расширяемое, но более слабо типизированное, например строки (опечатки не перехватываются, дубликаты не перехватываются и т. Д.), Или Вы можете использовать наследование, так как оно предлагает достойную расширяемость при более строгой типизации. Исключения требуют такого рода расширяемости другими без модификации и перекомпиляции оригиналов и других, так как они используются через границы DLL.

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





oop