MatlabでCompositePattern

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

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

# フォルダ階層
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579192/b59ecb74-0849-df13-7615-1a6de350b067.png)




# Main GUI
Tree1つとPushButton２つの構成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579192/24976732-191c-8c41-61a4-4c4c714c0843.png)


```Matlab: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
```Matlab: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
```

### 実装
子階層を持てるフォルダーのようなクラス
```Matlab: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
```

子階層を持てないファイルのようなクラス
```Matlab: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
```Matlab:Condition.m
classdef Condition
    enumeration
        Normal
        Alert
    end
end
```

Normal時のIcon (green.png)
![green.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579192/e47ec945-bcb1-1449-6e63-cd92cdf45144.png)

Alert時のIcon (red.png)
![red.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579192/dbb52b91-a852-170b-8907-4b043a04d522.png)


## Data
```Matlab: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
```

動作確認のダミーデータ
```Matlab: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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579192/72052084-bf85-7833-7eda-daf2df5dca75.png)


# 参考
udemyのピーコックアンダーソン氏の講義
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579192/289ae864-646d-6691-82ae-cf330c96f1a2.png)