繼承自類和抽象類
免責宣告:此處提供的示例僅用於顯示抽象類和繼承的使用,可能不一定具有實際用途。此外,在 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
現在,如果 ScreenLogger
和 DeepLogger
都繼承自此類,則如果未定義 LogMessage()
,MATLAB 將生成錯誤。抽象類有助於構建可以使用相同介面的類似類。
為了這個例子,我將做出稍微不同的改變。我將假設 DeepLogger 將同時在螢幕和檔案中執行日誌訊息。因為 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
只需呼叫 ScreenLogger
的 LogMessage
並在螢幕上顯示文字。我選擇了更復雜的方法來表示類在 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