繼承自類和抽象類

免責宣告:此處提供的示例僅用於顯示抽象類和繼承的使用,可能不一定具有實際用途。此外,在 MATLAB 中沒有多型的東西,因此抽象類的使用是有限的。此示例用於顯示建立類的人員,從其他類繼承並應用抽象類來定義公共介面。

在 MATLAB 中,抽象類的使用相當有限,但它在某些情況下仍然有用。

假設我們想要一個訊息記錄器。我們可能會建立一個類似於下面的類:

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

屬性和方法

簡而言之,屬性保持物件的狀態,而方法就像介面並定義物件的動作。

scrh 酒店受到保護。這就是它必須在建構函式中初始化的原因。還有其他方法(getter)來訪問此屬性,但它不能應對此示例。屬性和方法可以通過變數訪問,該變數通過使用點表示法後跟方法或屬性的名稱來儲存物件的引用:

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

屬性和方法可以是公共的,私有的或受保護的。在這種情況下,protected 表示我可以從繼承的類訪問 scrh,但不能從外部訪問。預設情況下,所有屬性和方法都是公共的因此 LogMessage() 可以在類定義之外自由使用。此外,LogMessage 定義了一個介面,這意味著當我們希望物件記錄我們的自定義訊息時,這是我們必須呼叫的。

應用

假設我有一個指令碼,我使用我的記錄器:

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

如果我有多個地方使用相同的記錄器,然後想要將其更改為更復雜的東西,例如在檔案中寫入訊息,我將不得不建立另一個物件:

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

並只需將程式碼的一行更改為:

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

上面的方法只是開啟一個檔案,在檔案的末尾新增一條訊息並關閉它。目前,為了與我的介面保持一致,我需要記住方法的名稱是 LogMessage(),但它同樣可以是其他任何東西。MATLAB 可以通過使用抽象類強制開發人員使用相同的名稱。假設我們為任何記錄器定義了一個通用介面:

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

現在,如果 ScreenLoggerDeepLogger 都繼承自此類,則如果未定義 LogMessage(),MATLAB 將生成錯誤。抽象類有助於構建可以使用相同介面的類似類。

為了這個例子,我將做出稍微不同的改變。我將假設 DeepLo​​gger 將同時在螢幕和檔案中執行日誌訊息。因為 ScreenLogger 已經在螢幕上記錄了訊息,所以我將從 ScreenLoggger 繼承 DeepLogger 以避免重複。除第一行外,ScreenLogger 完全沒有變化:

classdef ScreenLogger < MessageLogger
// the rest of previous code 

然而,DeepLogger 需要更多的 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

首先,我只是在建構函式中初始化屬性。其次,因為這個類繼承自 ScreenLogger,我也必須初始化這個 parrent 物件。這一行更為重要,因為 ScreenLogger 建構函式需要一個引數來初始化它自己的物件。這一行:

obj = obj@ScreenLogger(screenhandler);

簡單地說“呼叫 ScreenLogger 的 consructor 並用螢幕處理程式將其初始化”。值得注意的是,我已將 scrh 定義為受保護。因此,我可以從 DeepLogger 同樣訪問這個屬性。如果該屬性被定義為私有。初始化它的唯一方法是使用承包商。

另一個變化是在 methods 節。為了避免重複,我從父類呼叫 LogMessage() 來在螢幕上記錄訊息。如果我不得不改變任何東西來改進螢幕日誌,現在我必須在一個地方做。其餘的程式碼與 DeepLogger 的一部分相同。

因為這個類也繼承自抽象類 MessageLogger,我必須確保 DeepLogger 中的 LogMessage() 也被定義。繼承 MessageLogger 在這裡有點棘手。我認為它重新定義了 LogMessage 強制性 - 我的猜測。

就應用記錄器的程式碼而言,由於類中的通用介面,我可以放心地保證在整個程式碼中這一行不會出現任何問題。相同的訊息將像以前一樣登入螢幕,但另外程式碼會將此類訊息寫入檔案。

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

我希望這些例子解釋了類的使用,繼承的使用以及抽象類的使用。

PS。解決上述問題的方法之一就是其中之一。另一個不那麼複雜的解決方案是將 ScreenLoger 作為另一個記錄器的元件,如 FileLogger 等 .ScreenLogger 將儲存在其中一個屬性中。它的 LogMessage 只需呼叫 ScreenLoggerLogMessage 並在螢幕上顯示文字。我選擇了更復雜的方法來表示類在 MATLAB 中是如何工作的。以下示例程式碼:

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