Spec-Zone.ru › Octave 5

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

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

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

Это проще всего продемонстрировать на примере. Предположим, программисту нужен фильтр FIR, т.е. фильтр с многочленом числителя, но знаменателем 1. В традиционной программировании 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 > 1)
    print_usage ();
  endif

  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 будет получен от класса многочленов. Сам класс FIR-фильтра не имеет полей данных, но он должен предоставить структуру функции 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 > 1)
    print_usage ();
  endif

  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–2022 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/v5.2.0/Inheritance-and-Aggregation.html

Spec-Zone.ru

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