MATLAB Language Héritage des classes et des classes abstraites


Exemple

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