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.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow