2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Posted at

MVCとは

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

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

GUIDEでGUIを作成する

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

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

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

GUIDE

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

MVC_1.png

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

MVC_2.png

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

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

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); % 外部のクラスにコンポーネントへの参照を渡す
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パターンを適用して常にデータの同期を行います。

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内でコールバックを定義
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
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
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やめるってよ

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?