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 имеет смысл начать метод с строки , чтобы соответствовать остальной части Octave, которая выводит имя переменной для отображения, за которым следует значение. В общем случае не рекомендуется переопределять функцию printf ("%s =", inputname (1))display.
-
: display
(obj)¶ -
Отобразить содержимое объекта obj с его именем.
Интерпретатор Octave вызывает функцию
displayвсякий раз, когда ему нужно отобразить класс на экране. Как правило, это будет оператор, не заканчивающийся точкой с запятой, чтобы подавить вывод. Например:myclass (...)
Или:
myobj = myclass (...)
В общем случае, пользовательские классы должны переопределять метод
dispдля предотвращения стандартного вывода:myobj = myclass (...) ⇒ myobj = <class myclass>
При переопределении метода
displayнеобходимо позаботиться об правильном отображении имени объекта. Это можно сделать с помощью функцииinputname.
После существования конструктора и метода отображения можно создать экземпляр класса. Также можно проверить тип класса и изучить базовую структуру.
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–2023 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.2.0/Inheritance-and-Aggregation.html