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 вызовет конструктор без аргументов при загрузке объектов из сохраненных файлов, чтобы определить структуру наследования.
Класс может быть потомком более чем одного класса (см. класс), и наследование может быть вложенным. Нет ограничений на количество родителей или уровень вложенности, за исключением ограничений памяти или других физических ограничений.
Для класса 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 > 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/v6.4.0/Inheritance-and-Aggregation.html