Szukaj…


Definiowanie klasy

Klasę można zdefiniować za pomocą classdef w pliku .m o tej samej nazwie co klasa. Plik może zawierać blok end classdef ... i funkcje lokalne do użycia w metodach klasowych.

Najbardziej ogólna definicja klasy MATLAB ma następującą 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

Dokumentacja MATLAB: Atrybuty klas, atrybuty właściwości, atrybuty metod, atrybuty zdarzeń , ograniczenia klas wyliczeń .

Przykładowa klasa:

Klasę o nazwie Car można zdefiniować w pliku Car.m jako

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

Zauważ, że konstruktor jest metodą o tej samej nazwie co klasa. <Konstruktor to specjalna metoda klasy lub struktury w programowaniu obiektowym, która inicjuje obiekt tego typu. Konstruktor jest metodą instancji, która zwykle ma taką samą nazwę jak klasa, i może być używana do ustawiania wartości elementów obiektu, na wartości domyślne lub wartości zdefiniowane przez użytkownika.

Instancję tej klasy można utworzyć, wywołując konstruktor;

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

Wywołanie metody drive zwiększy przebieg

>> myCar.mileage 
    
    ans = 
            0

>> myCar.drive(450);

>> myCar.mileage
    
   ans = 
            450

Wartości a klasy uchwytów

Klasy w MATLAB są podzielone na dwie główne kategorie: klasy wartości i klasy obsługi. Główną różnicą jest to, że podczas kopiowania instancji klasy wartości dane bazowe są kopiowane do nowej instancji, podczas gdy dla klas uchwytów nowa instancja wskazuje na oryginalne dane, a zmiana wartości w nowej instancji zmienia je w oryginale. Klasę można zdefiniować jako uchwyt, dziedzicząc po klasie handle .

classdef valueClass
    properties
        data
    end
end

i

classdef handleClass < handle
    properties
        data
    end
end

następnie

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

Dziedziczenie z klas i klas abstrakcyjnych

Zastrzeżenie: przedstawione tutaj przykłady służą wyłącznie do pokazania użycia klas abstrakcyjnych i dziedziczenia i niekoniecznie mogą mieć praktyczne zastosowanie. Ponadto w MATLAB nie ma takiej rzeczy polimorficznej, dlatego użycie klas abstrakcyjnych jest ograniczone. W tym przykładzie pokazano, kto ma utworzyć klasę, odziedziczyć po innej klasie i zastosować klasę abstrakcyjną, aby zdefiniować wspólny interfejs.

Korzystanie z klas abstrakcyjnych jest raczej ograniczone w MATLAB, ale wciąż może się przydać przy kilku okazjach.

Powiedzmy, że chcemy rejestratora wiadomości. Możemy stworzyć klasę podobną do poniższej:

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

Właściwości i metody

Krótko mówiąc, właściwości utrzymują stan obiektu, podczas gdy metody są jak interfejs i definiują działania na obiektach.

scrh nieruchomości jest chroniony. Dlatego należy go zainicjować w konstruktorze. Istnieją inne metody (getters), aby uzyskać dostęp do tej właściwości, ale nie jest to możliwe w tym przykładzie. Dostęp do właściwości i metod można uzyskać za pomocą zmiennej, która zawiera odwołanie do obiektu, za pomocą notacji kropkowej, po której następuje nazwa metody lub właściwości:

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

Właściwości i metody mogą być publiczne, prywatne lub chronione. W tym przypadku scrh chroniony oznacza, że będę mógł uzyskać dostęp do scrh z odziedziczonej klasy, ale nie z zewnątrz. Domyślnie wszystkie właściwości i metody są publiczne. Dlatego LogMessage() może być swobodnie używany poza definicją klasy. LogMessage definiuje również interfejs, co oznacza, że musimy to wywoływać, gdy chcemy, aby obiekt rejestrował nasze niestandardowe komunikaty.

Podanie

Załóżmy, że mam skrypt, w którym korzystam z mojego programu rejestrującego:

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

Jeśli mam wiele miejsc, w których korzystam z tego samego rejestratora, a następnie chcę go zmienić na coś bardziej zaawansowanego, na przykład napisać wiadomość w pliku, musiałbym utworzyć inny obiekt:

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

i po prostu zmień jeden wiersz kodu na:

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

Powyższa metoda po prostu otworzy plik, doda wiadomość na końcu pliku i zamknie go. W tej chwili, aby zachować spójność z moim interfejsem, muszę pamiętać, że nazwa metody to LogMessage() ale równie dobrze może to być coś innego. MATLAB może zmusić programistę do trzymania się tej samej nazwy za pomocą klas abstrakcyjnych. Załóżmy, że definiujemy wspólny interfejs dla dowolnego programu rejestrującego:

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

Teraz, jeśli zarówno ScreenLogger jak i DeepLogger dziedziczą z tej klasy, MATLAB wygeneruje błąd, jeśli LogMessage() nie zostanie zdefiniowany. Klasy abstrakcyjne pomagają budować podobne klasy, które mogą korzystać z tego samego interfejsu.

Ze względu na ten przykład dokonam nieco innej zmiany. Zakładam, że DeepLogger będzie jednocześnie rejestrował komunikat na ekranie i w pliku. Ponieważ ScreenLogger już rejestruje wiadomości na ekranie, zamierzam odziedziczyć DeepLogger z ScreenLoggger aby uniknąć powtórzeń. ScreenLogger wcale się nie zmienia poza pierwszym wierszem:

classdef ScreenLogger < MessageLogger
// the rest of previous code 

Jednak DeepLogger wymaga więcej zmian w metodzie 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

Po pierwsze, po prostu inicjuję właściwości w konstruktorze. Po drugie, ponieważ ta klasa dziedziczy z ScreenLogger muszę również zainicjować ten obiekt ScreenLogger . Ta linia jest jeszcze ważniejsza, ponieważ konstruktor ScreenLogger wymaga jednego parametru do zainicjowania własnego obiektu. Ta linia:

obj = obj@ScreenLogger(screenhandler);

po prostu mówi „zadzwoń do konstruktora ScreenLoggera i zainicjuj go za pomocą procedury obsługi ekranu”. Warto tutaj zauważyć, że zdefiniowałem scrh jako chroniony. Dlatego mogłem uzyskać równy dostęp do tej właściwości z DeepLogger . Jeśli właściwość została zdefiniowana jako prywatna. Jedynym sposobem na zainicjalizowanie go byłoby użycie konsoli.

Kolejna zmiana dotyczy methods sekcji. Ponownie, aby uniknąć powtórzeń, LogMessage() z klasy nadrzędnej, aby zarejestrować komunikat na ekranie. Gdybym musiał coś zmienić, aby ulepszyć rejestrowanie ekranu, teraz muszę to zrobić w jednym miejscu. Pozostały kod jest taki sam, ponieważ jest częścią DeepLogger .

Ponieważ ta klasa dziedziczy również po klasie abstrakcyjnej MessageLogger , musiałem się upewnić, że LogMessage() w DeepLogger jest również zdefiniowany. Dziedziczenie z MessageLogger jest tutaj trochę trudne. Myślę, że to przypadek redefinicji LogMessage obowiązkowej - zgaduję.

Jeśli chodzi o kod, w którym stosowany jest rejestrator, dzięki wspólnemu interfejsowi w klasach, mogę się upewnić, że zmiana tego jednego wiersza w całym kodzie nie spowoduje żadnych problemów. Te same wiadomości będą wyświetlane na ekranie logowania jak poprzednio, ale dodatkowo kod zapisuje takie wiadomości do pliku.

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

Mam nadzieję, że te przykłady wyjaśniły użycie klas, wykorzystanie dziedziczenia i użycie klas abstrakcyjnych.


PS. Rozwiązanie powyższego problemu jest jednym z wielu. Innym rozwiązaniem, mniej złożonym, byłoby uczynienie ScreenLoger składnikiem innego programu rejestrującego, takiego jak FileLogger itp. ScreenLogger byłby przechowywany w jednej z właściwości. Jego LogMessage po prostu wywoła LogMessage z ScreenLogger i wyświetli tekst na ekranie. Wybrałem bardziej złożone podejście, aby raczej pokazać, jak działają klasy w MATLAB. Przykładowy kod poniżej:

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

Konstruktory

Konstruktor to specjalna metoda w klasie, która jest wywoływana podczas tworzenia instancji obiektu. Jest to zwykła funkcja MATLAB, która akceptuje parametry wejściowe, ale musi również przestrzegać pewnych zasad .

Konstruktory nie są wymagane, ponieważ MATLAB tworzy domyślny. W praktyce jest to jednak miejsce do definiowania stanu obiektu. Na przykład właściwości można ograniczyć, określając atrybuty . Następnie konstruktor może domyślnie zainicjować takie właściwości lub wartości zdefiniowane przez użytkownika, które w rzeczywistości mogą zostać przesłane przez parametry wejściowe konstruktora.

Wywołanie konstruktora prostej klasy

To jest prosta Person klasowa.

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

Nazwa konstruktora jest taka sama jak nazwa klasy. W konsekwencji konstruktory są wywoływane przez nazwę swojej klasy. Person klasowa może zostać utworzona w następujący sposób:

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

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

Wywołanie konstruktora klasy potomnej

Klasy można dziedziczyć z klas nadrzędnych, jeśli mają wspólne właściwości lub metody. Gdy klasa jest dziedziczona od innej, prawdopodobnie wywoływany jest konstruktor klasy nadrzędnej.

A klasa Member dziedziczy z klasy Person ponieważ Member wykorzystuje te same właściwości jak klasy Person, ale również dodaje payment do jej definicji.

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

Podobnie jak w przypadku klasy Person , Member jest tworzony przez wywołanie jego konstruktora:

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

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

Konstruktor Person wymaga trzech parametrów wejściowych. Member musi szanować ten fakt i dlatego wywołać konstruktora klasy Person z trzema parametrami. Spełnia go linia:

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

Powyższy przykład pokazuje przypadek, gdy klasa potomna potrzebuje informacji dla swojej klasy nadrzędnej. Dlatego konstruktor elementu Member wymaga czterech parametrów: trzech dla swojej klasy nadrzędnej i jednego dla siebie.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow