クラス概念を誰も理解していないのは弊社の特殊なアレなのではないかと思うけど、似たような現場があったら悲しいから書いておこう。
次のブログ記事をたいへん参考にしました:
クラスとは
クラスはデータの定義を行う構造体に、そのデータを処理する関数も一緒に埋め込んだものだと考えてください。
クラスを利用することでデータとデータの処理方法をセットにすることが出来ます。そうすればデータに対する間違った処理をコーディングしてしまうリスクを回避できますし、定義したデータをどのように扱えばいいのか読みやすくなります。
クラス定義
Matlabでは次のようにクラス定義が行えます。
(例とはいえ何のためのクラスなのかわからなさすぎるので後で書き換えようかな)
classdef Pointer < handle % handleクラスを継承するPointerクラスの定義
properties (SetAccess='protected',GetAccess='protected') % プロパティ変数の定義ブロックの開始
value % プロパティ変数valueの宣言
end
methods % メソッドの定義ブロックの開始
function obj = Pointer(init_value) % コンストラクタ
obj.value = init_value; % プロパティ変数valueに引数init_valueを設定
end
function value = get(obj) % getメソッドの定義 メソッドは自分で自由に名付けて自由に定義してね
value = obj.value; % 返り値としてプロパティ変数valueを返す
end
function set(obj, new_value) % setメソッドの定義 メソッドは自分で自由に名付けて自由に定義してね
obj.value = new_value; % プロパティ変数valueを引数new_valueに設定
end
end
end
作成したクラスは次のように利用できます。
>> p = Pointer(1); % インスタンス化
>> p.get() % メソッド呼び出し get(p)でも可
ans =
1
>> p.set(2); % メソッド呼び出し set(p,2)でも可
>> p.get()
ans =
2
インスタンスの特徴
handleクラスを継承するクラスからつくられるインスタンスは参照型データになります。
>> p1 = Pointer(1);
>> p2 = p1;
>> p2.set(2);
>> p1.get()
ans =
2
別インスタンスであれば当然別のデータとして扱うことが出来ます。
>> p1 = Pointer(1);
>> p2 = Pointer(1);
>> p1.set(2);
>> p2.set(3);
>> p1.get()
ans =
2
>> p2.get()
ans =
3
インスタンスを値渡しでコピーしたいとき
クローンメソッドを定義しましょう
classdef Pointer < handle
properties (SetAccess='protected',GetAccess='protected')
value
end
methods
function obj = Pointer(init_value)
obj.value = init_value;
end
function value = get(obj)
value = obj.value;
end
function set(obj, new_value)
obj.value = new_value;
end
+ function pointer = clone(obj) % 自分と同じプロパティ変数を持つインスタンスを返すメソッド
+ pointer = Pointer(0);
+ pointer.value = obj.value;
+ end
end
end
>> p1 = Pointer(1);
>> p2 = p1.clone();
>> p2.get()
ans =
1
>> p2.set(2);
>> p1.get()
ans =
1
>> p2.get()
ans =
2
継承
人類には早すぎるから教えない方がいいのかもしれない……
ポリモーフィズム
継承の説明を省いたことによりここの説明が難しくなった
何やってるのか勘で気づいてほしい
CounterクラスとMeterクラスはどちらもshowメソッドを持っているものとする
function Main
c1 = Counter(10);
c2 = Meter(10);
do(c1);
do(c2);
end
function do(c)
c.show();
end
10
**********
一行目の結果はCounterクラスのメソッドの実行結果
二行目の結果はMeterクラスのメソッドの実行結果
つまりdo関数から見れば、入って来た引数に
「おら、仕事しろ」
と命令すれば、各々のクラスが自分の職務を全うしてくれるわけです。
こういうの知らないとdo関数の中でswitchとかif文書いて……っていうのをあちこちの関数で繰り返すことになって大変でしょ
私の前任者たちは数千行ものswitch文とif文のつけたしをあちこちの関数で繰り返していました。それを一人で保守する仕事を今しています。まじでやめてくれ
MATLAB R2011a以降では異種混合配列によって同一のスーパークラスを親に持つ異なるサブクラスのインスタンスを配列に出来ます。
こんな感じで多重継承するか
classdef Counter() < handle & matlab.mixin.Heterogeneous
もっときれいにやるには親クラスをインターフェースとして定義してあげて、親クラスを継承するサブクラスを使ってください。
classdef InterfaceCounter() < handle & matlab.mixin.Heterogeneous
classdef Counter() < InterfaceCounter
classdef Meter() < InterfaceCounter
演算子のオーバーロード
クラスのインスタンスに対する演算子(+や=など)の働き方も自由に定義できます。
classdef String < handle
properties(SetAccess='protected',GetAccess='protected')
args
end
methods
function obj = String(args)
obj.args = args;
end
function ret = plus(obj,s) % plus演算子(+)のオーバーライド
str = [obj.args s.args]; % お互いのプロパティ変数argsを合体
ret = String(str); % 新しいStringオブジェクトを合体結果で初期化して返す
end
function ret = eq(obj,s) % eq演算子(==)のオーバーライド
ret = strcmp(obj.args, s.args); % お互いのプロパティ変数argsを比較
end
function ret = ne(obj,s) % ne演算子(~=)のオーバーライド
ret = ~strcmp(obj.args, s.args); % お互いのプロパティ変数argsを比較
end
function ret = char(obj) % charライブラリのオーバーライド
ret = obj.args; % プロパティ変数argsを返す
end
end
end
>> a = String('Hello');
>> b = String('World');
>> a == b % eq(a,b)と同じ意味
ans =
0
>> char(a + b) % char( plus(a,b) )と同じ意味
ans =
HelloWorld
オーバーロード可能な演算子の一覧
実はクロージャで疑似的にクラスが作成できます
MATLABは1ファイルで1クラスしか定義できないので、たまにこういうことやってるけど、あまりやらんほうがいいのかもね。
function obj = Counter(i)
function count
i = i + 1;
end
obj.count = @count;
function show
disp(i);
end
obj.show = @show;
end