【问题标题】:Private constructor in matlab oopmatlab oop中的私有构造函数
【发布时间】:2015-04-16 10:05:59
【问题描述】:

Matlab 不允许定义不同的方法来定义具有不同参数列表的多个构造函数,例如这将不起作用:

classdef MyClass

    methods
        function [this] = MyClass()
            % public constructor
            ...
        end        
    end    
    methods (Access = private)
        function [this] = MyClass(i)
            % private constructor
            ...
        end
    end

end

但是,如上例所示,具有无法从公共接口调用的特定语法的 private 构造函数有时很有用。

在需要同时定义公共和私有构造函数的情况下,您将如何最好地处理?

检查调用堆栈???

classdef MyClass
    methods
        function [this] = MyClass(i)
            if (nargin == 0)
                 % public constructor
                 ...
            else
                 % private constructor
                 checkCalledFromMyClass();
                 ...
            end
        end
    end
    methods(Static=true, Access=Private)
        function [] = checkCalledFromMyClass()
           ... here throw an error if the call stack does not contain reference to `MyClass` methods ...
        end
    end
end

定义一个辅助基类???

% Helper class 
classdef MyClassBase
    methods (Access = ?MyClass)
       function MyClassBase(i)
       end
    end
end

% Real class
classdef MyClass < MyClassBase
    methods
       function [this] = MyClass()
           this = this@MyClassBase();
       end
    end
    methods (Static)
       function [obj] = BuildSpecial()
           obj = MyClassBase(42); %%% NB: Here returned object is not of the correct type :( ...
       end
    end
end

其他解决方案???

【问题讨论】:

    标签: matlab oop


    【解决方案1】:

    我用来尝试解决此限制的一个偷偷摸摸的技巧是使用另一个只能由MyClass 构造的“标签”类,然后使用它来计算出您需要的构造函数变体。这是一个简单的草图:

    classdef MyClass
        properties
            Info
        end
        methods
            function o = MyClass(varargin)
                if nargin == 2 && ...
                           isa(varargin{1}, 'MyTag') && ...
                           isnumeric(varargin{2}) && ...
                           isscalar(varargin{2})
                    o.Info = sprintf('Private constructor tag: %d', varargin{2});
                else
                    o.Info = sprintf('Public constructor with %d args.', nargin);
                end
            end
        end
        methods (Static)
            function o = build()
            % Call the 'private' constructor
                o = MyClass(MyTag(), 3);
            end
        end
    end
    

    classdef MyTag
        methods (Access = ?MyClass)
            function o = MyTag()
            end
        end
    end
    

    注意Access = ?MyClass 说明符,这意味着只有MyClass 可以构建MyTag 的实例。在文档中有更多关于使用这种方法属性的信息:http://www.mathworks.com/help/matlab/matlab_oop/method-attributes.html

    【讨论】:

    • 请注意,这只是对@matlabgui 答案的改进,但它使用语言功能而不是依赖于调用堆栈。
    • 非常好...在我的示例中还想到了MyClassBase 中的(Access = ?MyClass),但看不到如何使用BuildSpecial() 返回正确的类型...很好的解决方案。
    • @Edric 这似乎是对 OP 问题的正确答案,但我很想知道什么样的要求可能会导致您需要一个表现类似的类。听起来您以前需要做类似的事情 - 如果您能说出来,那是什么?
    • @SamRoberts 在我的情况下,这实际上是为了强制用户使用 cmets 中所示的 ViewAs 方法来回答您的问题……尽管我同意隐藏 ob2 = MyClass(obj1, 'NewConventions') 构造函数并不是绝对强制性的。 .. 一般的想法是,当您需要从另一个对象的内部状态构建一个新对象而不公开该状态时,即使是暂时的,作为公共构造函数中的参数(+不需要深入检查这些参数,因为它们已经来自有效对象同类型)。
    • @SamRoberts 大多数情况下,我在编写需要记录的公共构造函数的类时使用过它(静态构建方法通常对于 MathWorks 编写的类不赞成 - 但我们仍然有一些,例如gpuArray.zeros...),但出于性能原因或其他原因,我们需要一个可以更有效地做事/跳过一些错误检查等的构造函数。
    【解决方案2】:

    利用varargin,您可以拥有一个接受多种语法的构造函数:

    classdef MyClass
    
        methods (Access = public)
            function obj = MyClass(varargin)
                % Do whatever you want with varargin
            end        
        end 
    
    end
    

    通常情况下,您可能有一些所有语法都需要的输入,然后还有一些可选输入:

    classdef MyClass
    
        methods (Access = public)
            function obj = MyClass(reqInput1, reqInput2, varargin)
                % Do whatever you want with varargin
            end        
        end 
    
    end
    

    如果您想更好地控制事物的构造方式,我将有一个构造函数,然后还有一些称为构造函数的公共静态方法。

    例如,假设我希望能够通过直接提供参数或通过提供包含参数的配置文件的名称来构造一个对象:

    classdef MyClass
    
        methods (Access = public)
            function obj = MyClass(reqInput1, reqInput2, varargin)
                % Do whatever you want with varargin
            end        
        end 
    
        methods (Static, Access = public)
            function obj = fromFile(filename)
                 myparams = readmyconfigfile(filename);
                 obj = MyClass(myparams.reqInput1, myparams.reqInput2, ...);
            end
        end
    
    end
    

    然后您可以使用o = MyClass(inputs)o = MyClass.fromFile(filename) 创建一个对象。

    如果您想允许人们从配置文件中构造 only,那么您可以将构造函数设为私有。如果你想以其他方式调用构造函数,你可以添加额外的公共静态方法。

    在任何情况下,要点是让构造函数接受多种语法的惯用方式是利用varargin

    【讨论】:

    • 我同意 varargin 和静态构造方法,无论如何,这里的想法更多的是允许用户使用经典构造函数语法创建对象,同时防止他们调用仅为内部使用而保留的特殊构造语法(例如创建一个具有特定状态的对象,否则无法从公共接口访问)。
    • 想象一下MyClass 中的ViewAs 方法,它允许创建某个对象的轻量级副本,以便在其他约定中表示它,同时仍然引用初始对象以避免内存重复并允许以任何约定 (obj2 = obj1.ViewAs('NewConventions');) 操作对象。显然,ViewAs 方法类似于obj2 = MyClass(obj1, 'NewConventions'),但我不想公开这种构造语法以迫使用户通过更具表现力的ViewAs
    • 您还可以想象如果不从内部类方法调用可能导致无效状态的内部构造语法。
    • 我想我理解你的要求——如果我理解正确,那么@Edric 的回答似乎符合你的需要。不过说实话,我真的无法想象在哪种情况下我会选择创建一个以这种方式运行的类。
    【解决方案3】:

    我很想对您的第一个选项进行修改,但要将构造函数修改为具有未记录的“模式”,而不仅仅是任何输入参数。

    在下面的示例中,我给了该名称 **private**,但您可以拥有最终用户不太可能偶然发现的任何您想要的东西......

    为了确保您仍然可以检查堆栈。

        function [this] = MyClass(i)
            if (nargin == 0)
                 % public constructor
            else
                 % private constructor
                 if ischar ( i ) && strcmp ( i, '**private**' )
                     this.checkCalledFromMyClass();
                 else
                     error ( 'MyClass:Consturctor', 'Error calling MyClass' );
                 end
            end
        end
    end
    

    【讨论】:

      猜你喜欢
      • 2011-04-20
      • 1970-01-01
      • 2019-10-16
      • 2012-01-02
      • 2018-04-01
      相关资源
      最近更新 更多