【個人開発】フォルダ構成図を作るWindowsアプリを作った

個人開発で、[**テキスト形式の樹形図を編集する「アスキーツリーメーカー」**](https://github.com/kossyprg/ascii-tree-maker)を作りました。exeファイル単体で機能するWindowsアプリになります。**直感的な操作で樹形図を編集できる** ように工夫しました。**フォルダ構成図** の作成を支援します！

![howToUse.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122187/6942cace-55e4-2c1f-f7aa-550644481cae.gif)


## アプリの使い方

### 1. 樹形図を編集する


ボタン操作で **直感的に樹形図を編集できます。** 

#### 子ノードを追加

選択したノードを親としたとき、子供にあたるノードを追加することができます。

<img width="350" alt="AddChildNode.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122187/8f83c070-3902-30c3-5a9d-33c0e0328689.gif">

#### 兄弟ノードを追加

選択したノードと兄弟関係にあるノードを追加することができます。

<img width="350" alt="AddBrotherNode.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122187/2d91ffaf-25ba-853b-f4e7-3e1a5aa623c1.gif">

#### ノードを移動

選択したノードを上下に移動させることができます。
ノードの移動は、親が等しい、かつ親からの距離が等しいノード間でのみ可能です。

<img width="350" alt="MoveNode.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122187/600aad50-a0ca-cd1a-1079-1a9475d596b7.gif">



#### ノードを削除

選択したノードを削除することができます。
選択したノードに他のノードがぶら下がっていた場合、それらもすべて削除されます。

<img width="350" alt="RemoveNode.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122187/dd91453e-38ab-097b-aec0-d2301eeaeef6.gif">



### 2. テキストベースの樹形図を出力

作成した樹形図はテキストベースの樹形図へ変換でき、すぐにコピペできます。

![ConvertTreeToTextFormatTree.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122187/a9a4a545-a844-ae0c-ce02-8491cd6aee5b.gif)

テキスト形式の樹形図の枝の長さは、ファイルタブの「設定」から調節することができます。

<img width="300" alt="branchLengthSetting.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122187/5b4e1099-a640-3a5d-7050-83fa0f2ff1da.png">

枝の長さを変えてみると、以下のような見栄えになります。

``` dummy:枝の長さ: 5
root
├───── bin
├───── etc
├───── usr
│      └───── local
│             └───── bin
└───── lib
```

``` dummy:枝の長さ: 0
root
├ bin
├ etc
├ usr
│ └ local
│   └ bin
└ lib
```

### 3. ファイル保存、読み込み

作成した樹形図をXMLファイルとして保存しておけば、再度編集することができます。

![LoadFile.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122187/4831cec8-cac9-1f4e-8858-0d2f5503cb9d.gif)

## （随時更新）v1.0.0 以降の追加機能
ここでは v1.0.0 リリース後に追加した機能について随時記述していきます。
この場を借りて Contributor の皆様にお礼申し上げます。

### フォルダ構成から樹形図を生成（v1.1.0 以降）

**フォルダ構成を表す樹形図を自動で生成できるようになりました！**

ファイルタブの **「フォルダから樹形図を生成」** を押下してください。

<img width="350" alt="CreateFolderStructureButton.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122187/a9c33e98-56d3-f035-668f-a482ae4f21b0.png">

起点となるフォルダを選択してください（下図では `root`）。

<img width="350" alt="SelectRootFolder.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122187/d7d75ed5-219f-788a-6466-5fb7a5aa2e0a.png">

OK を押下すると、フォルダ構成を表す樹形図が生成されます。

<img width="400" alt="CreatedTreeView.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122187/a857db35-c1e6-3d06-b6fa-2ac4d66197f8.png">

コピペのボタンを押下すると、テキスト形式のフォルダ構成図が得られます。

``` dummy:作成されたフォルダ構成図
root
├── bin
├── etc
│   ├── Debug
│   │   └── debug.txt
│   └── Release
│       └── release.txt
├── lib
└── usr
    └── local
        └── bin
```

## 開発について

ここでは、本アプリの開発に用いた技術や設計に関することについて記述します。

### 使用技術

- 言語：C#
- 開発環境：Visual Studio Community 2022
- 開発フレームワーク：.NET Framework 4.8.1

樹形図の編集に使えそうな[ **TreeView** ](https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.treeview?view=windowsdesktop-8.0 "TreeView")というツールが用意されていたため、こちらに決定しました。TreeViewのおかげで、樹形図の編集機能やテキスト変換のアルゴリズムは簡便に実装することができました。

### 開発工数と所感

**約100 時間** で作成しました。
.NET Frameworkを用いたWindows Formアプリの製作は学習コストが低めで、私のようなオブジェクト指向初心者にはもってこいだと思いました。加えて、初歩的な質問はChat-GPTがすべて答えてくれるので、特段詰まることなく実装を進めることができました。

経験が少ないこともあり、設計には時間を要しました。そもそも何をクラスにすればいいか、どんなメソッドを持たせればいいか、イベントをどう伝えるかなど、納得のいく設計になるまで時間がかかりました。本アプリを作る中で、技術書の中でも評判の高い[**『良いコード/悪いコードで学ぶ設計入門』（著：仙塲 大也）**](https://amzn.asia/d/2Tso7ey)を拝読し、設計のヒントを得ました。良い例と悪い例が多く記載されているため、リファクタリングを実践しやすく、大変有益な本でした。

今後も設計に関するインプットと実際の開発を通じたアウトプットの機会を設けて、設計スキルに磨きをかけていきたいです。

### クラス図

設計の勉強も兼ねて、クラス図を作成しました。すべてのクラスを1枚の図にまとめると訳が分からないので、機能ごとに分割して記述します。図は [astah](https://astah.change-vision.com/ja/) を用いて作成しました。

**注意事項**
- クラス図を描くのが初めてなもので、色々間違いがあるかと思いますがご了承ください（保険）
- 戻り値は省略しています
- 下線を引いたものは static 属性です


#### 樹形図の編集機能

![UML_NodeEditor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122187/d14f12c2-cb31-4f93-e232-9519e24af2ce.png)

樹形図のノードを編集する機能を NodeEditor クラスに凝集させました。ノードのラベルが空だとクリックしにくくなるなど都合が悪いので、NodeLabel なるクラスを用意し、空の場合は「テキストを入力」が設定されるようにしています。
点線の矢印は **依存** [^1] 関係を表します。


:::note info
「NodeEditor が NodeLabel に依存する」という関係になります。矢印の向きを混乱しがちなのですが、"NodeEditor depends on NodeLabel." のように英語で考えると、主語が始点、目的語が終点になるのでわかりやすいです[^2] [^3] 。
:::



#### テキスト形式の樹形図への変換機能

![UML_TreeConverter.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122187/4fb01307-37d8-9b54-d3f3-f0ac21a062db.png)

テキスト形式の樹形図へ変換する機能は TreeConverter クラスに凝集させました。

変換後の文字列は ConvertTreeToText メソッドからもらいます。引数の textFormatTreeDesigner はテキスト形式の樹形図のデザインを決める役割を果たします。デザインはユーザの設定した枝の長さ TextFormatTreeBranchLength によって決まります。枝の長さを int 型ではなく、TextFormatTreeBranchLength 型として定義することで、範囲外の値が代入されるのを防ぐことができます [^4] 。

#### ファイル関連の機能

![UML_FileManager.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122187/b3c75728-0d9e-2d92-cefe-02ae7858f642.png)

XMLファイルの読み込み、書き出し操作、保存状態の監視などは FileManager クラス[^5]に凝集させました。XMLファイルから樹形図への読み込みは ImportXmlFile クラスが、XMLファイルの書き出しは ExportXmlFile クラス[^6]が行います。

EditingFile クラスは、今編集している樹形図が保存済みなのか否かを管理します。これにより、アプリのウィンドウの上部に表示されている保存状態の表示切り替えが可能になります。

↓ 初期状態から編集中へ
<img width="350" alt="EditingFileState_animation.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2122187/8bb80977-d58d-a880-8637-71997c79e65b.gif">


なお、初期状態は保存先のパスも存在しないため、編集中のファイルが定義されているかを返す IsNotDefined() のようなメソッドも用意しています。

## おわりに

個人開発で、[**テキスト形式の樹形図を編集する「アスキーツリーメーカー」**](https://github.com/kossyprg/ascii-tree-maker)を作りました。直感的な操作で樹形図が作成できるように工夫しました。**フォルダ構成図** を作りたいときなどに、是非とも使ってみて頂けますと大変嬉しいです！

設計については至らぬ点が多々あるかと思います。ご指摘等あれば、記事のコメントなどにいただけますと大変助かります！

ここまでお読み頂きありがとうございました。
もしよければぜひ、いいねやリツイートなど頂けるととても励みになります！！
よろしくお願いいたします！！！

[^1]: [Qiita【新人教育 資料】第3章 UMLまでの道 〜図種類紹介とクラス図の解説編〜](https://qiita.com/devopsCoordinator/items/213e45694dfac0edcfbc)
クラス図の基本についてわかりやすく解説されています。

[^2]: [Qiita【あなたの知らない世界シリーズ】UMLの依存の方向性は、こう覚える](https://qiita.com/azuki8/items/163a6033a4646c644ad1) 
UML図が英語をベースとしているため、S+V+Oで考えると矢印の向きが把握しやすくなることを指摘しています。

[^3]: [IBM Dependency relationships](https://www.ibm.com/docs/en/dmrt/9.5?topic=diagrams-dependency-relationships) 
具体例で "the client uses or depends on the supplier." と書かれています。

[^4]: [p.37 仙塲 大也(2022)『良いコード/悪いコードで学ぶ設計入門』](https://amzn.asia/d/2Tso7ey)
不正状態から防護するようなコンストラクタを「完全コンストラクタ」と呼びます。今回実装したクラス TextFormatTreeBranchLength はコンストラクタで不正値をはじくようになっていますが、Update メソッドで値を更新できるため、いわゆる"完全コンストラクタ"ではありません（それでもTextFormatTreeBranchLengthの値を参照するため、不正状態にはならないはず...）。

[^5]: [p.229 仙塲 大也(2022)『良いコード/悪いコードで学ぶ設計入門』](https://amzn.asia/d/2Tso7ey)
一般に「～Manager」という名前のクラスは広い意味で捉えられる傾向があり、クラスが巨大化しがちなので、あまり良しとされません。ですが、ここでの責務はそこまで多くないので良しとしました。

[^6]: ExportXmlFile、ImportXmlFile クラスは、名前がメソッド名みたいでちょっと微妙ですね...。