MATLAB 语言继承类和抽象类

示例

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