Наследование the inheritance

Single Table Inheritance (Наследование с единой таблицей)

Паттерн проектирования Single Table Inheritance

Описание Single Table Inheritance

Представление всех классов из иерархии наследования в виде одной таблицы в БД, содержащей столбцы для всех полей различных классов.

Реляционные БД не поддерживают наследование, по этому, записывая данные об объектах в БД, мы вынуждены придумывать, как отобразить наследование в таблицах. Конечно, мы стараемся минимизировать JOIN’ы, которые мнговенно появятся, если наследование реализовывать несколькими таблицами в БД. Паттерн Single Table Inheritance (наследование с единой таблицей) записывает все поля всех классов иерархии в одну таблицу.

Использована иллюстрация с сайта Мартина Фаулера.

Наследование классов в JavaScript

Наследование на уровне объектов в JavaScript, как мы видели, реализуется через ссылку __proto__ .

Теперь поговорим о наследовании на уровне классов, то есть когда объекты, создаваемые, к примеру, через new Admin , должны иметь все методы, которые есть у объектов, создаваемых через new User , и ещё какие-то свои.

Наследование Array от Object

Для реализации наследования в наших классах мы будем использовать тот же подход, который принят внутри JavaScript.

Взглянем на него ещё раз на примере Array , который наследует от Object :

  • Методы массивов Array хранятся в Array.prototype .
  • Array.prototype имеет прототипом Object.prototype .

Поэтому когда экземпляры класса Array хотят получить метод массива – они берут его из своего прототипа, например Array.prototype.slice .

Если же нужен метод объекта, например, hasOwnProperty , то его в Array.prototype нет, и он берётся из Object.prototype .

Отличный способ «потрогать это руками» – запустить в консоли команду console.dir([1,2,3]) .

Вывод в Chrome будет примерно таким:

Здесь отчётливо видно, что сами данные и length находятся в массиве, дальше в __proto__ идут методы для массивов concat , то есть Array.prototype , а далее – Object.prototype .

Обратите внимание, я использовал именно console.dir , а не console.log , поскольку log зачастую выводит объект в виде строки, без доступа к свойствам.

Наследование в наших классах

Применим тот же подход для наших классов: объявим класс Rabbit , который будет наследовать от Animal .

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

Для того, чтобы наследование работало, объект rabbit = new Rabbit должен использовать свойства и методы из своего прототипа Rabbit.prototype , а если их там нет, то – свойства и методы родителя, которые хранятся в Animal.prototype .

Если ещё короче – порядок поиска свойств и методов должен быть таким: rabbit -> Rabbit.prototype -> Animal.prototype , по аналогии с тем, как это сделано для объектов и массивов.

Для этого можно поставить ссылку __proto__ с Rabbit.prototype на Animal.prototype .

Можно сделать это так:

Однако, прямой доступ к __proto__ не поддерживается в IE10-, поэтому для поддержки этих браузеров мы используем функцию Object.create . Она либо встроена либо легко эмулируется во всех браузерах.

Класс Animal остаётся без изменений, а Rabbit.prototype мы будем создавать с нужным прототипом, используя Object.create :

Теперь выглядеть иерархия будет так:

В prototype по умолчанию всегда находится свойство constructor , указывающее на функцию-конструктор. В частности, Rabbit.prototype.constructor == Rabbit . Если мы рассчитываем использовать это свойство, то при замене prototype через Object.create нужно его явно сохранить:

Полный код наследования

Для наглядности – вот итоговый код с двумя классами Animal и Rabbit :

Как видно, наследование задаётся всего одной строчкой, поставленной в правильном месте.

Обратим внимание: Rabbit.prototype = Object.create(Animal.prototype) присваивается сразу после объявления конструктора, иначе он перезатрёт уже записанные в прототип методы.

В некоторых устаревших руководствах предлагают вместо Object.create(Animal.prototype) записывать в прототип new Animal , вот так:

Частично, он рабочий, поскольку иерархия прототипов будет такая же, ведь new Animal – это объект с прототипом Animal.prototype , как и Object.create(Animal.prototype) . Они в этом плане идентичны.

Но у этого подхода важный недостаток. Как правило мы не хотим создавать Animal , а хотим только унаследовать его методы!

Более того, на практике создание объекта может требовать обязательных аргументов, влиять на страницу в браузере, делать запросы к серверу и что-то ещё, чего мы хотели бы избежать. Поэтому рекомендуется использовать вариант с Object.create .

Вызов конструктора родителя

Посмотрим внимательно на конструкторы Animal и Rabbit из примеров выше:

Как видно, объект Rabbit не добавляет никакой особенной логики при создании, которой не было в Animal .

Чтобы упростить поддержку кода, имеет смысл не дублировать код конструктора Animal , а напрямую вызвать его:

Такой вызов запустит функцию Animal в контексте текущего объекта, со всеми аргументами, она выполнится и запишет в this всё, что нужно.

Здесь можно было бы использовать и Animal.call(this, name) , но apply надёжнее, так как работает с любым количеством аргументов.

Переопределение метода

Итак, Rabbit наследует Animal . Теперь если какого-то метода нет в Rabbit.prototype – он будет взят из Animal.prototype .

В Rabbit может понадобиться задать какие-то методы, которые у родителя уже есть. Например, кролики бегают не так, как остальные животные, поэтому переопределим метод run() :

Вызов rabbit.run() теперь будет брать run из своего прототипа:

Вызов метода родителя внутри своего

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

Для вызова метода родителя можно обратиться к нему напрямую, взяв из прототипа:

Обратите внимание на вызов через apply и явное указание контекста.

Если вызвать просто Animal.prototype.run() , то в качестве this функция run получит Animal.prototype , а это неверно, нужен текущий объект.

Читайте так же:  План трудовой кодекс рф

Для наследования нужно, чтобы «склад методов потомка» ( Child.prototype ) наследовал от «склада метода родителей» ( Parent.prototype ).

Это можно сделать при помощи Object.create :

Для того, чтобы наследник создавался так же, как и родитель, он вызывает конструктор родителя в своём контексте, используя apply(this, arguments) , вот так:

При переопределении метода родителя в потомке, к исходному методу можно обратиться, взяв его напрямую из прототипа:

Структура наследования полностью:

Такое наследование лучше функционального стиля, так как не дублирует методы в каждом объекте.

Кроме того, есть ещё неявное, но очень важное архитектурное отличие.

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

Иначе говоря, в функциональном стиле в процессе создания Rabbit нужно обязательно вызывать Animal.apply(this, arguments) , чтобы получить методы родителя – и если этот Animal.apply кроме добавления методов говорит: «Му-у-у!», то это проблема:

…Которой нет в прототипном подходе, потому что в процессе создания new Rabbit мы вовсе не обязаны вызывать конструктор родителя. Ведь методы находятся в прототипе.

Поэтому прототипный подход стоит предпочитать функциональному как более быстрый и универсальный. А что касается красоты синтаксиса – она сильно лучше в новом стандарте ES6, которым можно пользоваться уже сейчас, если взять транслятор babeljs.

Наследование the inheritance

Наследования является одним из фундаментальных понятий ООП. Приведем его определение:

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

Суть понятия Наследование кроется уже в самом названии. Приведем пример наследования из реальной жизни.

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

Пытаясь провести классификацию некоторых новых животных или объектов, мы задаем следующие вопросы: В чем сходство этого объекта с другими объектами общего класса? В чем различия ? Каждый класс имеет набор поведений и характеристик, которые его определяют. Мы начинаем с верхушки фамильного дерева образца и будем спускаться по ветвям, задавая эти вопросы на протяжении всего пути. Более высокие уровни являются более общими, а вопросы более простыми: например, есть крылья или нет крыльев? Каждый уровень является более специфическим, чем предыдущий уровень и менее общим. Когда характеристика определена, все категории ниже этого определения включают эту характеристику. Поэтому, когда мы говорим про того или иного конкретного представителя класса(отряда, вида и т.д.), то нам не надо говорить про его общие особенности, характерные для этого класса, а говорим только про его специфические особенности в рамках этого класса.

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

Приведем пример. Можно создать какой-то базовый класс «транспортное средство», который универсален для всех средств передвижения, к примеру, на 4-х колесах. Этот класс «знает» как двигаются колеса, как они поворачивают, тормозят и т.д. А затем на основе этого класса создадим класс «легковой автомобиль», который унаследуем из класса «транспортное средство». Поскольку мы новый класс унаследовали из класса «транспортное средство», то мы и унаследовали все особенности этого класса и нам не надо в очередной раз описывать как двигаются колеса и т.д. Мы просто добавим те черты, особенности поведения, которые характерны для легковых автомобилей. В то же время мы можем взять за основу этот же класс «транспортное средство» и построить класса «грузовые автомобили». Описав отличительные особенности грузовых автомобилей, мы получим уже новый класс «грузовые автомобили». А, к примеру, на основании класса «грузовой автомобиль» уже можно описать определенный подкласс грузовиков и т.д. Таким образом, нам не надо каждый раз описывать все «с нуля». В этом и заключается главное преимущество использования механизма наследования. Мы как бы сначала формируем простой шаблон, а затем все усложняя и конкретизируя, поэтапно создаем все более сложный шаблон.

В данном случае был приведен пример простого наследования, когда наследование производится только из одного класса. В некоторых объектно-ориентированных языках программирования определены механизмы наследования, позволяющие наследовать из одного и более класса. Однако реализация подобных механизмов зависят от самого применяемого языка ООП. Кроме того, следует отметить, что особенности реализации даже простого наследования могут различаться от языка ООП к языку.

И немного о терминологии. В описаниях языков ООП принято класс, из которого наследуют называть родительским классом (parent class) или основой класса(base class). Класс, который получаем в результате наследования называется порожденным классом (derived or child class). Родительский класс всегда считается более общим и развернутым. Порожденный же класс всегда более строгий и конкретный, что делает его более удобным в применении при конкретной реализации.

Как вывод из всего сказанного приведу следующее мнение, что:

ООП — это процесс построения иерархии классов. А одним из наиболее важных свойств ООП является механизм, по которому типы классов могут наследовать характеристики из более простых, общих типов. Этот механизм называется наследованием. Наследование обеспечивает общность функций, в то же время допуская столько особенностей, сколько необходимо.

Наследование the inheritance

Postgres Pro реализует наследование таблиц, что может быть полезно для проектировщиков баз данных. (Стандарт SQL:1999 и более поздние версии определяют возможность наследования типов, но это во многом отличается от того, что описано здесь.)

Читайте так же:  Льготы учащимся военных вузов

Давайте начнём со следующего примера: предположим, что мы создаём модель данных для городов. В каждом штате есть множество городов, но лишь одна столица. Мы хотим иметь возможность быстро получать город-столицу для любого штата. Это можно сделать, создав две таблицы: одну для столиц штатов, а другую для городов, не являющихся столицами. Однако, что делать, если нам нужно получить информацию о любом городе, будь то столица штата или нет? В решении этой проблемы может помочь наследование. Мы определим таблицу capitals как наследника cities :

В этом случае таблица capitals наследует все столбцы своей родительской таблицы, cities . Столицы штатов также имеют дополнительный столбец state , в котором будет указан штат.

В Postgres Pro таблица может наследоваться от нуля или нескольких других таблиц, а запросы могут выбирать все строки родительской таблицы или все строки родительской и всех дочерних таблиц. По умолчанию принят последний вариант. Например, следующий запрос найдёт названия всех городов, включая столицы штатов, расположенных выше 500 футов:

Для данных из введения (см. Раздел 2.1) он выдаст:

А следующий запрос находит все города, которые не являются столицами штатов, но также находятся на высоте выше 500 футов:

Здесь ключевое слово ONLY указывает, что запрос должен применяться только к таблице cities , но не к таблицам, расположенным ниже cities в иерархии наследования. Многие операторы, которые мы уже обсудили, — SELECT , UPDATE и DELETE — поддерживают ключевое слово ONLY .

Вы также можете добавить после имени таблицы * , чтобы обрабатывались и все дочерние таблицы:

Указывать * не обязательно, так как теперь это поведение подразумевается по умолчанию (если только вы не измените параметр конфигурации sql_inheritance). Однако такая запись может быть полезна тем, что подчеркнёт использование дополнительных таблиц.

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

этот запрос выдаст:

(Если вы попытаетесь выполнить его у себя, скорее всего вы получите другие значения OID.) Собственно имена таблиц вы можете получить, обратившись к pg_class :

в результате вы получите:

Тот же эффект можно получить другим способом, используя псевдотип regclass ; при этом OID таблицы выводится в символьном виде:

Механизм наследования не способен автоматически распределять данные команд INSERT или COPY по таблицам в иерархии наследования. Поэтому в нашем примере этот оператор INSERT не выполнится:

Мы могли надеяться на то, что данные каким-то образом попадут в таблицу capitals , но этого не происходит: INSERT всегда вставляет данные непосредственно в указанную таблицу. В некоторых случаях добавляемые данные можно перенаправлять, используя правила (см. Главу 38). Однако в нашем случае это не поможет, так как таблица cities не содержит столбца state и команда будет отвергнута до применения правила.

Дочерние таблицы автоматически наследуют от родительской таблицы ограничения-проверки и ограничения NOT NULL (если только для них не задано явно NO INHERIT ). Все остальные ограничения (уникальности, первичный ключ и внешние ключи) не наследуются.

Таблица может наследоваться от нескольких родительских таблиц, в этом случае она будет объединять в себе все столбцы этих таблиц, а также столбцы, описанные непосредственно в её определении. Если в определениях родительских и дочерней таблиц встретятся столбцы с одним именем, эти столбцы будут « объединены » , так что в дочерней таблице окажется только один столбец. Чтобы такое объединение было возможно, столбцы должны иметь одинаковый тип данных, в противном случае произойдёт ошибка. Наследуемые ограничения-проверки и ограничения NOT NULL объединяются подобным образом. Так, например, объединяемый столбец получит свойство NOT NULL, если какое-либо из порождающих его определений имеет свойство NOT NULL. Ограничения-проверки объединяются, если они имеют одинаковые имена; но если их условия различаются, происходит ошибка.

Отношение наследования между таблицами обычно устанавливается при создании дочерней таблицы с использованием предложения INHERITS оператора CREATE TABLE . Другой способ добавить такое отношение для таблицы, определённой подходящим образом — использовать INHERIT с оператором ALTER TABLE . Для этого будущая дочерняя таблица должна уже включать те же столбцы (с совпадающими именами и типами), что и родительская таблица. Также она должна включать аналогичные ограничения-проверки (с теми же именами и выражениями). Удалить отношение наследования можно с помощью указания NO INHERIT оператора ALTER TABLE . Динамическое добавление и удаление отношений наследования может быть полезно при реализации секционирования таблиц (см. Раздел 5.10).

Для создания таблицы, которая затем может стать наследником другой, удобно воспользоваться предложением LIKE оператора CREATE TABLE . Такая команда создаст новую таблицу с теми же столбцами, что имеются в исходной. Если в исходной таблицы определены ограничения CHECK , для создания полностью совместимой таблицы их тоже нужно скопировать, и это можно сделать, добавив к предложению LIKE параметр INCLUDING CONSTRAINTS .

Родительскую таблицу нельзя удалить, пока существуют унаследованные от неё. Так же как в дочерних таблицах нельзя удалять или модифицировать столбцы или ограничения-проверки, унаследованные от родительских таблиц. Если вы хотите удалить таблицу вместе со всеми её потомками, это легко сделать, добавив в команду удаления родительской таблицы параметр CASCADE .

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

В запросах с наследуемыми таблицами проверка прав доступа выполняется только в родительской таблице. Так, например, наличие разрешения UPDATE для таблицы cities подразумевает право на изменение строк также в таблице capitals , когда к ним происходит обращение через таблицу cities . Это сохраняет видимость того, что эти данные (также) находятся в родительской таблице. Но изменить таблицу capitals напрямую без дополнительного разрешения нельзя. Подобным образом, политики безопасности на уровне строк (см. Раздел 5.7) для родительской таблицы применяются к строкам, получаемым из дочерних таблиц при выполнении запроса с наследованием. Политики же дочерних таблиц, если они определены, действуют только когда такие таблицы явно задействуются в запросе; в этом случае все политики, связанные с родительскими таблицами, игнорируются.

Читайте так же:  Каким налогом облагается договор дарения

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

5.9.1. Ограничения

Заметьте, что не все SQL-команды могут работать с иерархиями наследования. Команды, выполняющие выборку данных, изменение данных или модификацию схемы (например SELECT , UPDATE , DELETE , большинство вариантов ALTER TABLE , но не INSERT и ALTER TABLE . RENAME ), обычно по умолчанию обрабатывают данные дочерних таблиц и могут исключать их, если поддерживают указание ONLY . Команды для обслуживания и настройки базы данных (например REINDEX и VACUUM ) обычно работают только с отдельными физическими таблицами и не поддерживают рекурсивную обработку отношений наследования. Соответствующее поведение каждой команды описано в её справке (Команды SQL).

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

Если мы объявим cities . name с ограничением UNIQUE или PRIMARY KEY , это не помешает добавить в таблицу capitals строки с названиями городов, уже существующими в таблице cities . И эти дублирующиеся строки по умолчанию будут выводиться в результате запросов к cities . На деле таблица capitals по умолчанию вообще не будет содержать ограничение уникальности, так что в ней могут оказаться несколько строк с одним названием. Хотя вы можете добавить в capitals соответствующее ограничение, но это не предотвратит дублирование при объединении с cities .

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

Если вы сделаете, чтобы столбец другой таблицы ссылался на cities(name) , в этом столбце можно будет указывать только названия городов, но не столиц. В этом случае хорошего решения нет.

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

FAO.org

Наследование/ правовые механизмы

Inheritance provisions may vary depending on whether the deceased was a Christian, a Hindu, or a Muslim (14).

— Inheritance for Muslims is governed by Islamic Shariah as codified in the 1961 Muslim Family Laws Ordinance, and the 1962 West Pakistan Muslim Personal Law Shariat Application Act. These laws consolidated and amended the various Muslim laws.
The definitions of heirs, and their shares, are decided according to their sects and sub-sects, such as Cutchi Memon, Khoja, Sunni or Shia (14).

The Muslim Family Law Ordinance , 1961:
— Provides for the distribution of shares according to the personal law of each religious group in the country (14).

— In general, Muslim law of inheritance is based on the following principles:
i. All shares are distributed to legal heirs by intestate succession.

ii. The shares of the inheritance depend on the closeness of the relationship of the legal heirs to the deceased. Blood relations have the closest ties. These shares are distributed depending on how many children, sisters, brothers and other relatives the deceased person had, and they may change from case to case.

iii. Female children are entitled to half the inheritance of male children; wives inherit one-eighth of their husband’s estate.

iv. Heirs acquire an absolute interest in specific shares of the estate of their ancestor, even before distribution. Vested inheritance may occur. For example, if an heir dies before distribution, but was alive at the ancestor’s death, the share of his/her vested inheritance passes on to his/her heirs (15).

— Under the Sunni law there are 12 shares in a deceased person’s property, four for males and eight for females.

— The Shia law recognizes nine shares and does not include grandfathers, grandmothers or sons and daughter/s.

— Female shares are similar under both the Sunni and the Sharia laws:
i. the wife or wives gets 1/4 of the share if there is no child or child of a son, otherwise she gets1/8 of the property;
ii. the mother gets 1/3 if there is no child or child of a son, otherwise 1/6;
iii. a daughter gets half the share of the son;
iv. in the absence of a son, the daughter gets 1/2 of the inheritance and if there are more than one daughter they collectively get 2/3 of the share (14).

— The 1962 West Pakistan Muslim Personal Law Shariat Application Act entitled Muslim women to inherit all property, including agricultural property, with shares prescribed according to the Shariat. It also extended the Shariat to all of West Pakistan, except tribal areas in the North West Frontier Province (14).

Источники: цифры в скобках (*) обратиться к источникам, отображаются в списке литературы.