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

# MVCとは

MVCとはGUIアプリケーションのロジックを
データ部と表示部、そしてユーザー操作部分に分割する方法です。

今回はMVCパターンを適用しない形でアプリケーションを開発したあと、
MVCパターンを適用する形へリファクタリングします。

# GUIDEでGUIを作成する

MATLABではGUIDEを利用してGUIを作成することができます。
注意点としてGUIDEでGUIを作成した場合に自動生成されるMコードでは
コールバックの外に変数を定義することができません。

つまり変数としてデータの状態を保存してコールバック間でデータを共有することが出来ません。
そこでコールバックのみ手動で定義することにします。

まずはGUIDEコマンドでGUIDEを起動します。

```Matlab
GUIDE
```

GUIDEを起動したらブランクGUIからコンポーネントを設置します。

![MVC_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455839/b2fe00df-1f5e-1669-8747-b7392a143890.png)


コンポーネントを設置した後にCallbackとCreateFcnの割り当てを削除してください。
デフォルトでは`%automatic`とコールバックの自動生成が宣言されていますので
記述を空白にしてください。

![MVC_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455839/97ea6c9d-88f1-915c-8745-ea86c7fb915f.png)


自動生成されたMコードから別の関数あるいはクラスへコンポーネントへの参照である変数handleを渡してください。

渡した先のコンストラクタ内でコールバックを手動で定義します。

```Matlab:Test.m
function varargout = Test(varargin)
% End initialization code - DO NOT EDIT


% --- Executes just before Test is made visible.
function Test_OpeningFcn(hObject, eventdata, handles, varargin)
% UIWAIT makes Test wait for user response (see UIRESUME)
% uiwait(handles.figure1);


% --- Outputs from this function are returned to the command line.
function varargout = Test_OutputFcn(hObject, eventdata, handles) 
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
varargout{1} = handles.output;

controller = TestController(handles); % 外部のクラスにコンポーネントへの参照を渡す
```

```Matlab:TestController.m
classdef TestController < handle
    methods
        function obj = TestController(handles)

            count = 0;
            
            set(handles.pushbutton1,'Callback',@obj.pushButton);    % 手動でコールバックを設定

        end
            
        function pushButton(obj,hObject,eventdata)              % コールバックには引数として呼び出し元のコンポーネントへの参照とイベントデータが必要
            count = count + 1;
            set(handles.text1,'String',num2str(count));
        end
    end
end
```

プッシュボタンを押すとテキストが数値に変更され、ボタンを押すたびに数が増加します。

# MVCパターンの適用

MVCパターンを適用することでGUIアプリケーションを

- Model: データ処理を担当するアルゴリズム
- View: 表示を担当するアルゴリズム
- Controller: ユーザーの入力受付を担当するアルゴリズム

に分割します。

処理の責任をきっちり分担することでコードの見通しをよくし、改変に強いアプリケーションを開発することができます。

この際、ModelとViewはObserverパターンを適用して常にデータの同期を行います。

```Matlab:Test.m
function varargout = Test(varargin)
% End initialization code - DO NOT EDIT


% --- Executes just before Test is made visible.
function Test_OpeningFcn(hObject, eventdata, handles, varargin)

% UIWAIT makes Test wait for user response (see UIRESUME)
% uiwait(handles.figure1);


% --- Outputs from this function are returned to the command line.
function varargout = Test_OutputFcn(hObject, eventdata, handles) 
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
varargout{1} = handles.output;

model      = TestModel();                  % Modelを宣言
view       = TestView(handles);            % Viewを宣言
controller = TestController();             % Controllerを宣言

model.addObserver(view);                   % ViewをModelの観測者として登録

controller.setModel(model);                % ControllerにModelを管理してもらう
controller.giveCallback(handles);          % Controller内でコールバックを定義
```

```Matlab:TestModel.m
classdef TestModel < handle
    properties (SetAccess='private',GetAccess='private')
        value
        observers
    end
    methods
        function obj = TestModel
            obj.value = 0;
        end
        function value = getValue(obj)
            value = obj.value;
        end
        function addValue(obj)
            obj.value = obj.value + 1;    % データの更新
            obj.notifyToObservers();      % データの更新を観測者に伝える
        end
        function addObserver(obj,observer)
            obj.observers = [obj.observers {observer}];    % 観測者を登録
        end
    end
    methods (Access='private')
        function notifyToObservers(obj)
            for i = 1 : length(obj.observers)
                obj.observers{i}.update(obj)    % 観測者に自身への参照を渡して表示の更新を依頼
            end
        end
    end
end
```

```Matlab:TestView.m
classdef TestView < handle
    properties (SetAccess='private',GetAccess='private')
        handles
    end
    methods
        function obj = TestView(handles)
            obj.handles = handles;
        end
        function update(obj,model)
            set(obj.handles.text1,'String',num2str( model.getValue() ));    % Modelの更新通知を受け取り表示を更新
        end
    end
end
```

```Matlab:TestController.m
classdef TestController < handle
    properties (SetAccess='private',GetAccess='private')
        model
    end
    methods
        function setModel(obj,model)
            obj.model = model;
        end
        function giveCallback(obj,handles)
            
            set(handles.pushbutton1,'Callback',@obj.pushButton);

        end
            
        function pushButton(obj,hObject,eventdata)
            obj.model.addValue();    % Modelを更新
        end
    end
end
```

より実践的なMVCパターンでは
複数のModel,View,Controllerがお互いにメッセージングを行いアプリケーションを構成します。

# Mathworks、GUIDEやめるってよ

https://jp.mathworks.com/help/matlab/migrate-guide-apps.html