tag:crieit.net,2005:https://crieit.net/tags/uml/feed 「uml」の記事 - Crieit Crieitでタグ「uml」に投稿された最近の記事 2021-12-08T10:31:53+09:00 https://crieit.net/tags/uml/feed tag:crieit.net,2005:PublicArticle/17788 2021-11-26T19:37:42+09:00 2021-12-08T10:31:53+09:00 https://crieit.net/posts/UML-61a0b8f65e9fb 実践UML記述法:シーケンス図で継承元(親クラス)のメッセージ(メソッド)を利用する際の書き方について <h1 id="TL; DR"><a href="#TL%3B+DR">TL; DR</a></h1> <p><code>クラス名::メソッド名</code> なんてどうでしょう?</p> <h1 id="本題:シーケンス図で継承元(親クラス)のメッセージを利用する"><a href="#%E6%9C%AC%E9%A1%8C%EF%BC%9A%E3%82%B7%E3%83%BC%E3%82%B1%E3%83%B3%E3%82%B9%E5%9B%B3%E3%81%A7%E7%B6%99%E6%89%BF%E5%85%83%EF%BC%88%E8%A6%AA%E3%82%AF%E3%83%A9%E3%82%B9%EF%BC%89%E3%81%AE%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%82%92%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B">本題:シーケンス図で継承元(親クラス)のメッセージを利用する</a></h1> <p>シーケンス図のメッセージは、ライフラインがクラス図のクラスと紐付いている場合、クラスの操作を使うことが一般的かと思います。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuIh9BCb9LNZSjFvnyyh7JJkVpjxyk77TattTN5p5sPbv1Ob5YRaAoJc9nSME9IL5cKcb9QcUoVbvmPbLgGe6N5nW6OMd7IkVJrdneg5LePgh5Yg8C18egA2WhV1iSk-JlNCqD44g479AXWgw2gN5gIbA2ZQwka2k4i8qBeVKl1IW6m40" alt="クラス図の例" /> <img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuIh9BCb9LNZSjFrnyvx7JHiVDsz-tBJpwUpDZnlNFTdNpSLL05INcPnPa9XNeg1afV2qO-NpAIjUDBGgAIGM8tYeoagBKrCKh826hd_Sl19e74WjWiV51TUce6k74BTIU3QvzydUEHgQN0wfUIb0nm40" alt="シーケンス図の例" /></p> <p>2つだけのクラスを持つクラス図とシーケンス図</p> <pre><code class="puml">@startuml title クラス図の例 skinparam classAttributeIconSize 0 class 利用者 { } class クラス { + 操作() } クラス "1 - used" <-- "1" 利用者 @enduml </code></pre> <pre><code class="puml">@startuml title シーケンス図の例 participant ": 利用者" as 利用者 participant "used : クラス" as クラス 利用者 -> クラス: 操作() @enduml </code></pre> <p>しかし、継承元クラス(親クラス)のメッセージを利用する際、クラスとしては持っていない(かのように見える)メソッドを呼出すように書く必要があります。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuIh9BCb9LNY-RUQpplsF6tiC7pSkURfsnjCvAnutpdpSrFsuQVtZvfMFctO-dRtvSUEw9_kwkRYAipFp2XAB4dCLadCIYuiTIqgACfDAIr8za_FpWZEhKXKCkBZ0CWfFErO-dxBYHKEhGZLNBKpmnR9LS5E1uWeAsWhF9lS-sJj7GnEX2Au2eXD5ZqsDhYv20HT2GibGGLUXj3WrHKd1HbSNo5L2k83B8JKl1UXX0000" alt="継承が1つ存在するクラス図の例" /> <img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuIh9BCb9LNY-RUQpplsF6tiC7pSkURfsnjCvAnutpdpSrFsuQVlZvZsFcpO-Rjxyk6ddqzcR7pUkUxAlcukh06X0Pd9cGM9UYOAIbSBJZfNFfwnuqT2gf91OZE2XAYijJarHi59utBJ-SVDAe74WjGWU5nTScuAk7KBSIk7PvDudU-TeQ7BbvPUaAXHbfcUKM2cyMBZYnOePJtPq0Ltqid7guyOMeFkVjom4ChWSKlDIWBu30000" alt="継承が1つ存在するシーケンス図の例" /></p> <p>継承が1つ存在するクラス図とシーケンス図</p> <pre><code class="puml">@startuml title 継承が1つ存在するクラス図の例 skinparam classAttributeIconSize 0 class 利用者 { } class 親クラス { + 操作() } class クラス { } 親クラス <|-- クラス クラス "1 - used" <-- "1" 利用者 @enduml </code></pre> <pre><code class="puml">@startuml title 継承が1つ存在するシーケンス図の例 participant ": 利用者" as 利用者 participant "used : クラス" as クラス 利用者 -> クラス: 操作() note right: 表記上の違いが無い @enduml </code></pre> <p>上記はわかりやすい例ですが、下記のように継承とメッセージが複数存在すると、どのクラスのどのメソッドが使われているのか把握しづらくなります。<br /> 特に、子クラスのメソッドで親クラスのメソッドを呼出すような形は、どれがどのクラスで実装されているのか把握しづらいです。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuIh9BCb9LNY-RUQpplsF6tjUB6b_DdN3qxKpdivPyRXn-kF6PI-MJNknglTPmtAWxBEcLOyRMnuthN_SlF9nqywdipS_RbptP5yt5rTnTcPUGM9HOgv2SavYSR5ZIKbHPb9fIQfdSdvUS6PLge815nTOHk5fnyhdKrQyQ6XLgEQgXGc-N3tZ-T9fppksFLlV2pSUg411GLiXkIWriIHLGnEX-jdilJXL0nMd8Al5gR2q80NDs1VBLg4hIadDIKLLXAxYsUJU9tldG5HWzuLDZQukTYTpmKKnDM1HZKCfYhkvO1o4D48E8Q5agA2hK5gScgAaOAEh2-Gg8UA5oo4rBmMOD000" alt="継承が複数存在して視認性が悪いクラス図の例" /> <img src="http://www.plantuml.com/plantuml/png/VP7BIiD058RtynH3Lxhm1RAGFaWteJDe88uXcUmxCnMr5rnufrKeIcqALgNW1Iy-p6ccyIqyZP2WO3U7dFpVV6SEKusa6yfuooVIapwdyyTpk_y9gfDTxzhZ-_JsBEsFG9s26cR3aspSjOqRJAqUWsg2VWBp1le1p1JqIzgRWfhCFjg412ZrwbxW2aah3attaldHA6liLAWRbTrlB8uuemu5VGScGTs2BJj2YcDqRRtyxr1xs9szzrTMYTYMd8RUngP4Yxe0tG5rcTS9c04O0_erl-ypRb0Je19C2b7-hHTLjMl2jbhsO83SL-ygNxz92lBF_92x3AV_5pFkSycN07KKB0NYWFq0LIuOFjKt" alt="継承が複数存在して視認性が悪いシーケンス図の例" /></p> <p>継承が複数存在して視認性が悪いクラス図とシーケンス図</p> <pre><code class="puml">@startuml title 継承が複数存在して視認性が悪いクラス図の例 skinparam classAttributeIconSize 0 class 利用者 { } class 高祖父母クラス { + create() } class 曽祖父母クラス { + read() } class 祖父母クラス { + update() } class 親クラス { + delete() } class クラス { + 操作() } 高祖父母クラス <|-- 曽祖父母クラス 曽祖父母クラス <|-- 祖父母クラス 祖父母クラス <|-- 親クラス 親クラス <|-- クラス クラス "1 - used" <-- "1" 利用者 @enduml </code></pre> <pre><code class="puml">@startuml title 継承が複数存在して視認性が悪いシーケンス図の例 participant ": 利用者" as 利用者 participant "used : クラス" as クラス 利用者 -> クラス: 操作() note right: それぞれのメッセージが\nどのクラスのものか\n把握が難しい クラス -> クラス: read() クラス -> クラス: delete() クラス -> クラス: create() クラス -> クラス: update() @enduml </code></pre> <p>また、上記のような状態において、UML2系で記法の定義はされていません。</p> <p>現状で最新のUML2.5.1では、メッセージの記法は以下のように定義されています。<br /> <code>request-message-label</code> という部分が今回のターゲットとなるメッセージラベルです。<br /> 中身を確認すると、メソッド名に該当する <code>message-name</code>、引数に該当する <code>input-argument-list</code> の2つしか存在しません。<br /> クラス図の属性や操作には <code>prop-modifier</code> という名で詳細を記述できるため以下様にもできるのですが、シーケンス図のメッセージには上記の様な補足も書けないことになっています。</p> <pre><code class="bnf"><message-label> ::= <request-message-label> | <reply-message-label> | ‘*’ <request-message-label> ::= <message-name> [‘(‘[<input-argument-list>] ’)’] <input-argument-list> ::= <input-argument> [‘,’<input-argument>*] <input-argument> ::= [<in-parameter-name> ‘=’] <value-specification> | ‘-’ ; <reply-message-label> の定義は省略する </code></pre> <blockquote> <p><a target="_blank" rel="nofollow noopener" href="https://www.omg.org/spec/UML/2.5.1/About-UML/">About the Unified Modeling Language Specification Version 2.5.1</a></p> </blockquote> <h1 id="解決策"><a href="#%E8%A7%A3%E6%B1%BA%E7%AD%96">解決策</a></h1> <p>ここで、 <code>message-name</code> の定義を考えてみます。<br /> 定義は以下の通りです。</p> <blockquote> <p>The message-name appearing in a request-message-label is the name property of the Message.<br /> If the Message has a signature, this will be the name of the Operation or Signal referenced by the signature.<br /> Otherwise the name is unconstrained.</p> <p>request-message-labelに表示されるmessage-nameは、そのMessageのnameプロパティです。<br /> Messageにシグネチャがある場合は、そのシグネチャで参照されるOperationやSignalの名前になります。<br /> それ以外の場合、名前は制約を受けません。</p> </blockquote> <p>ここでいう「シグネチャで参照されるOperationやSignal」は、多くはライフラインに紐付くクラスの操作(つまりメソッド)です。</p> <p>操作を記述する際、クラス名の記載の有無についてまで明記はありません。<br /> しかし、UMLの仕様書にも多く書かれている記述として <code>クラス名::操作名</code> という書き方が使える様です。<br /> であれば、メッセージ名としても同様の記述を使えば良いのではないでしょうか?</p> <p><img src="http://www.plantuml.com/plantuml/png/VP5DIiD074VtSugXArruWIoaLmcTMGHY2SratvaWHc8sVfIMPgV6jA9L2XN5wc7-dGzluGIXerAo_3_UczzYkZ7p1omv2X4BM4YnNatKwIVmCtDpegwUvCDObZNm2OYyWTN8p0irl0Dn3bnOgEjSlbNckWIUDOvuWVGTn3EaIn2lShe4_hZvo16Ax4pG2oA_pt37mpARwSlQ38yxs8_TzLTBOagmXq4i89tPvnhNNGXjCtnmwBY7rKMnMPTx-sZB_WlJjRuTwMob5eN3te3wfAKorVoNJ6X86MtHl-x7kqPwTgMrsnKb4Vc9khHFx6zz0m00" alt="継承が複数存在するが視認性をある程度補えるシーケンス図の例" /></p> <p>継承が複数存在するが視認性をある程度補えるシーケンス図</p> <pre><code class="puml">@startuml title 継承が複数存在するが視認性をある程度補えるシーケンス図の例 participant ": 利用者" as 利用者 participant "used : クラス" as クラス 利用者 -> クラス: 操作() クラス -> クラス: 曽祖父母クラス::read() クラス -> クラス: 親クラス::delete() クラス -> クラス: 高祖父母クラス::create() クラス -> クラス: 祖父母クラス::update() @enduml </code></pre> <h1 id="結論"><a href="#%E7%B5%90%E8%AB%96">結論</a></h1> <p>クラス図におけるクラスの属性や操作の補足的記法をベースに考えることが多いため、シーケンス図でも同様の書き方ができないかを調べた挙句、シンプルな記法を思いつきました。<br /> 遠回りした気もしますが、お蔭でさまざまな定義などの再学習もできたため、結果的には良かったと思います。<br /> 仕様書、万歳!</p> Morichan tag:crieit.net,2005:PublicArticle/16108 2020-10-04T23:28:17+09:00 2021-11-29T13:41:31+09:00 https://crieit.net/posts/UML-5f79dc014a829 実践UML記述法:ユースケース図におけるユースケース間の関係の書き方を疑似コードから逆変換して考える <p>本記事は、 Qrunch からの移転記事です。</p> <p>移転元: <a target="_blank" rel="nofollow noopener" href="https://morichan.qrunch.io/entries/XRfxn2KmER0Skcvp">実践UML記述法:ユースケース図におけるユースケース間の関係の書き方を疑似コードから逆変換して考える - 雑木林</a></p> <h1 id="TL; DR"><a href="#TL%3B+DR">TL; DR</a></h1> <p>ユースケース図の <code>include</code> および <code>extends</code> は、それぞれメソッドおよび <code>if</code> 文が真の場合のメソッドに変換できます。<br /> また、拡張点は <code>if</code> 文の真偽値判定に変換できます。</p> <h1 id="本題:ユースケース図におけるユースケース間の関係の書き方を疑似コードから逆変換して考える"><a href="#%E6%9C%AC%E9%A1%8C%EF%BC%9A%E3%83%A6%E3%83%BC%E3%82%B9%E3%82%B1%E3%83%BC%E3%82%B9%E5%9B%B3%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E3%83%A6%E3%83%BC%E3%82%B9%E3%82%B1%E3%83%BC%E3%82%B9%E9%96%93%E3%81%AE%E9%96%A2%E4%BF%82%E3%81%AE%E6%9B%B8%E3%81%8D%E6%96%B9%E3%82%92%E7%96%91%E4%BC%BC%E3%82%B3%E3%83%BC%E3%83%89%E3%81%8B%E3%82%89%E9%80%86%E5%A4%89%E6%8F%9B%E3%81%97%E3%81%A6%E8%80%83%E3%81%88%E3%82%8B">本題:ユースケース図におけるユースケース間の関係の書き方を疑似コードから逆変換して考える</a></h1> <p>UMLのダイアグラムの1つに、ユースケース図というものがあります。<br /> ユースケース図とは、ステークホルダーとシステムの関係を、簡易的に図として表現するものです。</p> <p>以下のような図を、人生で1度は見たことがあるはずです。</p> <p><img src="http://www.plantuml.com/plantuml/png/ZPBVIiCm5CRlynIXLtgnFe2CiZSPfOqrq3KboLGGmIHEb0r47KFnGl3FS0n46IB-UPjZSNiBapbRsPKtXt3ExtmVvoCD8KCkswqKIIfJWek3bsd_kNWR5uUZh_xzTAy3wW7C4Cm7w5VGJp-FudA2K4hM9POCSvfiI1nJJY99MOPG64d6iLTAU3WKk2q8HyWnOPRyTS8x2Bjf50g21P5DAadBRNYpcwlYJ84-BJfte7kWkvQirMgsacr9Cc7TSCve9eLjBMWr1ZC2Oy3qmKn0FuBgVRwVWze7DNHLTprBhrJYnmFRMtMEoSBTpS4SWRwsyzcW2-ha5s4voPenOzmnoqvMx7O0wc9ESj9YcwIvnSCyfrc2S7MSxmUL45x3zJhDehGTao2mMF8_h5Ih-_wosHoUPnHRTXSXbxwDBYYtgavvTCFIzYTy0m00" alt="冷蔵庫利用者のユースケース図" /></p> <pre><code class="plantuml">@startuml title 冷蔵庫利用者のユースケース図 left to right direction actor "利用者" as user rectangle 冷蔵庫 { usecase set_item as "食品を入れる --- extension points 食品にラッピングが付いている" (食品を取る) as get_item (ドアを開ける) as open_door (ラッピングを外す) as remove_wrapping set_item ..> open_door : <<include>> get_item ..> open_door : <<include>> set_item <.. remove_wrapping : <<extend>> } user --> set_item user --> get_item @enduml </code></pre> <h1 id="問題点"><a href="#%E5%95%8F%E9%A1%8C%E7%82%B9">問題点</a></h1> <p>ここで、よく混乱するのが <code>include</code> および <code>extend</code> の意味、および、矢印の向きです。</p> <p><code>include</code> は、あるユースケースは別のユースケースを包含している、という意味を表します。</p> <blockquote> <p>ユースケースBの中には、ユースケースAが含まれていることを示します。<br /> 「has a」関係、つまり、「ユースケースB has a ユースケースA」の関係が成り立ちます。</p> </blockquote> <p><code>extend</code> は、あるユースケースが別のユールケースを拡張する、という意味を表します。</p> <blockquote> <p>ユースケースAに対して、機能を追加するようなユースケースBの関係を示します。</p> </blockquote> <p><a target="_blank" rel="nofollow noopener" href="http://www.itsenka.com/contents/development/uml/usecase.html">ユースケース図(Use Case Diagram) - UML入門 - IT専科</a></p> <p>この包含と拡張、という単語の違いは、個人的には非常に理解が難しかったです。<br /> それにより、以下の疑問が顕在化します。</p> <ul> <li>どのように <code>include</code> と <code>extend</code> を使い分けるのか</li> <li>なぜ <code>extend</code> の矢印の向きは <code>include</code> と逆なのか</li> </ul> <p>しかし、下記ページにより、長年の疑問が解けたので、解説しようと思います。</p> <p><a target="_blank" rel="nofollow noopener" href="http://architect360.apricot-jp.com/400/includeextend.html">ユースケース図のincludeとextend:アーキテクト360</a></p> <h1 id="解決策"><a href="#%E8%A7%A3%E6%B1%BA%E7%AD%96">解決策</a></h1> <p>エンジニアは、図で理解するより、ソースコードで理解する方が簡単ですよね。<br /> ということで、上記ユースケース図の例を、疑似コードに置換えてみます。</p> <p>冷蔵庫利用者のユースケース疑似コード</p> <pre><code class="py"># TODO: 例として挙げたユースケース図からは、ユースケースの実行順序までは読取れない def ドアを開ける: # ドアを開ける際の処理記述 def ラッピングを外す: # ラッピングを外す際の処理記述 def 食品を入れる: if 食品にラッピングが付いている: ラッピングを外す() ドアを開ける() # 食品を入れる際の処理記述 def 食品を取る: ドアを開ける() # 食品を取る際の処理記述 </code></pre> <p>各ユースケースは、各メソッドと対応します。</p> <p><code>include</code> の関係先ユースケース(例における「ドアを開ける」ユースケース)は、関係元ユースケース(例における「食品を入れる」ユースケースおよび「食品を取る」ユースケース)の共通部品として、関数内で呼出すメソッドとして対応します。</p> <p><code>extend</code> の関係元ユースケース(例における「ラッピングを外す」ユースケース)は、関係先ユースケース(例における「食品を入れる」ユースケース)を一部拡張するため、拡張点「食品にラッピングが付いている」場合にのみ呼出すメソッドとして対応します。</p> <p>ここで注意ですが、例として挙げたユースケース図からは、ユースケースの実行順序までは読取れないことに気をつけてください。<br /> 例えば、「食品を入れる」ユースケースで実行する、「ドアを開ける」ユースケース/「ラッピングを外す」ユースケース/「食品を入れる」ユースケースの3つ(3番目は、ユースケース自身のユースケースを指す)は、本来は前後関係がありますが、ユースケース図からは読取れません。<br /> 「ドアを開ける」前に「食品を入れる」ことはできませんし、「ラッピングを外す」前に「食品を入れる」ことは、本来はできないので、疑似コードでは順序を指定してます(ドアを開けた後にラッピングを外すことはできますが、電気代が掛かるので、先にラッピングを外しています)。</p> <p>表でまとめると、下記の通りです。</p> <div class="table-responsive"><table> <thead> <tr> <th>ユースケース図</th> <th>疑似ソースコード</th> </tr> </thead> <tbody> <tr> <td>ユースケース</td> <td>メソッド</td> </tr> <tr> <td><code>include</code></td> <td>関係元ユースケースの内部で実行するユースケース</td> </tr> <tr> <td><code>extend</code></td> <td>関係先ユースケースの内部で、拡張点が真の場合に実行するユースケース</td> </tr> <tr> <td>拡張点</td> <td><code>extend</code> の関係元ユースケースを実行する場合は真を表す真偽値</td> </tr> </tbody> </table></div> <p>こう見ると、最初の疑問点がはっきりすると思います。</p> <h2>どのように <code>include</code> と <code>extend</code> を使い分けるのか</h2> <p><code>include</code> の関係先ユースケースは、複数のユースケースから呼出されることを想定し、共通部品化すると良いです。</p> <p><code>extend</code> の関係元ユースケースは、まず関係先ユースケースを共通部品化し、ある分岐点(拡張点)でのみ実行するユースケースとして外出しすると良いです。</p> <h2>なぜ <code>extend</code> の矢印の向きは <code>include</code> と逆なのか</h2> <p>ここは、私の個人的な見解を述べます。</p> <p><code>extend</code> が <code>include</code> と逆向きなのは、 <code>extend</code> の関係元ユースケースが拡張点を求めていることが理由と考えます。<br /> すなわち、 <code>extend</code> の関係元ユースケースは、関係先ユースケースにおける拡張点に「依存」していると言えるのではないか、と考えます。</p> <p>疑似コードを見ればわかりますが、 <code>extend</code> の関係先ユースケースに拡張点が存在しないユースケース図は、どのような場合に関係元ユースケースが実行されるのか、判断できかねます。<br /> つまり、 <code>extend</code> の関係元ユースケースと、関係先ユースケースの拡張点は、セットであると考えるのが自然です。<br /> さらに言えば、 <code>extend</code> の関係元ユースケースは、関係先ユースケースの拡張点なしには関係を結ぶことは難しく、拡張点に依存して存在を維持している関係である、と言うこともできます。</p> <h1 id="結論"><a href="#%E7%B5%90%E8%AB%96">結論</a></h1> <p>ユースケース図の関係は、疑似コードから逆変換して考えてみると、理解しやすい気がします。<br /> UMLは高度に抽象的であるため、具象的な疑似コードに置換えることで、把握しやすいのかもしれません。</p> <p>これを元に、ユースケース図がもっと流行ることを期待します。</p> Morichan tag:crieit.net,2005:PublicArticle/16098 2020-10-04T23:17:50+09:00 2021-11-29T13:41:56+09:00 https://crieit.net/posts/UML-Python-Perl-PHP-UML-1 実践UML記述法:スクリプト言語(Python, Perl, PHPなどなど)における可視性をUMLで記述する1考察 <p>本記事は、 Qrunch からの移転記事です。</p> <p>移転元: <a target="_blank" rel="nofollow noopener" href="https://morichan.qrunch.io/entries/Ry4qc9vnjI1JFXnB">実践UML記述法:スクリプト言語(Python, Perl, PHPなどなど)における可視性をUMLで記述する1考察 - 雑木林</a></p> <h1 id="TL; DR"><a href="#TL%3B+DR">TL; DR</a></h1> <p>スクリプト言語における可視性をUMLで記述する場合、リバースエンジニアリングによる結果論的可視性を書くといいかもしれない</p> <h1 id="本題:スクリプト言語における可視性をUMLで記述する"><a href="#%E6%9C%AC%E9%A1%8C%EF%BC%9A%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E8%A8%80%E8%AA%9E%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E5%8F%AF%E8%A6%96%E6%80%A7%E3%82%92UML%E3%81%A7%E8%A8%98%E8%BF%B0%E3%81%99%E3%82%8B">本題:スクリプト言語における可視性をUMLで記述する</a></h1> <p>UMLには可視性という概念があります。</p> <p>Javaにおけるアクセス修飾子とほぼ同等のもの、と考えておけば間違いではありません。<br /> <code>public</code>、<code>private</code>、<code>protected</code>、および<code>package</code>です。</p> <p>スクリプト言語(ここではPython、Perl、PHPといったいわゆるP言語を対象としておきます)には、絶対的な可視性はありません。<br /> 例えばPythonであれば、変数の前にアンダースコアを付けることで「疑似的に」プライベート変数を設定できますが、実際に外部から参照できないわけではありません。</p> <h2 id="問題点"><a href="#%E5%95%8F%E9%A1%8C%E7%82%B9">問題点</a></h2> <p>スクリプト言語で開発する場合、UMLにおける可視性の情報は無意味です。<br /> それどころか、余計な情報として「ノイズ」と言われても仕方ありません。</p> <p>UMLには可視性を書かなくても構文的に問題ないため、可視性の情報無しでも大丈夫です。<br /> しかし、それでは次のような問題が起こりえます。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuKhEIImkLWWjJYrIgEPI08A2eioyaf3KYX8DJQvQBgYyPrvQVb5kOabcVXvKMGbG1PYHcrfSd9YU2b8BDaLNrqv1oJ0cBnEeHGbP8vT3QbuAq4e0" alt="UserクラスがInformationクラスと関連しているクラス図" /></p> <p>UserクラスがInformationクラスと関連しているクラス図</p> <pre><code class="plantuml">@startuml class User { printText() } class Information { text message } User --> "1\ninfo" Information @enduml </code></pre> <p>さて、上記のクラス図から「UserクラスはInformationクラスのtext変数を参照しているが、message変数は参照していない」という情報を読み取れるでしょうか。</p> <p>UserクラスがInformationクラスを持っているPythonコード</p> <pre><code class="py">class User: info = Information() # クラス図におけるprintText()メソッドと対応 def print_text(self): print ("{}".format(info.text)) class Information: text = "This is printed" message = "Oh no!" </code></pre> <p>お分かりの通り、クラス図からでは理解できません。<br /> しかし、クラス図から変数の利用状況が理解できなければ、結局コードを読まなければならず、UMLの浸透には貢献できません。</p> <p>余談ですが、クラス図では<code>printText</code>とキャメルケースにしているところ、Pythonコードでは<code>print_text</code>とスネークケースにしています。<br /> これは、UMLがキャメルケース、Pythonがスネークケースを推奨しているためです。</p> <h1 id="解決策"><a href="#%E8%A7%A3%E6%B1%BA%E7%AD%96">解決策</a></h1> <p>発想を逆転します。<br /> 設計時に可視性を決めるのではなく、コード上で利用しているか否かを判断し、それをUMLに反映してはどうでしょうか?</p> <p><img src="http://www.plantuml.com/plantuml/png/JSun2i9G383XFQS8dHHve1UGEdPMDvTOemJxqfAa88ftzragc3NV7r9xaCMOE_2xw4166TkEt7SH9kSnk6bxtSkJSGqmRV3eRFW2B9DmCD4uy2CMItZ_HAFNtZA5z3h35KOnSvFPdMxyzTEjbBPORKwQK4fO_UTGirxAUny0" alt="UserクラスがInformationクラスと関連しておりmessage変数は利用していないクラス図" /></p> <p>UserクラスがInformationクラスと関連しておりmessage変数は利用していないクラス図</p> <pre><code class="plantuml">@startuml class User { - printText() } class Information { + text - message } User --> "1\n- info" Information @enduml </code></pre> <p>上記のクラス図から、「少なくとも」Informationクラスのmessage変数を外部クラスは参照していないことがわかります。<br /> また、他の変数(info)やメソッド(<code>printText()</code>)についても、外部からの参照はないことがわかります。</p> <p>上記のクラス図を見ただけで、Informationクラスはtext変数にのみ注意すればよく、message変数はInformationクラス内部を見れば把握できることがわかります。</p> <h1 id="結論"><a href="#%E7%B5%90%E8%AB%96">結論</a></h1> <p>どうでしょう。<br /> あくまで視点を変えただけですから、「言われなくてもその通りじゃん」と言われればごもっともです。<br /> UMLを設計ツールとして可視性を考えるのではなく、UMLをあくまで可視化ツールとして利用するという考え方であれば、UMLを利用しやすくなるのではないでしょうか。</p> Morichan tag:crieit.net,2005:PublicArticle/16095 2020-10-04T23:14:35+09:00 2021-11-29T15:49:26+09:00 https://crieit.net/posts/UML 実践UML記述法:UMLのクラス図における関係の考察 <p>本記事は、 Qrunch からの移転記事です。</p> <p>移転元: <a target="_blank" rel="nofollow noopener" href="https://morichan.qrunch.io/entries/3n3Rsvp6njvRJP7l">UMLのクラス図における関係の考察 - 雑木林</a></p> <h1 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h1> <p>UMLのクラス図には、ノードのようなもの(クラスやインタフェースなど)とエッジのようなもの(関連や依存など)を記述できます。</p> <p>さて、クラスやインタフェースをプログラミング言語に落とし込むには、あまり苦も無く変換できると思います。<br /> 私もクラス図とPerlの変換について、記事を書いたりしていました。</p> <blockquote> <p>クラス図とPerlの対応付け: <a target="_blank" rel="nofollow noopener" href="https://qiita.com/Morichan/items/cfa83fee6d451847ef85">https://qiita.com/Morichan/items/cfa83fee6d451847ef85</a></p> </blockquote> <p>しかし、エッジのようなもの(以降、関係と呼びます)は、汎化関係以外を書くのは難しいのが現状です。<br /> C++への変換において、「<a target="_blank" rel="nofollow noopener" href="https://www.researchgate.net/publication/220610417_Recovering_UML_class_models_from_C_A_detailed_explanation">Recovering UML class models from C++: A detailed explanation</a>」では、関係をC++に対応付けするためには意味解析 (semancit analysis) をする必要がある、と書かれています。</p> <p>ここでいう意味解析という意味ですが、恐らくは構文解析だけでは瞬時に変換できず、構文解析結果を元にメモ化テーブルなどを作り、作成したメモ化テーブルから総合的に判断する必要がある、という意味だと私は捉えました。<br /> 例えば、1つのクラス内に同じ名前のメンバ変数(Javaであればフィールドと呼びます)が複数存在する場合、コンパイルエラーとなりますよね。<br /> 本来であれば避けるべきですが、構文解析だけでは判断できません。<br /> 判断するためには、構文解析によってフィールドのリストをメモ化テーブルに入れておき、同じメンバ変数をメモ化テーブルから確認しなければなりません。<br /> 他にも連想配列を使うとか、いろいろ方法はあると思いますが、どちらにせよ構文解析「だけ」では無理だということです。<br /> これを「意味解析をする必要がある」という意味で使っていると考えます。</p> <p>さて、上記の論文において「意味解析をする必要がある」という意味が、私が捉えた意味だとするならば、構文解析を元に関係を変換できるのではないか、と考えることができます。<br /> 以降は、私が考えたサイキョーの変換ですので、深く考えずに読んでいただければと思います。</p> <h1 id="関係の種類と対応"><a href="#%E9%96%A2%E4%BF%82%E3%81%AE%E7%A8%AE%E9%A1%9E%E3%81%A8%E5%AF%BE%E5%BF%9C">関係の種類と対応</a></h1> <p>関係には、以下の6つがあります。</p> <ul> <li>実現</li> <li>汎化</li> <li>コンポジション</li> <li>集約</li> <li>関連</li> <li>依存</li> </ul> <p>上部の関係が最も強く、下がるにつれて関係が弱くなります。</p> <p>余談ですが、「関係」と言えば上記6つの関係を総称したものとして取り、「関連」と言えば関係の中の1種類のものとして取ります。<br /> よく勘違いしやすいので気を付けましょう。</p> <p>以降、各関係をどう捉えるか、Javaを例にして考えてみます。</p> <h2 id="実現"><a href="#%E5%AE%9F%E7%8F%BE">実現</a></h2> <p>いわゆるインタフェースを実装しているクラスの関係です。<br /> クラス図では、下図のように記述します。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuShCAqajIajCJbLutBZrSLC_Z9p-k77-uwQcnuth5AgvQhcuadCIYuiLdgwR_s9nAufiPcv1JcfkQbv9CNuJ75BpKe0M0W00" alt="実現関係のクラス図" /></p> <p>Javaにおいてはinterfaceとimplementの関係を書けばよいです。</p> <pre><code class="java">interface ふわふわしたもの { // ... } class 実装したもの implements ふわふわしたもの { // ... } </code></pre> <p>簡単ですね。</p> <h2 id="汎化"><a href="#%E6%B1%8E%E5%8C%96">汎化</a></h2> <p>いわゆる継承クラスの関係です。<br /> クラス図では、下図のように記述します。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuKfCAYufIamkKNZMjVV5neNFPxKyRbprkAdfSUCwBamX1PiQNLs5fwtRqPL2X32LFcxgUDpKXKM3GsfU2j1-0000" alt="汎化関係のクラス図" /></p> <p>クラス図では、抽象クラスとクラスの汎化関係、およびクラスとクラスの汎化関係の間に差異はありません。</p> <p>Javaにおいてはextendsの関係を書けばよいです。</p> <pre><code class="java">abstract class 抽象的なもの { // ... } class 具象的なもの extends 抽象的なもの { // ... } class より具象的なもの extends 具象的なもの { // ... } </code></pre> <p>こちらも簡単ですね。</p> <p>つまり、実現と汎化については、構文解析レベルで判断可能です。<br /> ですから、これ以降について考えてみると面白いですよ。<br /> 以降は答えがありませんので、各自考えてみてください。</p> <h2 id="コンポジション"><a href="#%E3%82%B3%E3%83%B3%E3%83%9D%E3%82%B8%E3%82%B7%E3%83%A7%E3%83%B3">コンポジション</a></h2> <p>is-part-of関係と呼ばれる関係です。<br /> クラス図では、下図のように記述します。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuKhEIImkLb1wsRpYQKVRyrajZnjNFMvgUjouJaaXiLZ1EICp9uKBfkXfwuedUoTZA0f8B4hDA-7Y0YjMKD9LK7ZP35JOwwYaOAEhgmMJTqZDIm7Q2000" alt="コンポジション関係のクラス図" /></p> <p>ここで、関連端名として「部位A」という文字と、その左端に可視性の「-」 (private) が登場しました。<br /> Javaで記述する際に利用するので、併せて覚えてください。</p> <p>よく勘違いされる方がいますが、黒塗り菱形は持っている側に付きます。<br /> 持たれる側に付くわけではありません、少しややこしいですよね。<br /> また、持たれる側の矢印はオプションです、あってもなくてもあまり意味はありません。</p> <p>さて、Javaでどう書くかというと、私は「そのクラスだけがインスタンスを持っている」と捉えます。</p> <pre><code class="java">class 部分的なもの { // ... } class 全体的なもの { private 部分的なもの 部位A = new 部分的なもの(); } </code></pre> <p>new演算子以降については、あっても無くても問題ありません。<br /> あるいは、コンストラクタ内に記述するというのも手でしょう。</p> <pre><code class="java">class 部分的なもの { // ... } class 全体的なもの { private 部分的なもの 部位A; 全体的なもの() { 部位A = new 部分的なもの(); } } </code></pre> <p>さて、これだけだと「ふーん、そんなものか」だと思われると思いますが、次の集約を見れば違いがはっきりすると思います。</p> <h2 id="集約"><a href="#%E9%9B%86%E7%B4%84">集約</a></h2> <p>own-a関係と呼ばれる関係です。<br /> クラス図では、下図のように記述します。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuKhEIImkLb1wrjFuk77-uwQUnutB7ZUsF6xgVjpKzBXndP92Oh62SqPcJWeNYjB5sDG591QbvfLmSO7LAoZfAZphR02gVZgvqL59mKPNDn-cmY4rBmLeEG00" alt="集約関係のクラス図" /></p> <p>さて、コンポジションとの違いは、黒塗り菱形を白抜き菱形に変更しただけです。<br /> この違いですが、is-part-of関係(つまり「全体的なもの」は「部分的なもの」の一部である関係)とown-a関係(つまり「持っているもの」は「持たれているもの」を所有している)という言葉の違いである、と考えました。</p> <p>すなわち、Javaでどう書くかというと、私は「そのクラス以外にもインスタンスを(参照として)保持している」と捉えます。</p> <pre><code class="java">class 持たれているもの { // ... } class 持っているもの { private 持たれているもの 所持品X; 持っているもの() { 所持品X = new 持たれているもの(); } } </code></pre> <p>違い無しじゃないか!<br /> と思われたあなた、正解です。<br /> 上記のクラス図では違いはありません。<br /> しかし、下図でははっきり違いが見えると思います。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuNhMq_YuSVxZffx7ZSiUDxOyRkf-tDJqk76TWfAkmhFE1g3qqycD4KeADhgw-GfWnOlHLE8n8YOFoP0eg27FroryFg7jSUDqDEN4EIKIQWfOgitmSUCIe0eyxbgCQHZK1K_NxtknSI74QrguNOLk4M4Z8maPwP2Qbm8CUm00" alt="複数の集約関係のクラス図" /></p> <pre><code class="java">class 持たれているもの { // ... } class 持っているもの { private 持たれているもの 所持品X; 持っているもの(持たれているもの 所持品) { 所持品X = 所持品; } } class もう1つの持っているもの { private 持たれているもの 所持品Y; もう1つの持っているもの(持たれているもの 所持品) { 所持品X = 所持品; } } class 管理しているもの { private 持っているもの 管理対象X; private もう1つの持っているもの 管理対象Y; private 持たれているもの 所持品Z = new 持たれているもの(); 管理しているもの() { 管理対象X = new 持っているもの(所持品Z); 管理対象Y = new もう1つの持っているもの(所持品Z); } } </code></pre> <p>「管理しているもの」クラスのコンストラクタによって、「持っているもの」クラスのコンストラクタと「もう1つの持っているもの」クラスのコンストラクタに、所持品Zインスタンスを代入しています。<br /> Javaではインスタンスは基本的に参照渡しのため、「持っているもの」クラスの所持品Xと「もう1つの持っているもの」クラスの所持品Yと「管理しているもの」クラスの所持品Zは全て同じインスタンスを参照していることになります。</p> <p>これが、私の考える集約です。</p> <h2 id="関連"><a href="#%E9%96%A2%E9%80%A3">関連</a></h2> <p>has-a関係と呼ばれる関係です。<br /> クラス図では、下図のように記述します。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuNenOU_ZneMF6vSzRcnvtDJzkAdfSUCw1ILTXUUTAv_idHoWf62Zgwlmj7yQN9BB8JKl1QWL0000" alt="関連関係のクラス図" /></p> <p>実は、Javaにおいて一番ややこしい関係は、この関連だと考えます。<br /> すなわち、Javaでどう書くかというと、私は「実現、汎化、コンポジション、集約、依存のいずれにもあたらないが、依存より強い関係を持つ」と捉えます。</p> <pre><code class="java">class 落ちているもの { // ... } class 取るもの { void hoge() { 落ちているもの 物体P = new 落ちているもの(); } } </code></pre> <p>物体Pはhoge()メソッド内でのみ生存できます。<br /> つまり、「取るもの」クラスの属性にはあたりません。</p> <p>基本的に、関連端名を書ける関係は、インライン属性として同じように記述できます。<br /> コンポジションのクラス図における「全体的なもの」クラスと同義のインライン属性を持つクラスの図を、下図に示します。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuIhEpimhI2nAp5L8paaiBdOiAIdAJ2ejIVLCpiyBpgnALJ3WKeT8AIYzR5xnjAFj-QoMnush7ZSrFMvSfwIGMApWd96PayB51LNqj7N5axsJCHG591QbvfKeL7Cf084k4Bzi1ghhTLImKi0qbgkMoo4rBmNeFW00" alt="コンポジションのクラス図における「全体的なもの」クラスと同義のインライン属性を持つクラス" /></p> <p>これは逆に言えば、「全体的なもの」クラスの内部において、部位A属性をどの操作からでも参照できることを意味します。</p> <p>しかし、関連における「取るもの」クラスの持つ物体P属性は、特定の操作内(メソッド内)でのみ生存するため、「取るもの」クラス内の別の操作が利用することはできません。<br /> そのため、関連における関連端名をインライン属性には変換できません。</p> <p>UMLの仕様ではどうだったか定かではありませんが、確か全ての関係はインライン属性に変換可能だったような気がします [要検証] 。<br /> そのため、もしかしたらUMLの仕様とバッティングする恐れがあるので、この辺りは詳しく調査する必要がありそうです。</p> <h2 id="依存"><a href="#%E4%BE%9D%E5%AD%98">依存</a></h2> <p>とても範囲の広い関係です。<br /> クラス図では、下図のように記述します。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuIhEpimhI2nAp5L8paaiBdOiAIdAJ2ejIVLCpiyBpgnALJ3W0aieA3tPD_VfsXcF6wS-RkfvtBZiSMFBuwRknqrDZnlNAIaa5YiuPsJcf72XgvwEJMb0Ob5gNeb2DLSj5nTOcuADdXvKbCiXDIy5w380" alt="依存関係のクラス図" /></p> <p>依存と聞くと、上記までの5つの関係全ても依存に書換えられる気がします。<br /> しかし、UML2.5.1の仕様では、依存 (Dependency) は直接関係 (DirectedRelationship) と汎化関係であり、関連 (Association) と汎化 (Extension) とは異なるものとして定義されています。<br /> 実現 (InterfaceRealization) は依存と汎化関係であるため、実現については依存に書換えようと思えばできる関係と言えるでしょう。<br /> ちなみに、集約 (Aggregation) とコンポジション (Composition) は、属性 (Property) の種類を表す列挙子 (AggregationKind) として設定できるみたいで、こちらも全く異なります。</p> <p><img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuKfCAYufIamkKGXApKaioSpFAyx8B428kcGMfIQNfAQWo78XynHiQdHriQl3LEPON9oVd0aMv89hmJGijI0rBoKrBxcI2y646W1n6DW093P4qZEl9BKeBJ4vDIWrCIUp2c8Dr4g4AjUAajIyOf2K0z1eO1W00000" alt="UML2.5.1の仕様における関係の定義" /></p> <p>UML2.5.1の仕様では確かに書換えられないようですが、実際問題として「依存」という名前だけではよくわかりません。<br /> ではUML2.5.1の仕様を読めば解決するのかというと、そうでもなさそうです。</p> <blockquote> <p>A Dependency is a Relationship that signifies that a single model Element or a set of model Elements requires other model Elements for their specification or implementation.</p> <p>意訳: 依存は、とあるクラス(ここでは要素ではなくクラスと読んで差し支えない)の仕様や実装のために、別のクラスを必要としていることを意味する。</p> </blockquote> <p>つまり、実装に必要であればいいようです。</p> <p>ということで、Javaでどう書くかというと、私は「依存先のクラスをどこかしらで利用しており、上記5つの関係以外の場合全てを表す」と捉えます。</p> <pre><code class="java">class 依存されているもの { // ... } class 依存しているもの { // ... void hoge(依存されているもの 引数S) { // ... } } </code></pre> <p>例えば上記のように、メソッドの引数の型について、現状クラス図では定義することが難しいです。<br /> ですが、依存を書くことで、どこか関係性があるのだろうことは理解できます。</p> <p>他にも、キャストのためだけに呼ばれているとか、調べればいろいろ利用法が出てきそうです。<br /> どちらにせよ、クラス図からJavaに変換する際には利用できそうにありません。<br /> Javaからクラス図に変換する際に利用する関係だと思います。</p> <p>煩雑になりすぎてしまう問題は、容易に想像できます。<br /> しかし、UMLはいわゆるダイアグラムですから、読みづらければ非表示にすればよいだけです。<br /> 全てを表示する必要はなく、ダイアグラムの利用目的のために表示内容を変えるのは手だと思います。</p> <h1 id="おわりに"><a href="#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB">おわりに</a></h1> <p>私がUMLを研究していて、いつか挑戦してみたいことを書いてみました。<br /> もう修士課程は終盤直前ですが、もしこの話に興味があれば、勝手に使ってかまいません。<br /> もちろん、このサイトから引用したなんて野暮なことは書かず、自分で定義し、その理由付けも自分で行ってくださいね。<br /> その論文ができたら、ご一報くださると嬉しいです。</p> <p>UMLに未来があることを願います。</p> Morichan