Spec-Zone.ru › Octave 8

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 == 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 == 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/v7.2.0/Inheritance-and-Aggregation.html

Spec-Zone.ru

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