Recherche…


Définir une classe

Une classe peut être définie en utilisant classdef dans un fichier .m portant le même nom que la classe. Le fichier peut contenir le bloc classdef ... end et les fonctions locales à utiliser dans les méthodes de classe.

La définition de classe MATLAB la plus générale a la structure suivante:

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

Documentation MATLAB: attributs de classe, attributs de propriété, attributs de méthode, attributs d' événement , restrictions de classe d'énumération .

Exemple de classe:

Une classe appelée Car peut être définie dans le fichier Car.m comme

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

Notez que le constructeur est une méthode avec le même nom que la classe. <Un constructeur est une méthode spéciale d'une classe ou d'une structure dans la programmation orientée objet qui initialise un objet de ce type. Un constructeur est une méthode d'instance qui porte généralement le même nom que la classe et peut être utilisée pour définir les valeurs des membres d'un objet, soit par défaut, soit en fonction de valeurs définies par l'utilisateur.

Une instance de cette classe peut être créée en appelant le constructeur;

>> myCar = Car('Ford', 'Mustang'); //creating an instance of car class 

L' appel de la drive méthode incrémenter le kilométrage

>> myCar.mileage 
    
    ans = 
            0

>> myCar.drive(450);

>> myCar.mileage
    
   ans = 
            450

Classes valeur vs poignée

Les classes dans MATLAB sont divisées en deux catégories principales: les classes de valeur et les classes de handle. La principale différence est que lors de la copie d'une instance d'une classe de valeur, les données sous-jacentes sont copiées dans la nouvelle instance, tandis que pour les classes de descripteurs, la nouvelle instance pointe sur les données d'origine. Une classe peut être définie comme un handle en héritant de la classe de handle .

classdef valueClass
    properties
        data
    end
end

et

classdef handleClass < handle
    properties
        data
    end
end

puis

>> 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

Héritage des classes et des classes abstraites

Déni de responsabilité: les exemples présentés ici ont uniquement pour but de montrer l'utilisation de classes abstraites et de l'héritage et ne sont pas nécessairement utiles. En outre, il n'y a aucune chose aussi polymorphe dans MATLAB et par conséquent l'utilisation de classes abstraites est limitée. Cet exemple montre qui doit créer une classe, hérite d'une autre classe et applique une classe abstraite pour définir une interface commune.

L'utilisation de classes abstraites est plutôt limitée dans MATLAB, mais elle peut toujours être utile à quelques reprises.

Disons que nous voulons un enregistreur de messages. Nous pourrions créer une classe similaire à celle ci-dessous:

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

Propriétés et méthodes

En résumé, les propriétés contiennent un état d'objet tandis que les méthodes sont comme une interface et définissent des actions sur des objets.

La propriété scrh est protégée. C'est pourquoi il doit être initialisé dans un constructeur. Il existe d'autres méthodes (getters) pour accéder à cette propriété, mais cela ne correspond pas à cet exemple. Les propriétés et les méthodes peuvent être accessibles via une variable qui contient une référence à un objet en utilisant la notation par points suivie du nom d'une méthode ou d'une propriété:

mylogger = ScreenLogger(1);                         % OK
mylogger.LogMessage('My %s %d message', 'very', 1); % OK
mylogger.scrh = 2;                                  % ERROR!!! Access denied

Les propriétés et les méthodes peuvent être publiques, privées ou protégées. Dans ce cas, protected signifie que je pourrai accéder à scrh partir d'une classe héritée mais pas de l'extérieur. Par défaut, toutes les propriétés et méthodes sont publiques. Par conséquent, LogMessage() peut être librement utilisé en dehors de la définition de classe. En outre, LogMessage définit une interface, ce qui signifie que nous devons appeler ce message lorsque nous voulons qu'un objet enregistre nos messages personnalisés.

Application

Disons que j'ai un script où j'utilise mon enregistreur:

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 j'ai plusieurs endroits où j'utilise le même enregistreur et que je veux ensuite le transformer en quelque chose de plus sophistiqué, comme écrire un message dans un fichier, je devrai créer un autre objet:

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

et changez simplement une ligne de code en ceci:

clc;
% ... a code
logger = DeepLogger('mymessages.log');

La méthode ci-dessus ouvre simplement un fichier, ajoute un message à la fin du fichier et le ferme. Pour le moment, pour être cohérent avec mon interface, je dois me souvenir que le nom d'une méthode est LogMessage() mais que cela pourrait également être tout autre chose. MATLAB peut forcer le développeur à conserver le même nom en utilisant des classes abstraites. Disons que nous définissons une interface commune pour tout enregistreur:

classdef MessageLogger
    methods(Abstract=true)
        LogMessage(obj, varargin);
    end
end

Maintenant, si ScreenLogger et DeepLogger héritent tous deux de cette classe, MATLAB générera une erreur si LogMessage() n'est pas défini. Les classes abstraites aident à créer des classes similaires pouvant utiliser la même interface.

Pour cette raison, je vais faire un changement légèrement différent. Je vais supposer que DeepLogger fera les deux messages de journalisation sur un écran et dans un fichier en même temps. Étant ScreenLogger que ScreenLogger enregistre déjà les messages à l'écran, je vais hériter de DeepLogger de ScreenLoggger pour éviter les répétitions. ScreenLogger ne change pas du tout en dehors de la première ligne:

classdef ScreenLogger < MessageLogger
// the rest of previous code 

Cependant, DeepLogger besoin de plus de modifications dans la méthode 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

Tout d'abord, j'initialise simplement les propriétés dans le constructeur. Deuxièmement, comme cette classe hérite de ScreenLogger je dois également initialiser cet objet parrent. Cette ligne est d'autant plus importante que le constructeur de ScreenLogger nécessite un paramètre pour l'initialisation de son propre objet. Cette ligne:

obj = obj@ScreenLogger(screenhandler);

il suffit de dire "Appelle le consortium de ScreenLogger et l'initialise avec un gestionnaire d'écran". Il convient de noter ici que j'ai défini scrh comme protégé. Par conséquent, je pourrais également accéder à cette propriété à partir de DeepLogger . Si la propriété a été définie comme privée. La seule façon de l'initialiser serait d'utiliser le consœur.

Un autre changement concerne les methods section. Pour éviter la répétition, j'appelle LogMessage() depuis une classe parente pour enregistrer un message sur un écran. Si je devais changer quoi que ce soit pour améliorer la journalisation de l’écran, je dois maintenant le faire au même endroit. Le code de repos est identique à celui de DeepLogger .

Parce que cette classe hérite également d'une classe abstraite MessageLogger je devais m'assurer que LogMessage() dans DeepLogger était également défini. Hériter de MessageLogger est un peu difficile ici. Je pense que cela rend la redéfinition de LogMessage obligatoire - à mon avis.

En ce qui concerne le code où un enregistreur est appliqué, grâce à une interface commune dans les classes, je peux assurer que cette ligne dans le code entier ne posera aucun problème. Les mêmes messages seront enregistrés à l’écran, mais le code écrira ces messages dans un fichier.

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');

J'espère que ces exemples expliquent l'utilisation des classes, l'utilisation de l'héritage et l'utilisation de classes abstraites.


PS La solution au problème ci-dessus est l'une des nombreuses. Une autre solution, moins complexe, consisterait à faire de ScreenLoger un composant d'un autre enregistreur comme FileLogger etc. ScreenLogger serait conservé dans l'une des propriétés. Son LogMessage appelle simplement LogMessage du ScreenLogger et affiche le texte sur un écran. J'ai choisi une approche plus complexe pour montrer comment les classes fonctionnent dans MATLAB. L'exemple de code ci-dessous:

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

Constructeurs

Un constructeur est une méthode spéciale dans une classe appelée lorsqu'une instance d'un objet est créée. C'est une fonction MATLAB standard qui accepte les paramètres d'entrée, mais elle doit également respecter certaines règles .

Les constructeurs ne sont pas requis car MATLAB en crée un par défaut. En pratique, cependant, il s'agit d'un endroit pour définir un état d'un objet. Par exemple, les propriétés peuvent être restreintes en spécifiant des attributs . Ensuite, un constructeur peut initaliser de telles propriétés par défaut ou des valeurs définies par l'utilisateur qui peuvent en fait être envoyées par des paramètres d'entrée d'un constructeur.

Appeler un constructeur d'une classe simple

Ceci est une classe simple 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

Le nom d'un constructeur est le même que le nom d'une classe. Par conséquent, les constructeurs sont appelés par le nom de leur classe. Une Person classe peut être créée comme suit:

>> p = Person('John','Smith','London')
p = 
  Person with properties:

       name: 'John'
    surname: 'Smith'
    address: 'London'

Appeler un constructeur d'une classe enfant

Les classes peuvent être héritées des classes parentes si elles partagent des propriétés ou des méthodes communes. Lorsqu'une classe est héritée d'une autre, il est probable qu'un constructeur d'une classe parente doit être appelé.

Un Member classe hérite d'une classe Person car Member utilise les mêmes propriétés que la classe Person mais ajoute également le payment à sa définition.

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 même que pour la classe Person , Member est créé en appelant son constructeur:

>> m = Member('Adam','Woodcock','Manchester',20)
m = 
  Member with properties:

    payment: 20
       name: 'Adam'
    surname: 'Woodcock'
    address: 'Manchester'

Un constructeur de Person nécessite trois paramètres d'entrée. Member doit respecter ce fait et donc appeler un constructeur de la classe Person avec trois paramètres. Il est rempli par la ligne:

obj = obj@Person(name,surname,address);

L'exemple ci-dessus montre le cas où une classe enfant a besoin d'informations pour sa classe parente. C'est pourquoi un constructeur de Member requiert quatre paramètres: trois pour sa classe parente et un pour lui-même.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow