Sök…


Definiera en klass

En klass kan definieras med classdef i en .m fil med samma namn som klassen. Filen kan innehålla classdef ... end och lokala funktioner för användning inom klassmetoder.

Den mest allmänna definitionen av MATLAB-klassen har följande struktur:

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-dokumentation: Klassattribut , Egenskapsattribut , Metodattribut , Eventattribut , Begränsningar av uppräkningsklass .

Exempel klass:

En klass som heter Car kan definieras i filen Car.m som

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

Observera att konstruktören är en metod med samma namn som klassen. <En konstruktör är en speciell metod för en klass eller struktur i objektorienterad programmering som initialiserar ett objekt av den typen. En konstruktör är en instansmetod som vanligtvis har samma namn som klassen och kan användas för att ställa in värdena för medlemmarna i ett objekt, antingen som standard eller användardefinierade värden.>

En instans av denna klass kan skapas genom att ringa konstruktören;

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

Att ringa drive ökar körsträckan

>> myCar.mileage 
    
    ans = 
            0

>> myCar.drive(450);

>> myCar.mileage
    
   ans = 
            450

Värde vs handtagsklasser

Klasser i MATLAB är indelade i två huvudkategorier: värdeklasser och handklasser. Den största skillnaden är att när man kopierar en instans av en värdeklass kopieras de underliggande data till den nya instansen, medan för handtagsklasser pekar den nya instansen på originaldata och ändrar värden i ny instans ändrar dem i originalet. En klass kan definieras som ett handtag genom att ärva från handle .

classdef valueClass
    properties
        data
    end
end

och

classdef handleClass < handle
    properties
        data
    end
end

sedan

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

Arv från klasser och abstrakta klasser

Friskrivningsklausul: exemplen som presenteras här är endast för att visa användningen av abstrakta klasser och arv och kanske inte nödvändigtvis av praktisk användning. Det finns inte heller någon så sak som polymorf i MATLAB och därför är användningen av abstrakta klasser begränsad. Detta exempel är att visa vem som ska skapa en klass, ärva från en annan klass och tillämpa en abstrakt klass för att definiera ett gemensamt gränssnitt.

Användningen av abstrakta klasser är ganska begränsad i MATLAB men det kan fortfarande vara användbart vid ett par tillfällen.

Låt oss säga att vi vill ha en meddelandelogger. Vi kan skapa en klass som liknar den nedan:

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

Egenskaper och metoder

Kort sagt, egenskaper har ett tillstånd för ett objekt medan metoder är som gränssnitt och definierar åtgärder på objekt.

Fastigheten scrh skyddas. Det är därför det måste initialiseras i en konstruktör. Det finns andra metoder (getters) för att få tillgång till den här egenskapen men det är inte till exempel exemplet. Egenskaper och metoder kan nås via en variabel som innehåller en referens till ett objekt med hjälp av pricknotation följt av ett namn på en metod eller en egenskap:

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

Egenskaper och metoder kan vara offentliga, privata eller skyddade. I det här fallet betyder skyddat att jag kommer att kunna få åtkomst till scrh från en ärvd klass men inte utanför. Som standard är alla egenskaper och metoder offentliga. Därför kan LogMessage() fritt användas utanför klassdefinitionen. LogMessage definierar också ett gränssnitt vilket innebär att detta är vad vi måste ringa när vi vill att ett objekt ska logga in våra anpassade meddelanden.

Ansökan

Låt oss säga att jag har ett skript där jag använder min logger:

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

Om jag har flera platser där jag använder samma loggare och sedan vill ändra den till något mer sofistikerad, till exempel att skriva ett meddelande i en fil, skulle jag behöva skapa ett annat objekt:

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

och ändra bara en rad i en kod till den här:

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

Ovanstående metod öppnar helt enkelt en fil, lägger till ett meddelande i slutet av filen och stänger den. För närvarande, för att vara konsekvent med mitt gränssnitt, måste jag komma ihåg att namnet på en metod är LogMessage() men det kan lika vara allt annat. MATLAB kan tvinga utvecklaren att hålla sig till samma namn genom att använda abstrakta klasser. Låt oss säga att vi definierar ett gemensamt gränssnitt för alla loggare:

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

Om både ScreenLogger och DeepLogger ärver från denna klass kommer MATLAB att generera ett fel om LogMessage() inte är definierat. Abstrakta klasser hjälper till att bygga liknande klasser som kan använda samma gränssnitt.

För det här exemplet kommer jag att göra något annorlunda förändring. Jag kommer att anta att DeepLogger kommer att göra både loggningsmeddelande på en skärm och i en fil på samma gång. Eftersom ScreenLogger redan loggar meddelanden på skärmen kommer jag att ärva DeepLogger från ScreenLoggger att undvika upprepning. ScreenLogger förändras inte alls bortsett från den första raden:

classdef ScreenLogger < MessageLogger
// the rest of previous code 

DeepLogger behöver dock fler förändringar i LogMessage metoden:

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

Först initialiserar jag helt enkelt egenskaper i konstruktören. För det andra, eftersom denna klass ärver från ScreenLogger jag också initiera detta parrentobjekt. Den här linjen är ännu viktigare eftersom ScreenLogger konstruktören kräver en parameter för att initalisera sitt eget objekt. Denna rad:

obj = obj@ScreenLogger(screenhandler);

säger helt enkelt "ring konsulenten till ScreenLogger och initalisera den med en skärmhanterare". Det är värt att notera här att jag har definierat scrh som skyddad. Därför kunde jag lika få tillgång till den här egenskapen från DeepLogger . Om fastigheten definierades som privat. Det enda sättet att integrera det skulle vara att använda konsuktorn.

En annan förändring är i avsnittet methods . För att undvika upprepning ringer jag LogMessage() från en förälderklass för att logga ett meddelande på en skärm. Om jag var tvungen att ändra något för att göra förbättringar i skärmloggning, måste jag göra det på ett ställe. Restkoden är densamma som den är en del av DeepLogger .

Eftersom denna klass också ärver från en abstrakt klass MessageLogger jag tvungen att se till att LogMessage() inuti DeepLogger också definieras. Arv från MessageLogger är lite svårt här. Jag tror att det LogMessage omdefinition av LogMessage obligatoriskt - min gissning.

När det gäller koden där en loggare används, tack vare ett gemensamt gränssnitt i klasser, kan jag vara säker på att ändra denna rad i hela koden inte skulle göra några problem. Samma meddelanden kommer att logga in på skärmen som tidigare men dessutom kommer koden att skriva sådana meddelanden till en fil.

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

Jag hoppas att dessa exempel förklarade användningen av klasser, användningen av arv och användningen av abstrakta klasser.


PS. Lösningen för ovanstående problem är ett av många. En annan lösning, mindre komplex, skulle vara att göra ScreenLoger till en del av en annan logger som FileLogger etc. ScreenLogger skulle hållas i en av egenskaperna. LogMessage skulle helt enkelt ringa LogMessage för ScreenLogger och visa text på en skärm. Jag har valt en mer komplex metod för att snarare visa hur klasser fungerar i MATLAB. Exempelkoden nedan:

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

konstruktörer

En konstruktör är en speciell metod i en klass som kallas när en instans av ett objekt skapas. Det är en vanlig MATLAB-funktion som accepterar ingångsparametrar, men den måste också följa vissa regler .

Konstruktörer krävs inte eftersom MATLAB skapar en standard. I praktiken är det emellertid en plats att definiera ett objekts tillstånd. Till exempel kan egenskaper begränsas genom att ange attribut . Sedan kan en konstruktör initalisera sådana egenskaper som standard eller användardefinierade värden som i själva verket kan skickas med ingångsparametrar för en konstruktör.

Ring en konstruktör av en enkel klass

Detta är en enkel 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

Namnet på en konstruktör är samma som namnet på en klass. Följaktligen kallas konstruktörer med namnet på sin klass. En Person kan skapas enligt följande:

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

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

Ring en konstruktör av en barnklass

Klasser kan ärvas från moderklasser om de delar gemensamma egenskaper eller metoder. När en klass ärvs från en annan är det troligt att en konstruktör av en förälderklass måste kallas.

En klass Member ärver från en klass Person eftersom Member använder samma egenskaper som klassen person men det lägger även till payment till dess definition.

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

På samma sätt som klassen Person skapas Member genom att ringa sin konstruktör:

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

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

En Person kräver tre ingångsparametrar. Member måste respektera detta faktum och därför ringa en konstruktör av klassen Person med tre parametrar. Det uppfylls av linjen:

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

Exemplet ovan visar fallet när en barnklass behöver information för sin förälderklass. Detta är anledningen till att en konstruktör av Member kräver fyra parametrar: tre för sin överordnade klass och en för sig själv.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow