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