MATLAB使いのハード屋のために教えてやるか、"クラス"ってやつをよ……

クラス概念を誰も理解していないのは弊社の特殊なアレなのではないかと思うけど、似たような現場があったら悲しいから書いておこう。

次のブログ記事をたいへん参考にしました：

https://saitodevel01.hatenadiary.org/entry/20101210/1291978535

# クラスとは

クラスはデータの定義を行う構造体に、そのデータを処理する関数も一緒に埋め込んだものだと考えてください。

クラスを利用することでデータとデータの処理方法をセットにすることが出来ます。そうすればデータに対する間違った処理をコーディングしてしまうリスクを回避できますし、定義したデータをどのように扱えばいいのか読みやすくなります。

# クラス定義

Matlabでは次のようにクラス定義が行えます。

(例とはいえ何のためのクラスなのかわからなさすぎるので後で書き換えようかな)

```Matlab:Pointer.m
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
```

作成したクラスは次のように利用できます。

```Matlab
>> p = Pointer(1);     % インスタンス化
>> p.get()             % メソッド呼び出し get(p)でも可

ans =
        1

>> p.set(2);           % メソッド呼び出し set(p,2)でも可
>> p.get()

ans =
        2
```

# インスタンスの特徴

handleクラスを継承するクラスからつくられるインスタンスは参照型データになります。

```Matlab
>> p1 = Pointer(1);
>> p2 = p1;
>> p2.set(2);
>> p1.get()

ans =
        2
```

別インスタンスであれば当然別のデータとして扱うことが出来ます。

```Matlab
>> p1 = Pointer(1);
>> p2 = Pointer(1);
>> p1.set(2);
>> p2.set(3);
>> p1.get()

ans =
        2

>> p2.get()

ans =
        3
```

# インスタンスを値渡しでコピーしたいとき

クローンメソッドを定義しましょう

```diff:Pointer.m
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
```

```Matlab
>> p1 = Pointer(1);
>> p2 = p1.clone();
>> p2.get()

ans =
        1

>> p2.set(2);
>> p1.get()

ans =
        1

>> p2.get()

ans =
        2
```

# 継承

人類には早すぎるから教えない方がいいのかもしれない……

# ポリモーフィズム

継承の説明を省いたことによりここの説明が難しくなった
何やってるのか勘で気づいてほしい

CounterクラスとMeterクラスはどちらもshowメソッドを持っているものとする

```Matlab:Main.m
function Main
    c1 = Counter(10);
    c2 = Meter(10);
    do(c1);
    do(c2);
end

function do(c)
    c.show();
end
```

```Maltab:結果
10
**********
```

一行目の結果はCounterクラスのメソッドの実行結果
二行目の結果はMeterクラスのメソッドの実行結果

つまりdo関数から見れば、入って来た引数に
「おら、仕事しろ」
と命令すれば、各々のクラスが自分の職務を全うしてくれるわけです。

こういうの知らないとdo関数の中でswitchとかif文書いて……っていうのをあちこちの関数で繰り返すことになって大変でしょ

**私の前任者たちは数千行ものswitch文とif文のつけたしをあちこちの関数で繰り返していました。それを一人で保守する仕事を今しています。まじでやめてくれ**

MATLAB R2011a以降では異種混合配列によって同一のスーパークラスを親に持つ異なるサブクラスのインスタンスを配列に出来ます。

[MathWorks:異種混合クラス階層の設計](https://jp.mathworks.com/help/matlab/matlab_oop/heterogeneous-arrays.html)

こんな感じで多重継承するか

```matlab
classdef Counter() < handle & matlab.mixin.Heterogeneous 
```

もっときれいにやるには親クラスをインターフェースとして定義してあげて、親クラスを継承するサブクラスを使ってください。

```matlab
classdef InterfaceCounter() < handle & matlab.mixin.Heterogeneous 
```

```matlab
classdef Counter() < InterfaceCounter
```

```matlab
classdef Meter() < InterfaceCounter
```


# 演算子のオーバーロード

クラスのインスタンスに対する演算子(+や=など)の働き方も自由に定義できます。

```Matlab:String.m
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
```

```Matlab
>> a = String('Hello');
>> b = String('World');
>> a == b                  % eq(a,b)と同じ意味

ans =
        0

>> char(a + b)             % char( plus(a,b) )と同じ意味

ans =
        HelloWorld
```

## オーバーロード可能な演算子の一覧

https://jp.mathworks.com/help/matlab/matlab_oop/implementing-operators-for-your-class.html

# 実はクロージャで疑似的にクラスが作成できます

MATLABは1ファイルで1クラスしか定義できないので、たまにこういうことやってるけど、あまりやらんほうがいいのかもね。

```Matlab:Counter.m
function obj = Counter(i)
    function count
        i = i + 1;
    end
    obj.count = @count;
    function show
        disp(i);
    end
    obj.show = @show;
end
```