Spec-Zone.ru › Octave 9

34.5 Наследование и агрегация

Создание новых классов с помощью классов поддерживается в Octave посредством наследования и агрегации.

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

Проще всего показать это на примере. Предположим, программисту нужен КИХ-фильтр, то есть фильтр с полиномом в числителе и единицей в знаменателе. В традиционном программировании на Octave это выглядело бы так.

>> x = [some data vector];
>> n = [some coefficient vector];
>> y = filter (n, 1, x);

Аналогичное поведение можно реализовать в виде класса @FIRfilter. Конструктор этого класса находится в файле FIRfilter.m в каталоге класса @FIRfilter.

## -*- texinfo -*-
## @deftypefn  {} {} FIRfilter ()
## @deftypefnx {} {} FIRfilter (@var{p})
## Create a FIR filter with polynomial @var{p} as coefficient vector.
## @end deftypefn

function f = FIRfilter (p)

  if (nargin == 0)
    p = @polynomial ([1]);
  elseif (! isa (p, "polynomial"))
    error ("@FIRfilter: P must be a polynomial object");
  endif

  f.polynomial = [];
  f = class (f, "FIRfilter", p);

endfunction

Как и прежде, начальные комментарии содержат документацию к конструктору класса. Этот конструктор очень похож на конструктор класса полиномов, за исключением того, что объект полинома передаётся третьим аргументом функции class, указывая Octave, что класс FIRfilter будет производным от класса полиномов. Сам класс КИХ-фильтра не содержит полей данных, однако должен передать структуру функции class. Поскольку конструктор @polynomial добавляет в структуру объекта элемент с именем polynomial, @FIRfilter лишь инициализирует структуру фиктивным полем polynomial, которое впоследствии будет перезаписано.

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

Класс может быть дочерним по отношению к нескольким классам (см. class), а наследование может быть вложенным. Число родительских классов и уровень вложенности ограничены только объёмом памяти и другими физическими ограничениями.

Для класса FIRfilter требуется более точно управлять отображением объекта. Поэтому переопределяется метод display, а не метод disp (см. Методы классов). Простой пример:

function display (f)
  printf ("%s.polynomial", inputname (1));
  disp (f.polynomial);
endfunction

Обратите внимание, что метод отображения класса FIRfilter использует метод disp класса polynomial для непосредственного отображения коэффициентов фильтра. Кроме того, обратите внимание, что в методе display имеет смысл начинать с строки printf ("%s =", inputname (1)), чтобы соответствовать остальному поведению Octave, который выводит имя отображаемой переменной, а затем её значение. В целом переопределять функцию display не рекомендуется.

: display (obj)

Отображает содержимое объекта obj, предваряя его именем объекта.

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

myclass (...)

Или:

myobj = myclass (...)

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

myobj = myclass (...)
  ⇒ myobj =

  <class myclass>

При переопределении метода display вместо этого необходимо позаботиться о корректном отображении имени объекта. Для этого можно использовать функцию inputname.

См. также: disp, class, subsref, subsasgn.

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

octave:1> f = FIRfilter (polynomial ([1 1 1]/3))
f.polynomial = 0.33333 + 0.33333 * X + 0.33333 * X ^ 2
octave:2> class (f)
ans = FIRfilter
octave:3> isa (f, "FIRfilter")
ans =  1
octave:4> isa (f, "polynomial")
ans =  1
octave:5> struct (f)
ans =

  scalar structure containing the fields:

polynomial = 0.33333 + 0.33333 * X + 0.33333 * X ^ 2

Остаётся добавить метод обработки данных, чтобы класс стал пригоден к использованию. Однако перед этим обычно желательно предусмотреть возможность изменения данных, хранящихся в классе. Поскольку по умолчанию поля базовой структуры являются закрытыми, необходимо предоставить механизм доступа к ним. Для обеих задач можно использовать метод subsref.

function r = subsref (f, x)

  switch (x.type)

    case "()"
      n = f.polynomial;
      r = filter (n.poly, 1, x.subs{1});

    case "."
      fld = x.subs;
      if (! strcmp (fld, "polynomial"))
        error ('@FIRfilter/subsref: invalid property "%s"', fld);
      endif
      r = f.polynomial;

    otherwise
      error ("@FIRfilter/subsref: invalid subscript type for FIR filter");

  endswitch

endfunction

В случае "()" можно фильтровать данные с помощью полинома, переданного конструктору.

octave:2> f = FIRfilter (polynomial ([1 1 1]/3));
octave:3> x = ones (5,1);
octave:4> y = f(x)
y =

   0.33333
   0.66667
   1.00000
   1.00000
   1.00000

В случае "." можно просмотреть содержимое поля полинома.

octave:1> f = FIRfilter (polynomial ([1 1 1]/3));
octave:2> f.polynomial
ans = 0.33333 + 0.33333 * X + 0.33333 * X ^ 2

Чтобы изменить содержимое объекта, необходим метод subsasgn. Например, следующий код делает поле полинома доступным для записи извне:

function fout = subsasgn (f, index, val)

  switch (index.type)
    case "."
      fld = index.subs;
      if (! strcmp (fld, "polynomial"))
        error ('@FIRfilter/subsasgn: invalid property "%s"', fld);
      endif
      fout = f;
      fout.polynomial = val;

    otherwise
      error ("@FIRfilter/subsasgn: Invalid index type")
  endswitch

endfunction

так что

octave:1> f = FIRfilter ();
octave:2> f.polynomial = polynomial ([1 2 3])
f.polynomial = 1 + 2 * X + 3 * X ^ 2

Определение класса FIRfilter как дочернего класса полиномов означает, что объект FIRfilter можно использовать везде, где можно использовать объект полинома. Для фильтра это нехарактерный способ использования. Более разумным подходом к проектированию может быть агрегация вместо наследования. В этом случае полином является просто полем структуры класса. Конструктор класса для случая агрегации может выглядеть так:

## -*- texinfo -*-
## @deftypefn  {} {} FIRfilter ()
## @deftypefnx {} {} FIRfilter (@var{p})
## Create a FIR filter with polynomial @var{p} as coefficient vector.
## @end deftypefn

function f = FIRfilter (p)

  if (nargin == 0)
    f.polynomial = @polynomial ([1]);
  else
    if (! isa (p, "polynomial"))
      error ("@FIRfilter: P must be a polynomial object");
    endif

    f.polynomial = p;
  endif

  f = class (f, "FIRfilter");

endfunction

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

© 1996–2026 The Octave Project Developers
Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies.
Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions.
https://docs.octave.org/v9.4.0/Inheritance-and-Aggregation.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API