четверг, 10 декабря 2015 г.

Настраиваем порядок определения методов при множественном наследовании в Perl

Как это работает по умолчанию?

package Top;

sub new { bless {}, shift } 

sub name { __PACKAGE__ }

package Left;
use base 'Top';

package Right;
use base 'Top';

sub name { __PACKAGE__ }

package Bottom;
use base qw/Left Right/;

package main;

my $obj = Bottom->new;
print $obj->name;

Случай 1. Вызывается метод name модуля Top.

  1. Есть некий объект класса "Bottom".
  2. Вызываем метод "name" на данном объекте.
  3. Так как "name" не реализован в классе "Bottom" то perl будет искать данный метод в родительских классах "Left" и "Right".
  4. Поиск начинается с крайнего левого класса из списка наследуемых, т.е. с "Left".
  5. Метод "name" так же не присутствует в классе "Left", поэтому поиск продолжается в родительских классах класса "Left", в данном случае в "Top".
  6. Так как "name" присутствует в "Top", то данный метод будет вызван и программа напечатает "Top". Метод найден, поиск завершён. 

Случай 2. Вызывается метод name модуля Right.

Теперь уберём метод "name" из модуля "Top". Тогда на шаге 6 поиск не будет завершён, а продолжит своё дело в модуле "Right". И так как "name" находится в "Right", то по аналогии с первым случаем, поиск завершается и программа печатает имя класса "Right".

Т.е. порядок поиска по классам в данных случаях таков: Bottom - Left - Top - Right.

Вышеприведённый порядок определения метода ( "Method Resolition Order" MRO ) на данном ромбовидном наследовании классов реализуется с помощью алгоритма "Поиск вглубь" ("Depth First Search" DFS). Данный алгоритм в перл используется по умолчанию.

Как нам бы хотелось, чтобы это работало?

При множественном наследовании, такой подход порой бывает непримемлем. Как правило, необходимо чтобы поиск методов происходил сначала во всех прямых родительских классах, а уж затем непосредственно и в их родительских классах. Т.е. порядок поиска был бы такой: Bottom - Left - Right - Top.

Как этого достичь?

 

Для достижения подобного поведения, всё, что нам потребуется - это добавить в модуль "Bottom", указание на другой алгоритм MRO, а именно "C3 superclass linearization". В перл для этого существует специальная прагма 'mro', которая позволяет переключать алгоритм поиска методов со стандартного 'dfs' на 'c3'.

Итак, теперь класс "Bottom" выглядит так:

package Bottom;
use mro 'c3';
use base qw/Left Right/;

При таких настройках поиск методов будет происходить по такому пути: Bottom - Left - Right - Top.

И уже в результате вызова метода "name", мы получим ожидаемый ответ "Right", вместо "Top", когда использовался стандартный поиск "DFS".

Комментариев нет:

Отправить комментарий