LoginSignup
2
0

MatlabでCompositePattern

Last updated at Posted at 2024-03-06

udemy(参考に記載)でC#のCompositePatternを聴講し、普段使っているMatlabのAppDesignerに実装してみた。

仕様

末端のエリアを選択した状態でAlertをクリック→その末端エリアを含む親エリアも警報状態とする。
任意のエリアを選択した状態でReleaseをクリック→子エリアすべての警報を解除する。
末端エリアを選択した状態で警報状態にはできない(一括警報は禁止)。

フォルダ階層

image.png

Main GUI

Tree1つとPushButton2つの構成
image.png

Main.mlapp
properties (Access = private)
    areas_ cell
end

methods (Access = private)
    % TreeにNodeを追加
    function AddNode(app, area, parentNode)
        if exist('parentNode','var')
            node = uitreenode(parentNode, 'Text', area.Name, 'Icon', 'green.png');
        else
            node = uitreenode(app.tree_List, 'Text', area.Name, 'Icon', 'green.png');
        end
        node.NodeData = area;

        children = area.GetChildren();
        for i_node = 1:numel(children)
            app.AddNode(children{i_node}, node);
        end
    end

    % Icon変更の再帰処理
    function SetImage(app)
        nodes = app.tree_List.Children;
        for i_node = 1:numel(nodes)
            app.SetImageMethod(nodes(i_node));
        end
    end
    function SetImageMethod(app, node)
        area = node.NodeData;
        if ~isempty(area)
            if area.GetCondition() == Condition.Alert
                node.Icon = 'red.png';
            else
                node.Icon = 'green.png';
            end
        end

        children = node.Children;
        for i_child = 1:numel(children)
            app.SetImageMethod(children(i_child));
        end
    end
end

% Callbacks that handle component events
methods (Access = private)
    function startupFcn(app)
        addpath('Area');
        addpath('Data');

        movegui(app.UIFigure, 'center');

        entities = LayerFake.GetData();

        for i_entity = 1:numel(entities)
            entity = entities(i_entity);
            switch entity{1}.Kind
                case 1 % BlockArea
                    app.areas_{end+1} = BlockArea(entity);
                case 2 % MeasureArea
                    app.areas_{end+1} = MeasureArea(entity);
            end
        end

        ids = cellfun(@(x) x.Id, app.areas_);
        for i_area = 1:numel(app.areas_)
            area = app.areas_{i_area};
            parentIndx = find(ids==area.ParentId, 1);
            if ~isempty(parentIndx)
                app.areas_{parentIndx}.Add(area);    % 親に追加
            end
        end

        parentIds = cellfun(@(x) x.ParentId, app.areas_);
        rootIndx = find(parentIds==0);
        if isempty(rootIndx)
            warndlg('rootがありません');
            return
        end

        for i_root = rootIndx
            app.AddNode(app.areas_{i_root});
        end

        expand(app.tree_List, 'all');
    end

    % Alertボタンクリック時のコールバック
    function pb_AlertButtonPushed(app, event)
        area = app.tree_List.SelectedNodes.NodeData;
        if isempty(area)
            return
        end
        area.Alert();
        app.SetImage();
    end

    % Releaseボタンクリック時のコールバック
    function pb_ReleaseButtonPushed(app, event)
        area = app.tree_List.SelectedNodes.NodeData;
        if isempty(area)
            return
        end
        area.Release();
        app.SetImage();
    end

    % Close request function: UIFigure
    function UIFigureCloseRequest(app, event)
        rmpath('Area');
        rmpath('Data');
        delete(app)
    end
end

各Class

Composite

Abstract

AreaBase.m
classdef (Abstract) AreaBase < handle
    properties (SetAccess = private, GetAccess = public)
        entity_
        Id int32
        ParentId int32
        Name string
    end
    methods (Access = protected)
        function this = AreaBase(kaisouEntity)
            this.entity_ = kaisouEntity{1};
            this.Id = this.entity_.Id;
            this.ParentId = this.entity_.ParentId;
            this.Name = this.entity_.Name;
        end
    end
    methods (Abstract = true)
        Add
        Alert
        Release
        GetCondition
        GetChildren
    end
end

実装

子階層を持てるフォルダーのようなクラス

BlockArea.m
classdef BlockArea < AreaBase
    properties (SetAccess = private, GetAccess = private)
        areas_ cell
    end
    methods (Access = public, Sealed = true)
        function this = BlockArea(kaisouEntity)
            this = this@AreaBase(kaisouEntity);
        end
        function Add(this, areaBase)
            this.areas_{end+1} = areaBase;
        end
        function Alert(this)
            warndlg('一括警報設定はできません');
        end
        function Release(this)
            for i_area = 1:numel(this.areas_)
                this.areas_{i_area}.Release();
            end
        end
        function out = GetCondition(this)
            for i_area = 1:numel(this.areas_)
                area = this.areas_{i_area};
                if area.GetCondition() == Condition.Alert
                    out = Condition.Alert;
                    return
                end
            end
            out = Condition.Normal;
        end
        function out = GetChildren(this)
            out = this.areas_;
        end
    end
end

子階層を持てないファイルのようなクラス

MeasureArea.m
classdef MeasureArea < AreaBase
    properties (SetAccess = private, GetAccess = private)
        condition_
    end
    methods (Access = public, Sealed = true)
        function this = MeasureArea(kaisouEntity)
            this = this@AreaBase(kaisouEntity);
            this.condition_ = Condition.Normal;
        end
        function Add(this, areaBase)
            msg = sprintf('Addはできません this.Id=%d area.Id=%d', this.Id, areaBase.Id);
            warndlg(msg);
        end
        function Alert(this)
            this.condition_ = Condition.Alert;
        end
        function Release(this)
            this.condition_ = Condition.Normal;
        end
        function out = GetCondition(this)
            out = this.condition_;
        end
        function out = GetChildren(this)
            out = {};
        end
    end
end

状態のEnumeration

Condition.m
classdef Condition
    enumeration
        Normal
        Alert
    end
end

Normal時のIcon (green.png)
green.png

Alert時のIcon (red.png)
red.png

Data

LayerEntity.m
classdef LayerEntity
    properties (SetAccess = private, GetAccess = public)
        Id int64
        ParentId int64
        Kind int8
        Name string
    end
    methods (Access = public)
        function this = LayerEntity(id, parentId, kind, name)
            this.Id = id;
            this.ParentId = parentId;
            this.Kind = kind;
            this.Name = name;
        end
    end
end

動作確認のダミーデータ

LayerFake.m
classdef LayerFake
    properties (SetAccess = private, GetAccess = private)
    end
    
    methods (Access = public, Static = true)
        function out = GetData()
            out = {};
            % =========================
            out{end+1} = LayerEntity(1, 0, 1, "北海道");

            % =========================
            out{end+1} = LayerEntity(2, 0, 1, "東北");

            % =========================
            out{end+1} = LayerEntity(3, 0, 1, "関東");

            out{end+1} = LayerEntity(11, 3, 1, "東京");
            out{end+1} = LayerEntity(12, 11, 2, "立川");
            out{end+1} = LayerEntity(13, 11, 2, "町田");

            out{end+1} = LayerEntity(14, 3, 1, "神奈川");
            out{end+1} = LayerEntity(15, 14, 2, "横浜");

            % =========================
            out{end+1} = LayerEntity(4, 0, 1, "中部");

            % =========================
            out{end+1} = LayerEntity(5, 0, 1, "近畿");

            % =========================
            out{end+1} = LayerEntity(6, 0, 1, "中国");

            % =========================
            out{end+1} = LayerEntity(7, 0, 1, "四国");

            % =========================
            out{end+1} = LayerEntity(8, 0, 1, "九州");

        end
    end
end

起動画面

image.png

参考

udemyのピーコックアンダーソン氏の講義
image.png

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