2013-08-15 7 views
5

w Matlab, można zdefiniować klasę jako takie:Dynamicznie przypisać getter dla właściwości zależnej w programie MATLAB

classdef klass < handle 
    properties(Dependent) 
     prop 
    end 
end 

Matlab jest doskonale szczęśliwy instancji obiektu tej klasy, nawet bez definiowania getter dla prop. Zawodzi tylko wtedy, gdy próbuję uzyskać do niego dostęp (co zrozumiałe). Chciałbym ustawić dynamicznie GetMethod na podstawie nazwy właściwości.

Niestety, nawet jeśli nieruchomość jest zależna, pole meta.property dla GetMethod jest nadal tylko do odczytu. I podczas dziedziczenia z dynamicprops może pozwolić dodając właściwości i programowo ustawienie jego GetMethod w każdym przypadku, nie sądzę, może to być używane do zmiany istniejącej właściwości. Być może będę musiał udać się tą drogą, ale jako propmusi istnieć dla każdego obiektu, który wolałbym po prostu ustawić gettera na podstawie klasy. Czy coś takiego jest możliwe?

Alternatywne rozwiązanie może być metodą typu catch-all. W innych językach można to osiągnąć za pomocą podobnego do Rubiego, method_missing lub podobnego do PHP lub podobnego do PHP. Ale o ile wiem, w Matlab nie ma (udokumentowanego ani innego) analogu.


(Mój przypadek użycia: klasa ta zostanie odziedziczony przez wiele podklas zdefiniowanych przez użytkownika, a wszystkie ich właściwości zależne są dostępne w podobny sposób, tylko zmienia na podstawie nazwy właściwości Zamiast pytać użytkowników pisać get.*. metody zawijające wywołanie wspólnego kodu dla każdej z jego zależnych właściwości, chciałbym ustawić je wszystkie dynamicznie za pomocą anonimowych wskaźników funkcji zawierających niezbędne metadane).

Odpowiedz

4

Oto moja propozycja: tworzenie metodę w nadklasy nazwie add_dyn_prop. Tę metodę należy wywoływać w podklasach, zamiast tworzyć zwykły sposób tworzenia dependent property.

Pomysł polega na tym, że nadklasa dziedziczy po dynamicprops i korzysta z nowej właściwości z addprop do add i ręcznie ustawia metody dostępu na podstawie swojej nazwy.

classdef klass < dynamicprops 
    methods (Access = protected) 
     function add_dyn_prop(obj, prop, init_val, isReadOnly) 
      % input arguments 
      narginchk(2,4); 
      if nargin < 3, init_val = []; end 
      if nargin < 4, isReadOnly = true; end 

      % create dynamic property 
      p = addprop(obj, prop); 

      % set initial value if present 
      obj.(prop) = init_val; 

      % define property accessor methods 
      % NOTE: this has to be a simple function_handle (@fun), not 
      % an anonymous function (@()..) to avoid infinite recursion 
      p.GetMethod = @get_method; 
      p.SetMethod = @set_method; 

      % nested getter/setter functions with closure 
      function set_method(obj, val) 
       if isReadOnly 
        ME = MException('MATLAB:class:SetProhibited', sprintf(... 
         'You cannot set the read-only property ''%s'' of %s', ... 
         prop, class(obj))); 
        throwAsCaller(ME); 
       end 
       obj.(prop) = val; 
      end 
      function val = get_method(obj) 
       val = obj.(prop); 
      end 
     end 
    end 
end 

teraz w podklasie, zamiast definiowania właściwość zależna zwykły sposób, możemy skorzystać z tej nowej funkcji dziedziczonej w konstruktorze zdefiniować właściwość dynamiczna:

classdef subklass < klass 
    %properties (Dependent, SetAccess = private) 
    % name 
    %end 
    %methods 
    % function val = get.name(obj) 
    %  val = 'Amro'; 
    % end 
    %end 

    methods 
     function obj = subklass() 
      % call superclass constructor 
      obj = [email protected](); 

      % define new properties 
      add_dyn_prop(obj, 'name', 'Amro'); 
      add_dyn_prop(obj, 'age', [], false) 
     end    
    end 
end 

Wyjście:

>> o = subklass 
o = 
    subklass with properties: 

    age: [] 
    name: 'Amro' 
>> o.age = 10 
o = 
    subklass with properties: 

    age: 10 
    name: 'Amro' 
>> o.name = 'xxx' 
You cannot set the read-only property 'name' of subklass. 

Oczywiście można teraz dostosować metodę gettera na podstawie nazwy właściwości zgodnie z pierwotnym zamierzeniem.


EDIT:

Na podstawie uwag, proszę znaleźć poniżej lekkim odmianie tej samej techniki omówione powyżej.

Chodzi o to, aby podklasa wymagała utworzenia właściwości (zdefiniowanej jako abstrakcyjna w superklasie) zawierającej nazwy pożądanych właściwości dynamicznych, które mają zostać utworzone. Konstruktor superklasy tworzyłby wówczas określone właściwości dynamiczne, ustawiając ich metody dostępu na ogólne funkcje (które mogłyby dostosować swoje zachowanie w oparciu o nazwę właściwości, zgodnie z żądaniem). Korzystam z tej samej funkcji, o której wspomniałem wcześniej.

W podklasie po prostu musimy zaimplementować odziedziczoną właściwość abstrakcyjną dynamic_props, zainicjowaną listą nazw (lub {}, jeśli nie chcesz tworzyć żadnej właściwości dynamicznej). Na przykład możemy napisać:

classdef subklass < klass 
    properties (Access = protected) 
     dynamic_props = {'name', 'age'} 
    end 

    methods 
     function obj = subklass() 
      obj = [email protected](); 
     end 
    end 
end 

nadklasą jest podobny do tego, co mieliśmy wcześniej wcześniej, tylko teraz jest to jego obowiązkiem jest wywołać add_dyn_prop w jego konstruktora dla każdej z nazw właściwości:

classdef klass < dynamicprops  % ConstructOnLoad 
    properties (Abstract, Access = protected) 
     dynamic_props 
    end 
    methods 
     function obj = klass() 
      assert(iscellstr(obj.dynamic_props), ... 
       '"dynamic_props" must be a cell array of strings.'); 
      for i=1:numel(obj.dynamic_props) 
       obj.add_dyn_prop(obj.dynamic_props{i}, [], false); 
      end 
     end 
    end 

    methods (Access = private) 
     function add_dyn_prop(obj, prop, init_val, isReadOnly) 
      % input arguments 
      narginchk(2,4); 
      if nargin < 3, init_val = []; end 
      if nargin < 4, isReadOnly = true; end 

      % create dynamic property 
      p = addprop(obj, prop); 
      %p.Transient = true; 

      % set initial value if present 
      obj.(prop) = init_val; 

      % define property accessor methods 
      p.GetMethod = @get_method; 
      p.SetMethod = @set_method; 

      % nested getter/setter functions with closure 
      function set_method(obj,val) 
       if isReadOnly 
        ME = MException('MATLAB:class:SetProhibited', sprintf(... 
         'You cannot set the read-only property ''%s'' of %s', ... 
         prop, class(obj))); 
        throwAsCaller(ME); 
       end 
       obj.(prop) = val; 
      end 
      function val = get_method(obj) 
       val = obj.(prop); 
      end 
     end 
    end 
end 

Uwaga: Nie użyłem atrybutu klasy ConstructOnLoad lub atrybutu właściwości Transient, ponieważ nadal nie jestem pewien, w jaki sposób wpłynęłoby to na ładowanie obiektu z zapisanego pliku MAT w odniesieniu do właściwości dynamicznych.

>> o = subklass 
o = 
    subklass with properties: 

    age: [] 
    name: [] 

>> o.name = 'Amro'; o.age = 99 
o = 
    subklass with properties: 

    age: 99 
    name: 'Amro' 
+1

To jest najbliżej tego, co zrobiłem: każda z podklas definiuje właściwość 'dynamic_props', a nadklasa (wymuszona na' [ConstructOnLoad] (http://www.mathworks.com/help/matlab/matlab_oop/class -constructor-methods.html) ') inicjuje je wszystkie w swoim konstruktorze, ustawiając GetMethod na anonimową funkcję, która przechowuje nazwę właściwości, aby umożliwić ogólną implementację. Myślę, że jest nieco prostszy i lepiej zdefiniowany dla użytkownika. Chciałbym, żeby Matlab miał bardziej dynamiczne możliwości programowania OO. Twoje zdrowie! –

+1

@MattB .: Wysłałem alternatywną implementację na podstawie tego, co opisałeś. – Amro

+0

Tak, dokładnie jak to zrobiłem, z wyjątkiem Transient = true i constructOnLoad. Wydaje się, że działa dobrze z zapisywaniem i ładowaniem dla moich celów. Zastanawiałem się, czy nie odpowiedzieć na moje własne pytanie, ale uznałem, że jest wystarczająco blisko. Teraz jest naprawdę! –

2

Sprawdź, czy tego właśnie chcesz. Problem polega na tym, że użytkownik będzie musiał uzyskać właściwości za pomocą(), co może być dość nudne, ale w każdym razie myślę, że w ten sposób można zmienić zmienne. Nie można ich zmienić bezpośrednio w klasie, ale można zmienić wartości właściwości obiektów na żądanie. Nie trzeba zmieniać wartości w konstruktorze, możesz to zrobić za pomocą innej funkcji, która będzie dziedziczona przez klasy.

klass1.m

classdef(InferiorClasses = {?klass2}) klass < handle 

    methods 
    function self = klass 
     selfMeta = metaclass(self); 
     names = {selfMeta.PropertyList.Name}; 
     for name = names 
     switch name{1} 
     case 'prop_child_1' 
      self.(name{1}) = @newGetChild1PropFcn; 
     case 'prop_child_2' 
      self.(name{1}) = @newGetChild2PropFcn; 
     end 
     end 
    end 
    end 
    methods(Static) 
    function out = prop 
     out = @defaultGetPropFcn; 
    end 
    end 
end 

function out = defaultGetPropFcn 
    out = 'defaultGetPropFcn'; 
end 

function out = newGetChild1PropFcn 
    out = 'newGetChild1PropFcn'; 
end 

function out = newGetChild2PropFcn 
    out = 'newGetChild2PropFcn'; 
end 

klass2.m

classdef klass2 < klass 
    properties 
    prop_child_1 = @defaultGetChildPropFcn1 
    prop_child_2 = @defaultGetChildPropFcn2 
    end 
    methods 
    function self = klass2 
     self = [email protected]; 
    end 
    end 
end 

function out = defaultGetChildPropFcn1 
    out = 'defaultGetChildPropFcn1'; 
end 
function out = defaultGetChildPropFcn2 
    out = 'defaultGetChildPropFcn2'; 
end 

wyjściowa:

a = klass2 
b=a.prop_child_1() 


b = 

newGetChild1PropFcn 
+0

Tak, jest to podobne do tego, o czym wspomniałem w ostatnim akapicie (przypadku użycia). Działa to w porządku dla jednej właściwości, ale jest to dużo podstawowa dla każdej właściwości w każdej podklasie. –

+0

@Matt B. Nie możesz napisać metody abstrakcyjnej zdefiniowanej dla każdej podklasy, która zmienia wszystkie zależne właściwości na zdefiniowaną fcnHandle? – Werner

+0

@Matt B. Czy jest to dokładnie to, czego starasz się unikać? – Werner

Powiązane problemy