Zoeken…


Een klasse definiëren

Een klasse kan worden gedefinieerd met behulp van classdef in een .m bestand met dezelfde naam als de klasse. Het bestand kan het bevatten classdef ... end block en lokale functies voor gebruik binnen methoden van de klasse.

De meest algemene MATLAB-klassedefinitie heeft de volgende structuur:

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

MATLAB-documentatie: Klasseattributen , Eigenschapsattributen , Methodeattributen , Gebeurtenisattributen , Opsommingstype beperkingen .

Voorbeeldklasse:

Een klasse met de naam Car kan in het bestand Car.m worden gedefinieerd als

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

Merk op dat de constructor een methode is met dezelfde naam als de klasse. <Een constructor is een speciale methode van een klasse of structuur in objectgeoriënteerd programmeren die een dergelijk object initialiseert. Een constructor is een instantiemethode die meestal dezelfde naam heeft als de klasse en kan worden gebruikt om de waarden van de leden van een object in te stellen, hetzij standaard of door de gebruiker gedefinieerde waarden.

Een exemplaar van deze klasse kan worden gemaakt door de constructor aan te roepen;

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

Het aanroepen van de drive verhoogt de kilometerstand

>> myCar.mileage 
    
    ans = 
            0

>> myCar.drive(450);

>> myCar.mileage
    
   ans = 
            450

Waarde versus handvatklassen

Klassen in MATLAB zijn verdeeld in twee hoofdcategorieën: waardeklassen en afhandelingsklassen. Het grote verschil is dat bij het kopiëren van een instantie van een waardeklasse, de onderliggende gegevens worden gekopieerd naar de nieuwe instantie, terwijl voor afhandelingsklassen de nieuwe instantie naar de oorspronkelijke gegevens verwijst en de waarden in de nieuwe instantie worden gewijzigd in de oorspronkelijke instantie. Een klasse kan worden gedefinieerd als een handle door over te nemen van de handle klasse.

classdef valueClass
    properties
        data
    end
end

en

classdef handleClass < handle
    properties
        data
    end
end

vervolgens

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

Overerving van klassen en abstracte klassen

Disclaimer: de hier gepresenteerde voorbeelden zijn alleen bedoeld om het gebruik van abstracte klassen en overerving aan te tonen en hoeven niet noodzakelijkerwijs praktisch te zijn. Ook is er geen zoiets als polymorf in MATLAB en daarom is het gebruik van abstracte klassen beperkt. Dit voorbeeld laat zien wie een klasse moet maken, erven van een andere klasse en een abstracte klasse toepassen om een gemeenschappelijke interface te definiëren.

Het gebruik van abstracte klassen is vrij beperkt in MATLAB, maar het kan nog steeds nuttig zijn bij een paar gelegenheden.

Laten we zeggen dat we een berichtenlogger willen. We kunnen een klasse maken die lijkt op die hieronder:

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

Eigenschappen en methoden

Kortom, eigenschappen hebben de status van een object, terwijl methoden als interface werken en acties op objecten definiëren.

Het eigendom scrh is beschermd. Daarom moet het in een constructor worden geïnitialiseerd. Er zijn andere methoden (getters) om toegang te krijgen tot deze eigenschap, maar dit voorbeeld valt buiten dit voorbeeld. Eigenschappen en methoden kunnen toegankelijk zijn via een variabele die een verwijzing naar een object bevat met behulp van puntnotatie gevolgd door een naam van een methode of een eigenschap:

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

Eigenschappen en methoden kunnen openbaar, privé of beveiligd zijn. In dit geval betekent beschermd dat ik toegang heb tot scrh van een overgeërfde klasse, maar niet van buitenaf. Standaard zijn alle eigenschappen en methoden openbaar. Daarom kan LogMessage() vrij worden gebruikt buiten de klassedefinitie. LogMessage definieert ook een interface, wat betekent dat we dit moeten noemen als we willen dat een object onze aangepaste berichten registreert.

Toepassing

Stel dat ik een script heb waarin ik mijn logger gebruik:

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

Als ik meerdere plaatsen heb waar ik dezelfde logger gebruik en deze wil veranderen in iets geavanceerder, zoals een bericht in een bestand schrijven, zou ik een ander object moeten maken:

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

en verander gewoon een regel code hierin:

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

De bovenstaande methode opent eenvoudig een bestand, voegt een bericht toe aan het einde van het bestand en sluit het. Op dit moment moet ik, om consistent te zijn met mijn interface, onthouden dat de naam van een methode LogMessage() maar het kan evengoed iets anders zijn. MATLAB kan ontwikkelaar dwingen zich aan dezelfde naam te houden door abstracte klassen te gebruiken. Laten we zeggen dat we een gemeenschappelijke interface definiëren voor elke logger:

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

Als zowel ScreenLogger als DeepLogger van deze klasse overnemen, genereert MATLAB een fout als LogMessage() niet is gedefinieerd. Abstracte klassen helpen bij het bouwen van vergelijkbare klassen die dezelfde interface kunnen gebruiken.

Omwille van dit voorbeeld zal ik een iets andere verandering aanbrengen. Ik ga ervan uit dat DeepLogger zowel een logboekbericht op een scherm als in een bestand tegelijkertijd uitvoert. Omdat ScreenLogger al berichten op het scherm logt, ga ik DeepLogger erven van ScreenLoggger om herhaling te voorkomen. ScreenLogger verandert helemaal niet, behalve de eerste regel:

classdef ScreenLogger < MessageLogger
// the rest of previous code 

DeepLogger heeft echter meer wijzigingen nodig in de LogMessage methode:

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

Ten eerste initialiseer ik eenvoudig eigenschappen in de constructor. Ten tweede, omdat deze klasse van ScreenLogger erft, moet ik ook dit parrent-object initialiseren. Deze regel is nog belangrijker omdat ScreenLogger constructor één parameter nodig heeft om zijn eigen object te initialiseren. Deze lijn:

obj = obj@ScreenLogger(screenhandler);

zegt eenvoudig "bel de consructor van ScreenLogger en initialiseer het met een schermhandler". Het is vermeldenswaard dat ik scrh als beschermd heb gedefinieerd. Daarom had ik ook vanuit DeepLogger toegang tot deze eigenschap. Als de eigenschap is gedefinieerd als privé. De enige manier om het te initialiseren is door consctor te gebruiken.

Een andere wijziging is in methods . Nogmaals om herhaling te voorkomen, roep ik LogMessage() van een bovenliggende klasse op om een bericht op een scherm te loggen. Als ik iets moest veranderen om verbeteringen aan te brengen in de schermregistratie, moet ik dat nu op één plek doen. De DeepLogger is hetzelfde als het is een onderdeel van DeepLogger .

Omdat deze klasse ook van een abstracte klasse MessageLogger erft, moest ik ervoor zorgen dat LogMessage() in DeepLogger ook is gedefinieerd. Het overnemen van MessageLogger is hier een beetje lastig. Ik denk dat het herdefiniëren van LogMessage verplicht is - denk ik.

Wat betreft de code waarin de logger wordt toegepast, kan ik, dankzij een gemeenschappelijke interface in klassen, er zeker van zijn dat deze regel in de hele code geen problemen zou veroorzaken. Dezelfde berichten worden op het scherm aangemeld als voorheen, maar bovendien schrijft de code dergelijke berichten naar een bestand.

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

Ik hoop dat deze voorbeelden het gebruik van klassen, het gebruik van overerving en het gebruik van abstracte klassen hebben verklaard.


PS. De oplossing voor het bovenstaande probleem is een van de vele. Een andere, minder complexe oplossing zou zijn om van ScreenLoger een onderdeel te maken van een andere logger zoals FileLogger etc. ScreenLogger zou in een van de eigenschappen worden bewaard. De LogMessage zou LogMessage van de ScreenLogger eenvoudigweg aanroepen en tekst op een scherm weergeven. Ik heb voor een meer complexe aanpak gekozen om liever te laten zien hoe klassen werken in MATLAB. De onderstaande voorbeeldcode:

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

constructors

Een constructor is een speciale methode in een klasse die wordt aangeroepen wanneer een instantie van een object wordt gemaakt. Het is een normale MATLAB-functie die invoerparameters accepteert, maar hij moet ook bepaalde regels volgen.

Constructors zijn niet vereist omdat MATLAB een standaard maakt. In de praktijk is dit echter een plek om een staat van een object te definiëren. Eigenschappen kunnen bijvoorbeeld worden beperkt door attributen op te geven . Vervolgens kan een constructor dergelijke eigenschappen standaard initialiseren of door de gebruiker gedefinieerde waarden die in feite kunnen worden verzonden door invoerparameters van een constructor.

Een constructeur van een eenvoudige klasse bellen

Dit is een eenvoudige 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

De naam van een constructor is hetzelfde als de naam van een klasse. Bijgevolg worden constructeurs bij de naam van zijn klasse genoemd. Een Person kan als volgt worden gemaakt:

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

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

Een constructeur van een kinderklasse bellen

Klassen kunnen worden overgenomen van bovenliggende klassen als de gemeenschappelijke eigenschappen of methoden worden gedeeld. Wanneer een klasse van een andere klasse wordt geërfd, is het waarschijnlijk dat een constructor van een bovenliggende klasse moet worden aangeroepen.

Een Member van de klasse erft van een Person van de klasse omdat het Member dezelfde eigenschappen gebruikt als de persoon van de klasse, maar het voegt ook payment aan zijn definitie.

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

Net als bij de klasse Person , wordt Member gemaakt door de constructor aan te roepen:

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

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

Een constructor van Person heeft drie invoerparameters nodig. Member moet dit feit respecteren en daarom een constructor van de klasse Person met drie parameters bellen. Het wordt vervuld door de regel:

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

Het bovenstaande voorbeeld toont het geval wanneer een kindklasse informatie nodig heeft voor zijn bovenliggende klasse. Daarom heeft een constructor van Member vier parameters nodig: drie voor zijn bovenliggende klasse en één voor zichzelf.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow