MATLAB Language
Programmation orientée objet
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.