继承自类和抽象类
免责声明:此处提供的示例仅用于显示抽象类和继承的使用,可能不一定具有实际用途。此外,在 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