MATLAB Language
Programación orientada a objetos
Buscar..
Definiendo una clase
Una clase se puede definir usando classdef
en un archivo .m
con el mismo nombre que la clase. El archivo puede contener la classdef
... end
del bloque y funciones locales para su uso dentro de los métodos de clase.
La definición de clase MATLAB más general tiene la siguiente estructura:
classdef (ClassAttribute = expression, ...) ClassName < ParentClass1 & ParentClass2 & ...
properties (PropertyAttributes)
PropertyName
end
methods (MethodAttributes)
function obj = methodName(obj,arg2,...)
...
end
end
events (EventAttributes)
EventName
end
enumeration
EnumName
end
end
Documentación de MATLAB: atributos de clase , atributos de propiedad , atributos de método , atributos de evento , restricciones de clase de enumeración .
Ejemplo de clase:
Una clase llamada Car
puede definirse en el archivo Car.m
como
classdef Car < handle % handle class so properties persist
properties
make
model
mileage = 0;
end
methods
function obj = Car(make, model)
obj.make = make;
obj.model = model;
end
function drive(obj, milesDriven)
obj.mileage = obj.mileage + milesDriven;
end
end
end
Tenga en cuenta que el constructor es un método con el mismo nombre que la clase. <Un constructor es un método especial de una clase o estructura en la programación orientada a objetos que inicializa un objeto de ese tipo. Un constructor es un método de instancia que generalmente tiene el mismo nombre que la clase y se puede usar para establecer los valores de los miembros de un objeto, ya sea por defecto o por valores definidos por el usuario.>
Se puede crear una instancia de esta clase llamando al constructor;
>> myCar = Car('Ford', 'Mustang'); //creating an instance of car class
Llamar al método de drive
incrementará el kilometraje.
>> myCar.mileage
ans =
0
>> myCar.drive(450);
>> myCar.mileage
ans =
450
Clases de valor vs manejo
Las clases en MATLAB se dividen en dos categorías principales: clases de valor y clases de manejo. La principal diferencia es que al copiar una instancia de una clase de valor, los datos subyacentes se copian en la nueva instancia, mientras que para las clases de manejo la nueva instancia apunta a los datos originales y el cambio de valores en la nueva instancia los cambia en el original. Una clase se puede definir como un identificador heredando de la clase de handle
.
classdef valueClass
properties
data
end
end
y
classdef handleClass < handle
properties
data
end
end
entonces
>> v1 = valueClass;
>> v1.data = 5;
>> v2 = v1;
>> v2.data = 7;
>> v1.data
ans =
5
>> h1 = handleClass;
>> h1.data = 5;
>> h2 = h1;
>> h2.data = 7;
>> h1.data
ans =
7
Herencia de clases y clases abstractas.
Descargo de responsabilidad: los ejemplos que se presentan aquí solo tienen el propósito de mostrar el uso de las clases abstractas y la herencia y no necesariamente tienen un uso práctico. Además, no hay ninguna cosa tan polimórfica en MATLAB y, por lo tanto, el uso de clases abstractas es limitado. Este ejemplo es para mostrar quién debe crear una clase, heredar de otra clase y aplicar una clase abstracta para definir una interfaz común.
El uso de clases abstractas es bastante limitado en MATLAB, pero aún puede ser útil en un par de ocasiones.
Digamos que queremos un registrador de mensajes. Podríamos crear una clase similar a la siguiente:
classdef ScreenLogger
properties(Access=protected)
scrh;
end
methods
function obj = ScreenLogger(screenhandler)
obj.scrh = screenhandler;
end
function LogMessage(obj, varargin)
if ~isempty(varargin)
varargin{1} = num2str(varargin{1});
fprintf(obj.scrh, '%s\n', sprintf(varargin{:}));
end
end
end
end
Propiedades y metodos
En resumen, las propiedades mantienen el estado de un objeto, mientras que los métodos son como una interfaz y definen acciones en los objetos.
La propiedad scrh
está protegida. Es por esto que debe ser inicializado en un constructor. Hay otros métodos (captadores) para acceder a esta propiedad, pero no es posible en este ejemplo. Se puede acceder a las propiedades y los métodos a través de una variable que contiene una referencia a un objeto utilizando la notación de puntos seguida de un nombre de un método o una propiedad:
mylogger = ScreenLogger(1); % OK
mylogger.LogMessage('My %s %d message', 'very', 1); % OK
mylogger.scrh = 2; % ERROR!!! Access denied
Las propiedades y los métodos pueden ser públicos, privados o protegidos. En este caso, protegido significa que podré acceder a scrh
desde una clase heredada pero no desde afuera. Por defecto, todas las propiedades y métodos son públicos. Por lo tanto, LogMessage()
se puede usar libremente fuera de la definición de clase. Además, LogMessage
define una interfaz que significa que esto es lo que debemos llamar cuando queremos que un objeto registre nuestros mensajes personalizados.
Solicitud
Digamos que tengo un script donde utilizo mi registrador:
clc;
% ... a code
logger = ScreenLogger(1);
% ... a code
logger.LogMessage('something');
% ... a code
logger.LogMessage('something');
% ... a code
logger.LogMessage('something');
% ... a code
logger.LogMessage('something');
Si tengo varios lugares donde utilizo el mismo registrador y luego quiero cambiarlo por algo más sofisticado, como escribir un mensaje en un archivo, tendría que crear otro objeto:
classdef DeepLogger
properties(SetAccess=protected)
FileName
end
methods
function obj = DeepLogger(filename)
obj.FileName = filename;
end
function LogMessage(obj, varargin)
if ~isempty(varargin)
varargin{1} = num2str(varargin{1});
fid = fopen(obj.fullfname, 'a+t');
fprintf(fid, '%s\n', sprintf(varargin{:}));
fclose(fid);
end
end
end
end
y solo cambia una línea de un código en esto:
clc;
% ... a code
logger = DeepLogger('mymessages.log');
El método anterior simplemente abrirá un archivo, agregará un mensaje al final del archivo y lo cerrará. En este momento, para ser coherente con mi interfaz, debo recordar que el nombre de un método es LogMessage()
pero que podría ser cualquier otra cosa. MATLAB puede forzar al desarrollador a mantener el mismo nombre usando clases abstractas. Digamos que definimos una interfaz común para cualquier registrador:
classdef MessageLogger
methods(Abstract=true)
LogMessage(obj, varargin);
end
end
Ahora, si tanto ScreenLogger
como DeepLogger
heredan de esta clase, MATLAB generará un error si LogMessage()
no está definido. Las clases abstractas ayudan a construir clases similares que pueden usar la misma interfaz.
Por el bien de este ejemplo, haré un cambio ligeramente diferente. Voy a asumir que DeepLogger hará el mensaje de registro en una pantalla y en un archivo al mismo tiempo. Como ScreenLogger
ya registra los mensajes en la pantalla, heredaré DeepLogger
de ScreenLoggger
para evitar repeticiones. ScreenLogger
no cambia en absoluto aparte de la primera línea:
classdef ScreenLogger < MessageLogger
// the rest of previous code
Sin embargo, DeepLogger
necesita más cambios en el método LogMessage
:
classdef DeepLogger < MessageLogger & ScreenLogger
properties(SetAccess=protected)
FileName
Path
end
methods
function obj = DeepLogger(screenhandler, filename)
[path,filen,ext] = fileparts(filename);
obj.FileName = [filen ext];
pbj.Path = pathn;
obj = obj@ScreenLogger(screenhandler);
end
function LogMessage(obj, varargin)
if ~isempty(varargin)
varargin{1} = num2str(varargin{1});
LogMessage@ScreenLogger(obj, varargin{:});
fid = fopen(obj.fullfname, 'a+t');
fprintf(fid, '%s\n', sprintf(varargin{:}));
fclose(fid);
end
end
end
end
En primer lugar, simplemente inicializo las propiedades en el constructor. En segundo lugar, debido a que esta clase se hereda de ScreenLogger
, también tengo que inicializar este objeto original. Esta línea es aún más importante porque el constructor ScreenLogger
requiere un parámetro para inicializar su propio objeto. Esta línea:
obj = obj@ScreenLogger(screenhandler);
simplemente dice "llame al constructor de ScreenLogger e initalize con un controlador de pantalla". Vale la pena señalar aquí que he definido scrh
como protegido. Por lo tanto, también podría acceder a esta propiedad desde DeepLogger
. Si la propiedad fue definida como privada. La única forma de inicializarlo sería utilizando un consejero.
Otro cambio está en los methods
sección. De nuevo, para evitar la repetición, llamo a LogMessage()
desde una clase primaria para registrar un mensaje en una pantalla. Si tuviera que cambiar algo para realizar mejoras en el registro de pantalla, ahora tengo que hacerlo en un solo lugar. El código de resto es el mismo que forma parte de DeepLogger
.
Debido a que esta clase también se hereda de una clase abstracta MessageLogger
, tuve que asegurarme de que LogMessage()
dentro de DeepLogger
también esté definido. Heredar de MessageLogger
es un poco complicado aquí. Creo que es obligatorio redefinir LogMessage
, supongo.
En términos del código donde se aplica un registrador, gracias a una interfaz común en las clases, puedo estar seguro de que el cambio de esta línea en todo el código no generará ningún problema. Los mismos mensajes se registrarán en la pantalla que antes, pero además el código escribirá dichos mensajes en un archivo.
clc;
% ... a code
logger = DeepLogger(1, 'mylogfile.log');
% ... a code
logger.LogMessage('something');
% ... a code
logger.LogMessage('something');
% ... a code
logger.LogMessage('something');
% ... a code
logger.LogMessage('something');
Espero que estos ejemplos expliquen el uso de las clases, el uso de la herencia y el uso de las clases abstractas.
PD. La solución para el problema anterior es una de muchas. Otra solución, menos compleja, sería hacer que ScreenLoger
sea un componente de otro registrador como FileLogger
etc. ScreenLogger
se mantendría en una de las propiedades. Su LogMessage
simplemente llamaría LogMessage
del ScreenLogger
y mostraría el texto en una pantalla. He elegido un enfoque más complejo para mostrar cómo funcionan las clases en MATLAB. El código de ejemplo a continuación:
classdef DeepLogger < MessageLogger
properties(SetAccess=protected)
FileName
Path
ScrLogger
end
methods
function obj = DeepLogger(screenhandler, filename)
[path,filen,ext] = fileparts(filename);
obj.FileName = [filen ext];
obj.Path = pathn;
obj.ScrLogger = ScreenLogger(screenhandler);
end
function LogMessage(obj, varargin)
if ~isempty(varargin)
varargin{1} = num2str(varargin{1});
obj.LogMessage(obj.ScrLogger, varargin{:}); % <-------- thechange here
fid = fopen(obj.fullfname, 'a+t');
fprintf(fid, '%s\n', sprintf(varargin{:}));
fclose(fid);
end
end
end
end
Constructores
Un constructor es un método especial en una clase que se llama cuando se crea una instancia de un objeto. Es una función MATLAB normal que acepta parámetros de entrada, pero también debe seguir ciertas reglas .
No se requieren constructores ya que MATLAB crea uno predeterminado. En la práctica, sin embargo, este es un lugar para definir un estado de un objeto. Por ejemplo, las propiedades se pueden restringir especificando atributos . Luego, un constructor puede inicializar dichas propiedades por defecto o valores definidos por el usuario que, de hecho, pueden enviarse por los parámetros de entrada de un constructor.
Llamando a un constructor de una clase simple.
Esta es una simple clase de Person
.
classdef Person
properties
name
surname
address
end
methods
function obj = Person(name,surname,address)
obj.name = name;
obj.surname = surname;
obj.address = address;
end
end
end
El nombre de un constructor es el mismo que el nombre de una clase. En consecuencia, los constructores son llamados por el nombre de su clase. Una clase de Person
puede ser creada de la siguiente manera:
>> p = Person('John','Smith','London')
p =
Person with properties:
name: 'John'
surname: 'Smith'
address: 'London'
Llamando a un constructor de una clase infantil
Las clases se pueden heredar de las clases primarias si comparten propiedades o métodos comunes. Cuando una clase se hereda de otra, es probable que se deba llamar a un constructor de una clase primaria.
Un Member
clase hereda de una Person
clase porque el Member
usa las mismas propiedades que la persona de la clase pero también agrega el payment
a su definición.
classdef Member < Person
properties
payment
end
methods
function obj = Member(name,surname,address,payment)
obj = obj@Person(name,surname,address);
obj.payment = payment;
end
end
end
De manera similar a la clase Person
, el Member
se crea llamando a su constructor:
>> m = Member('Adam','Woodcock','Manchester',20)
m =
Member with properties:
payment: 20
name: 'Adam'
surname: 'Woodcock'
address: 'Manchester'
Un constructor de Person
requiere tres parámetros de entrada. Member
debe respetar este hecho y, por lo tanto, llamar a un constructor de la clase Person
con tres parámetros. Se cumple por la línea:
obj = obj@Person(name,surname,address);
El ejemplo anterior muestra el caso cuando una clase secundaria necesita información para su clase principal. Por esta razón, un constructor de Member
requiere cuatro parámetros: tres para su clase principal y uno para sí mismo.