mercoledì, marzo 26, 2008

OO in Matlab: Single inheritance

Belli guaglioni, ricordate questo post rtelativo alla programmazione OO in Matlab? Bè ogni tanto per sfizio mi diletto a giocherellare con la metaprogrammazione e ho da proporvi un paio di chicche, una (banale) in matlab e una più interessante in VimScript (prossimo post).
Iniziamo pensare alla tecnica proposta per programmare OO in Matlab, abbiamo compreso come ottenere molte features, non abbiamo ottenuto però l'ereditarietà. Per aggiungere l'ereditarietà vogliamo tenere d'occhio i seguenti aspetti:
  • Overriding: vogliamo poter "sovrascrivere" i metodi della superclasse con le nostre nuove versioni senza perdere però la possibilità di accedere esplicitamente alle versioni della superclasse.
  • Accessors: non abbiamo descritto in maniera molto dettagliata come poter realizzare dei metodi accessori al meglio della comodità d'utilizzo, l'idea è quella di ottenere una sorta di property come in C#, per intenderci.
  • Protected access: nell'antico post sulla OOP in Matlab abbiamo descritto in maniera precisa come ottenere accesso pubblico e privato ai metodi, come costruire funzioni private accessorie non legate agli oggetti, come memorizzare gli attributi come privati ma non come pubblici (cosa che non faremo, usando invece i metodi accessori). Resta però da efinire come ottenere un livello protetto di accesso ai metodi o attributi. Si cominci a considerare il fatto che se si ottiene l'accesso protetto ai metodi si può sfruttare quello per avere accesso protetto verso gli attributi semplicemente con degli accessori protetti. Per ora però purtroppo non siamo in grado di creare metodi ad accesso protetto, quindi ci limitiamo ad utilizzare la tecnica delle property.
Dopo tutta sta pizza passiamo al codice: vogliamo creare la classe stepCouter sottoclasse di counter, in essa dovremo sfruttare il valore attuale del contatore per poterlo leggere, modificare e reimpostare; per questo motivo creiamo il seguente metodo accessorio nel contatore:
% The actual value accessor:
function ao = Actual(ai)
% Getting the actual value:
if nargin>0; start=ai; end

% Setting the output:
if nargout>0; ao=start; end
end
Collegheremo questo metodo all'oggetto salvando il function_handler con il nome "actual" all'interno dell'istanza restituita:
% Returning the counter: this is the public methods' set exported.
c = struct('next',@Next,'prev',@Prev, ...
'reset',@Reset,'actual',@Actual);
Fatto questo abbiamo la possibilità, nella sottoclasse, di istanziare il contatore e aggiungervi dei metodi che, sfruttando la property 'actual', possano modificare anche l'istanza della superclasse.
Si noti che la superclasse e la sottoclasse avranno collegati a sé due workspace differenti con all'interno i loro rispettivi attributi privati; questo significa che con questa tecnica comunque ogni classe genera istanza che hanno il proprio workspace separato da tutti gli altri, senza possibilità di interferenze. Ecco il codice:

function c = stepCounter(start,step)
% STEPCOUNTER Generates a new counter that uses a step.
%
% Here the single hineritance is obtained by generating an object of the
% superclass and then joining the method calls, notice that the mantained
% workspaces are distinguished so there are difficulties in the private
% attributes access (there is no protected access, only public or private);
% so an accessor in the class "counter" must be added (in this case
% "actual"), this allows to have the actual attribute to be public.

% =========================================================================
% Creating the object:
% =========================================================================

% Using the superclass' constructor:
c = counter(start);
clear start;

% Adding local methods:
c.step = @Step;
% Saving the old method so that can be explicitly called if required:
c.counter.next = c.next;
c.next = @Next;
% Same for the prev:
c.counter.prev = c.prev;
c.prev = @Prev;

% =========================================================================
% The public methods body.
% =========================================================================
% The next integer function:
function n = Next
% Increment:
n = c.actual(c.actual() + step);
end
% The prev integer function:
function n = Prev
% Decrement:
n = c.actual(c.actual() - step);
end
% Setting and getting the step value:
function so = Step(si)
% Getting the actual value:
if nargin>0; step=si; end

% Setting the output:
if nargout>0; so=step; end
end
% =========================================================================
% Private methods are simple functions and can be defined here.
% =========================================================================
end

% =========================================================================
% Support functions that are not methods (no access to attributes) are
% defined here.
% =========================================================================
Quindi in questa sottoclasse "step" è un attributo locale privato, ed è l'unico presente nel workspace della sottoclase "stepCounter", il parametro "start" del costruttore viene rimosso una volta utilizzato perché dopo la chiamata al costruttore della superclasse non serve più (è stato memorizzato nel workspace della superclasse) e se non eliminato rimarrebbe nel workspace privato della sottoclasse "stepCounter" occupando spazio inutile e potenzialmente dando la possibilità di scrivere codice errato e confuso. Per "step" si è creata una property, i metodi "next" e "prev" sono stati sostituiti e le vecchie versioni sono state salvate nella struttura stessa in una sottostruttura con il nome della superclasse. Ora è possibile scrivere codice come il seguente:
c1 = counter(10);
c2 = stepCounter(10,10);
c1.next()
c2.next() % Uso la versione "overridata":
s=c2.step %ottengo lo step:
c2.step(20); % Imposto lo step:
c2.counter.next(); % Uso la versione originale:

Nessun commento: