免责声明:此处提供的示例仅用于说明抽象类和继承的使用,不一定具有实际用途。另外,在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受到保护。这就是为什么必须在构造函数中对其进行初始化的原因。还有其他方法(获取器)可以访问此属性,但此示例无法解决。可以通过一个变量来访问属性和方法,该变量使用点符号后跟方法或属性的名称来保存对对象的引用:
mylogger = ScreenLogger(1); % OK
mylogger.LogMessage('My %s %d message', 'very', 1); % OK
mylogger.scrh = 2; % ERROR!!! Access denied属性和方法可以是公共的,私有的或受保护的。在这种情况下,保护意味着我将能够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继承这个类,MATLAB会如果产生错误LogMessage()没有定义。抽象类有助于构建可以使用相同接口的相似类。
为了这个例子,我将做一些稍微不同的更改。我将假设DeepLogger将同时在屏幕和文件中同时记录消息。因为ScreenLogger已经在屏幕上记录了消息,所以我将继承DeepLogger自ScreenLoggger以避免重复。ScreenLogger除了第一行外根本没有改变:
classdef ScreenLogger < MessageLogger // 其余的先前代码
但是,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自我,所以我也必须初始化此对象。这一行甚至更为重要,因为ScreenLogger构造函数需要一个参数来初始化其自己的对象。这行:
obj = obj@ScreenLogger(screenhandler);
只需说“调用ScreenLogger的指导者并用屏幕处理程序将其初始化”即可。在这里值得注意的是,我已将其定义scrh为受保护的。因此,我同样可以从访问此属性DeepLogger。如果该属性定义为私有。初始化它的唯一方法是使用消费者。
在部分中有另一个更改methods。为了避免重复,我再次LogMessage()从父类中调用以在屏幕上记录消息。如果必须更改任何内容以改善屏幕记录,现在必须在一个地方进行。其余代码与相同DeepLogger。
因为此类也继承自抽象类,MessageLogger所以我必须确保LogMessage()内部DeepLogger也已定义。从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成为另一个记录器的组成部分,例如FileLoggeretc。ScreenLogger将被保存在其中一个属性中。它LogMessage会简单地调用LogMessage的ScreenLogger,并显示在屏幕上的文字。我选择了更复杂的方法来展示类在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