日本ソフトウェア科学会第 39 回大会 (2022 年度) 講演論文集

ソフトウェアの概念モデルに対する欠陥検知手法の検討
飯山 隆章

橋浦 弘明

今日のソフトウェアは大規模化，複雑化しており，ソフトウェアの設計段階で欠陥が作り込まれていると，その修正のために多くの手戻りを発生させることになる．このため，設計段階で欠陥を検知し除去することは重要である．ソフトウェアの欠陥検知手法は，これまでにも様々なものが提案されており，それらの多くはソースコードを対象としている．そこで，本研究ではソフトウェア開発の初期段階で用いられる概念モデルに対して欠陥検知をする手法について検討を行なう．
概念モデルに対して欠陥検知を行う場合，モデルから得られる情報が限定されるという問題がある．そこで，本研究では既存のソースコード向けの Code Smell，Anti-Pattern 検知手法を概念モデルに対して適用し，その有効性について評価を行なった．

でないと欠陥検出を行うことができない．このため，

1 はじめにソースコードからソフトウェア設計段階で作り込まれ今日のソフトウェアは大規模化，複雑化している．

た欠陥が見つかった場合には，それまでの作業をやりこのため，ソフトウェアの設計段階で欠陥が作りこま直す必要があるため，大きな手戻りが発生する．

れていると，実装したソースコードの品質を損なう可本研究は，このような問題の発生を防ぐためにソ能性がある．これにより，ソフトウェアの保守およびフトウェアの設計段階で用いる概念モデルに対して，機能の拡張の際に，困難を来たす可能性がある．このモデルの欠陥の特定する手法について検討を行なう．

ため、設計段階で欠陥を検知し除去することは重要である．

2 関連研究ソフトウェアの欠陥検知手法については，これまで

本章ではソースコードに対する既存の欠陥検出手にも様々なものが提案されている．それらの多くは法として，Chidamber と Kemerer [1] の CK メトリ

ソースコードを対象としており，その欠陥を特定する

クスと Genero ら [2] のクラス図からソフトウェアのために対象のソースコードのメトリクスを計測した

複雑さの計測手法，Peldszus ら [5] の Code Smell とり，Code Smell，Anti-Pattern を検出する手法が用

Anti-Pattern 検出手法の 3 つを取りあげる．

いられている．しかしながら，ソースコードを対象とした欠陥検出はソフトウェアが実装段階に入ってから∗ A Study of a Defect Detection Method for Software

Conceptual Models
This is an unrefereed paper. Copyrights belong tothe Author(s).
Takaaki Iiyama, Hiroaki Hashiura, 日本工業大学大学
院工学研究科電子情報メディア工学専攻, Departmentof Electronics, Information and Media Engineering
Major, Graduate School of Engineering, Nippon Institute of Technology.

2. 1 CK メトリクス
Chidamber と Kemerer [1] の CK メトリクスは，ソースコードから 6 つのメトリクスを収集し，ソフトウェアの複雑さを定量的に求める手法を提案している．

CK メトリクスは，WMC(重み付きメソッド数)，LCOM(クラスの凝集度)，NOC(クラスのサブクラスの数)，DIT(クラスの継承の深さ)，CBO(オブジェクトクラス間の結合)，RFC(クラスの応答) の 6 つのメが用いているモデルを図 1 に示す．この Anti-Patternトリクスから構成される．

検出モデルでは，Anti-Pattern をソースコード中に特定の Code Smell が含まれる状態であると定義し2. 2 クラス図からソフトウェアの複雑さの計測ている．また，Code Smell については他の特定のGenero ら [2] のソフトウェアの複雑さの計測手法

Code Smell が含まれていたり，ソースコードから得は，前述の CK メトリクスがソースコードに対するられるメトリクスが閾値を超えた状態であると定義

複雑さの計測であったのに対し，クラス図を対象とししている．したがって，Anti-Pattern 検出はソースて複雑さの計測を行っている．

コードからのメトリクスの取得，Code Smell の検出，具体的には，関連の数，依存関係の数，集約の数，Anti-Pattern 検出の順に行なう必要がある．

継承の数，継承の深さ，継承の親の数，属性の数，メ以下に Peldszus ら [5] の Anti-Pattern 検出の流れ

ソッドの数の 8 つのメトリクスをクラス図から収集の詳細について述べる．まず始めに，ソースコードかし，ソフトウェアの複雑さを評価している．

ら Code Smell の検出に用いる 7 種類のメトリクスを取得する．

2. 3 Peldszus らの手法

(a) F: クラスのフィールドの数

Peldszus ら [5] は，コードに含まれる Anti-Pattern(b) M: クラスのメソッドの数を検出するために，ソースコードからメトリクスの(c) GM: クラスの getter の数取得を取得し，それ組み合わせることで Code Smell(d) SM: クラスの setter の数の検出を行う手法を提案している．ここで，Anti-(e) OI: メッセージの送信の数

Pattern はソフトウェア全体に対して悪影響を与えるウェアに対して局所的に悪影響を与えるソフトウェア(f) II: メッセージの受信の数
a−F ∗M(g) LCOM5=
F −F ∗M(a: フィールドにアクセスしているメソッド数)の欠陥を表している．

次に，取得したメトリクスを使って Code Smell の欠陥を表しており，また，Code Smell とは，ソフト

Peldszus ら [5] が検出対象としている Anti-Patternは以下の 3 つである．

検出を行う．前述の 3 つの Anti-Pattern 検出に用いられる Code Smell は以下の 5 つである．これらのメ• God Class: 1 つのクラスに大部分の責務が割トリクスの値をクラス毎に求め，それらの平均を閾り当てられている状態．God Class が与える悪値として，それを超えているクラスについては Code

影響は，モジュールに必要なものと不必要なものSmell として検知を行う．
GM + SM(1) Data Class =M(2) Large Class = F + M
OI(3) Controller Class =II(4) Low Cohesion = LCOM5が分かりづらくなるため保守性が低下する．

• Blob: 1 つのクラスに大部分の責務が割り当てられている状態に加え，他のクラスのためのデータを保持している状態．Blob が与える悪影響は，God Class と同様の影響に加え，他のクラスのためのデータを保持しているため，修正の影響が他のクラスに波及する可能性がある．

• Swiss Army Knife: 多くの責務を果たせる(5) Data Class User: Data Class を参照しているクラス数
最後に，検出された Code Smell を用いて Anti-Pattern の検出を行う．

インタフェースを持っている状態．Swiss Army

ここで，Code Smell の検出について，図 2 の事例

Knife が与える悪影響は，全貌の把握が難しく，を用いて具体的に説明する．このクラス図から検出さ継承などの際に，責任同士で干渉を起こす可能性れる Code Smell は Large Class である．図 2 に含まがある．

れる Main, PrinterProxy, Printable, Printer クラス

これらの Anti-Pattern 検出のために Peldszus ら [5]それぞれについて 各クラス F+M の総和 を求めると

図1

Anti-Pattern 検出のためのモデル [5]M ain+P rinterP roxy+P rintable+P rinter = (0+とができないという問題がある．本研究では，この1)+(2+4)+(0+3)+(1+4) = 14 となり，Large Class
各クラス F+M の総和
検出の閾値については=クラス数
14= 3.5 と求めることができる．よって，閾値を4超えている PrinterProxy クラス (F + M = 6) とような問題を解決するために，概念モデルから得らPrinter クラス (F + M = 5) に Large Class として

具体的には，前章で述べたメトリクスや Code Smell

れる情報だけを用いて Peldszus ら [5] の Code Smell
や Anti-Pattern の検出を行う手法について検討を行った．

について，概念モデルから得ることができない項目の検知される．

識別を行い，その後にそれらの項目に対する代替案について検討を行うこととした．

3. 1 概念モデルから得ることができない項目の識別
ソースコードと実装前の概念モデルの大きな違いは，メソッドの内部情報に関するメトリクスが取得できないことである．
この制約により，前章で述べた 7 種類のメトリク
図2

Code Smell 検出の例

スのうち，メッセージの送信 (OI)，メッセージの受信 (II)，LCOM5 の 3 つは算出することができな
い．しがって，これらのメトリクスに依存している

3 提案手法Controller Class，Low Cohesion，Data Class

前述の Peldszus ら [5] の手法はソフトウェアの欠User の 3 つの Code Smell についても検出が不可能

陥検出に対して有効な方法であるが，ソースコードである．

からメトリクスを取得する必要があるため，コードが存在しない実装前の概念モデルに対して用いるこ

3. 2 概念モデルから得ることができない項目に対する代替案
ここで，前述の概念モデルから得ることができない項目に対して代替案を検討する．
まず，メッセージの送信 (OI)，メッセージの受信して表現することができるため，クラス図のようなデータと親和性が高い．また，各種メトリクスの計測と Code Smell，Anti-Pattern 検知は JavaScript を用いて実装を行った．

(II) については，関連の誘導可能性で代替する．具体

ツールの実行の流れを図 3 に示す．本ツールでは的には，関連に誘導可能性が存在する場合，矢印の指概念モデル（クラス図）から欠陥を検知するため，前し元となっているクラスは送信 (OI)，矢印の指し先処理としてクラス図をあらかじめグラフデータベーのクラスは受信 (II) として取り扱うこととした．まスに登録しておく必要がある．具体的には概念モデた，Data Class の判定については誘導可能性の有無

ル（クラス図）をクラス情報 (クラス名，フィールド，のみを用いて判定することとする．

メソッド) と関連 (関連が指しているクラス，関連を一方で，LCOM5 については代用になるものが存在しなかった．このため，本研究では LCOM5 と，それに依存する Code Smell(Low Cohesion) については取り扱わないものとする．よって，Low Cohesion

送っているクラス，関連の種類) で表現された JSON形式に変換することが必要になる．
実装したツールによる Code Smell と Anti-Patternの検知の例を図 4 に示す．

に関しては，Anti-Pattern である God Class の検出に用いられているが，これについては他の Code Smell

5 評価のみで God Class の検出を行う．

ツールの有効性を確認するために実験を行った．実これらの代替案を反映した本研究で用いるメトリクスを以下に示す．

(a) F: クラスのフィールドの数験目的は本ツールが Peldszus ら [5] と同じ欠陥検知結果を示すことを確認することである．実験目的を以下の RQ で明らかにする．

(b) M: クラスのメソッドの数(RQ1) 本ツールの欠陥検知の精度はどの程度か(c) GM: クラスの getter の数(RQ2) Peldszus ら [5] の手法から改良した点が本(d) SM: クラスの setter の数

ツールの欠陥検知にどれだけ影響を及ぼすか(e) OI: 誘導可能性の数 (矢印の元)RQ1 は，本ツールの正解率と適合率，再現率，F 値(f) II: 誘導可能性の数 (矢印の先)を求めることによって確認する．RQ2 は，Peldszus(g) LCOM5: (※利用不可)

ら [5] の手法と本ツールでそれぞれ欠陥検知を行い，本研究で用いる Code Smell の一覧を以下に示す．
GM + SM(1) Data Class =M(2) Large Class = F + M
OI(3) Controller Class =II(4) Low Cohesion: (※利用不可)(5) Data Class User: Data Class から誘導可能であるクラスその差を比較する．
実験対象として，以下の 2 つのデータを使用した．

RQ1 を明らかにするためには，実装を行っていない概念モデルが必要になる．このため，日本工業大学大学院電子情報メディア工学専攻の講義「ソフトウェア設計特論」で学生が作製したモデルを 6 個とSpiderTailed [4] の初期モデルを 1 個の計 7 個を対象に評価を行った．また，RQ2 では Peldszus ら [5] の4 実現手法手法を用いるため，クラス図と実装済みのコードの前章で述べた手法実現するために，ウェブアプリ両方が必要になる．したがって，今回の評価ではデザケーションとしてツールの実装を行った．クラス図

インパターンの書籍 [6] に記載されているパターンをの保存にはグラフデータベースである neo4j [3] を用22 個を用いた．デザインパターンは優れた設計に見いた．neo4j はデータがノードとリレーションで構成られる共通事項をカタログ化したものであり，それをされており，ノードをクラス，リレーションを関連と実装したコードからは Anti-Pattern が検知されるこ図3

本手法のフローチャートとは基本的にはない．

率と適合率，再現率，F 値の結果を表 2 と表 4 に示
す．Code Smell は表 2 が示すように全体的に 0.95 以

6 結果と考察上の数値であり，正解率は 0.98 と高い数値を得るこ

6. 1 RQ1とができた．Anti-Pattern は表 4 が示すように再現

RQ1 の評価は，クラス図から本手法の正解である率を除く結果で 0.9 以上を示しており，特に正解率と欠陥検知結果と本ツールの欠陥検知の結果を混合行適合率が最も高い数値を得ることができた．全体的列し，正解率と適合率，再現率，F 値を用いて行う．に Code Smell と Anti-Pattern の検知精度は正解率

本手法の正解である欠陥検知結果と本ツールのが 0.98 以上の数値を出したことで，高い欠陥検知のCode Smell と Anti-Pattern の検知結果を表 1 と表3 に示す．また，Code Smell と Anti-Pattern の正解精度があることが明らかになった．
次に表 1，表 3 中の FN と FP の原因について考察

する．FN は，2 個であり，具体的な Code Smell は

6. 2 RQ2Controller Class と Data Class であった．FP は，3RQ2 は，Peldszus ら [5] の手法から改良した点が個であり，具体的な Code Smell の Data Class User

本ツールの欠陥検知にどれだけ影響を及ぼすかを調が 2 つ，Anti-Pattern の Swiss Army Knife の 1 つべることである．したがって，概念モデルと実装がであった．誤検知の原因は主に人為的ミスとツールの用意されたソフトウェアを用意し，概念モデルに対欠陥であり，手法による問題ではないことが明らかにしては本手法のツールを，ソースコードに対してはなった（表 5）．

Peldszus ら [5] の手法を適用し，それぞれから得られたデータを比較する．具体的には Peldszus ら [5] の手

表1

正解の結果とツールの Code Smell の検知結果
ツールの検知結果検知した (Positive)

本手法の検知結果

検知しない (Negative)

検知した (True)602

検知しない (False)

2168

法の結果を True/False とし，本ツールの結果の誤答
TP + TN率 (1 −) をもとめ，本手法
TP + FP + FN + TNで改良した箇所の影響度を調べることとした．

Peldszus ら [5] の手法と本ツールの Anti-Patternの検知結果を表 6 に示す．また，表 6 に示すとおり，誤答率は 0.04 であった．これらの結果から，本ツー

表2

Code Smell の正解率と再現率，適合率，F 値

正解率

再現率

適合率

F値

0.982758621

0.967741935

0.967741935

0.967741935

ルの改良点が結果に与える影響は低いと考えられる．

Anti-Pattern の種類別に FN と FP の原因を考察する．FN は God Class が 6 つと Blob が 5 件であった．また，FP は God Class が 2 件であった．
これらが発生した原因について詳細に調査したと

表3

正解の結果とツールの Anti-Pattern の検知結果
ツールの検知結果

本手法の検知結果検知した (Positive)

検知しない (Negative)

検知した (True)50

検知しない (False)

1

168

ころ，Code Smell の Low Cohesion を取り除いたことによる弊害と Controller Class が検知部分の問題であることが判明した．本来，God Class の検出はController Class，Low Cohesion，Large Class の 3
つの Code Smell によって行われる．しかしながら，本ツールではモデルから取得できる情報の制約から，

表4

Anti-Pattern の正解率と再現率，適合率，F 値

Low Cohesion を取り扱わないものとしたため，その正解率

再現率

適合率

F値分だけ Anti-Pattern の検知が甘くなってしまったた

0.994252874

0.833333333

10.909090909

めであると考えられる．また，Controller Class については，メッセージの送信 (OI)，メッセージの受信(II) を誘導可能性のみで判定を行ったが、これについ誤検知の原因の種類分けてもその分だけ Anti-Pattern の検知が甘くなってしnumber

欠陥の種類まったと考えられる．

1

Controller Class

人為的ミス

2

Data Class

ツールの欠陥

3

Data Class User

人為的ミス

表545

Data Class User
Swiss Army Knife

誤検知の原因表6

Peldszus ら [5] の手法とツールの Anti-Patternの検知結果
ツールの検知結果検知した (Positive)

ツールの欠陥人為的ミス

Peldszus ら [5] の手法の検知結果

検知しない (Negative)

検知した (True)02

検知しない (False)11305

7 まとめと今後の課題
従来の欠陥検知手法は，ソースコードを対象としたものが多く概念モデルに対して欠陥検出を行うことができなかった．これに対し，本研究では概念モデル上で欠陥の検知を行う手法とツールを開発し，評価を行った．実装したツールと Peldszus ら [5] の手法を比較した結果，誤検知率が十分低いことが明らかになった．
今後の課題は，欠陥を検知した箇所から，利用者に最適な修正方法を提示したり，他の Code Smell やAnti-Pattern に対してツールを拡張することである．
謝辞 本研究の一部は JSPS 科研費 21K12179 の助成を受けた．

参 考 文 献[1] Chidamber, S. and Kemerer, C.: A metrics
suite for object oriented design, IEEE Transac-tions on Software Engineering, Vol. 20, No. 6(1994),
pp. 476–493.
[2] Genero, M., Piattini, M., Manso, M. E., and Garcia, F.: Early Metrics for Object Oriented Information Systems, Proceedings of the 6th International Conference on Object Oriented Information
Systems, OOIS ’00, Berlin, Heidelberg, SpringerVerlag, 2000, pp. 414–425.
[3] Neo4j: https://neo4j.com. accessed:2022-08-10.
[4] Okajima, T., Tanaka, T., Hazeyama, A., andHashiura, H.: SpiderTailed: A Tool for Detecting
Presentation Failures Using Screenshots and DOM
Extraction, Knowledge-Based Software Engineering: 2022, Cham, Springer International Publishing, Aug. 2022 (to appear).
[5] Peldszus, S., Kulcsár, G., Lochau, M., andSchulze, S.: Continuous detection of design ﬂaws inevolving object-oriented programs using incremental multi-pattern matching, 2016 31st IEEE/ACM
International Conference on Automated Software
Engineering (ASE), 2016, pp. 578–589.
[6] 結城浩: 増補改訂版 Java 言語で学ぶデザインパター
ン入門, SB クリエイティブ株式会社, 2019.

図4

ツールによる欠陥検知の例

D, L

VisualXMLBuilder

VisualConstraintAnalyst+ __init__()+ build(xml_path: str, constriant_dictionary: dict[str: VisualConstraint])+ field: type

DU

VisualMultipleAnalyst

SelectorPathDeserializer

1+ field: type

DU1VisualScreenShotGenerator

VisualConstraintComparer

VisualConstraintParser

Anti-Pattern・God Class : G・Blob : B・Swiss Army Knife : S+ __init__()+ parse(xml: str)
+ get_component_constraint(selector): Optional[VisualConstraint]+ get_all_component_constraint(): Dict[str: VisualConstraint]- __tree: ElementTree- __root: Element

D, L, DU

+ __init__()1+ set_xml_2_before_update_constraints(constraints_path:str)+ set_xml_2_after_update_constraints(constraints_path: str)
+ compare(test_report: VisualTestReport)- __before_update_constraints: dict[str: VisualConstrant]- __after_update_constraints: dict[str: VisualConstraint]

Extends

VisualReportWriter+ __init__()+ write(path: str, report: VisualReport)+ field: type

Extends+ __init__()+ write(path: str, report: VisualReport)+ __init__()+ write(path: str, report: VisualReport)

HTMLReportWriter+ field: type+ field: type

HTMLReportWriter+ __init__(dict{str: VisualConstraint})+ set_after_update_constraint(selector: str, visual_constraint: VisualConstraint)+ set_before_update_constraint(selector: str, visual_constranit: VisualConstraint)+ set_after_update_image_paths(selector: str, image_paths: list[str])+ set_before_update_image_paths(selector: str, image_paths: list[str])+ set_localize_html(selector: str, html: str)
+ set_localize_css(selector: str, css; str)
+ set_score(selector: str, score: int)
+ get_after_update_constraint(selector: str): VisualConstraint+ get_before_update_constraint(selector :str): VisualConstraint+ get_after_update_image_paths(selector: str): list[str]+ get_before_update_image_paths(selector: str): list[str]+ get_localize_html(selector :str): str+ get_localize_css(selector str): list[str]+ get _score(selector: str): int

VisualRreport

1

D, L, C, G

+ execute_visual_test(before_constraint: str, after_constraint: str, before_html: str, after_html: str)
+ execute_visual_test_all_sequence()

1str, xml_path)
1+ execute_generate_visual_constraint(json_path:- __data: dict{str: dict{str: dict{str: list[str], str: VisualConstraint}, str: dict{str: list[str], str: VisualConstraint}, str: str, str: list[str], str: int}}

L, C, DU ,B, G

VisualTestExecuter+ execute_generate_all_elements_screen_shot(url: str, driver:Union[Chrome, FireFox], save_path: str): type+ field: type

DUD, L+ __init__()+ make_constraints(selectors: list[str]): dict[str: VisualConstraint]+ make_results(selectors: list[str]): dict[str, bool]

1+ __init__(driver: Union[Chrome, FireFox])+ save_screen_shot_all_elements_from_constraints(url: str, constraint_path: str, json_path_str, save_path:str)+ save_screen_shot_all_elements_from_selector_list(url: str, selectors: list[str], json_path: str, save_path:str)- __driver: Union[Chrome, FireFox]- __paser: VisualConstraintParser- __serializer: SelectorPathSerializer- __i: int+ generate_constraints(json_path: str, xml_path)

1+ __init__()SelectorPathSerializer+ __init__()+ serialize(selector_path: dict[str: str], json_path: str)
+ serialize_multiple(selector_path[str: list[str]], json_path: str)

+ field: type

VisualConstraintGenerator+ __init__()+ deserialize(json_path: str): dict[str: str]
+ deserialize_multiple(json_path: str): dict[str, list[str]]+ field: type

SelectorGenerator+ generate_all_selector_from_html_string(html_string: str): dict[str: int]
+ generate_all_selector_from_html_document(url: str): dict[str: int]
+ generate_all_selector_from_beautiful_soup(soup: BeautifulSoup)- __generate_selector(root_selector: str, now_node: Element, all_selector: dict[str: int])- __generate_now_selector(now_id: str, now_class: str, now_node: Element): str+ field: type+ field: type

VCDictionaryBuilder+ __init__()+ analyze_all_property(frame_path: str, component_path: list[str], visual_constraint: VisualConstraint)+ analyze_size(img_path: list[str], visual_constraint: VisualConstraint)+ analyze_locations(frame_path: str, component_path: list[str], visual_constraint: VisualConstraint)+ analyze_background_color(img_path: list[str], visual_constraint: VisualConstraint)- __check_constraint_majority(results: list[VisualConstraint]): VisualConstraint+ field: type

Extends+ __init__()+ analyze_all_property(frame_path: str, component_path: str, visual_constraint: VisualConstraint)+ analyze_size(img_path: str, visual_constraint: VisualConstraint)+ analyze_locations(frame_path: str, component_path: str, visual_constraint: VisualConstraint)* analyze_background_color(img_path: str, visual_constraint: VisualConstraint)+ field: type

Code Smell・Data Class : D・Large Class : L・Controller Class : C・Data Class User : DU

D, L, DU

DU

L, C, DU, G, B

L, DU

+ __init__(selector: str)
+ get_selector(): str+ get_all_property(): dict[str: Optional[str]]+ get_property(property_name: str): Optional[str]+ set_property(property_name: str, property_value: str)

- selector: str- visual_property: dict[str : str]

VisualConstraint

L

VisualDOMComparerL, DU+ __init__()+ localize(selector: str, error_type: list[str]): str, list[str]- __html_localizer: VisualHTMLLocalizer- __css_localizer: VisualCSSLocalizer

VisualDOMLocalizer+ __init__()+ set_before_update_html(url: str)
+ set_after_update_html(url: str)
+ set_before_update_css(url: str)
+ set_after_update_css(url: str)
+ is_compare_html(): bool
+ is_compare_css(): bool
+ is_compare(): bool
+ get_html_diff(): list[str]+ get_css_diff(): list[str]+ get_diff(): list[str], list[str]- __before_update_html: BeautifulSoup- __after_update_html: BeautofulSoup- __before_update_css: CSSStyleSheet- __before_update_css: CSSStyleSheet1+ __init__()+ set_css_from_url(url: str)
+ set_css_from_strings(css_strings: str)
+ localize(selector: str, error_type: list[str]): list[str]- __stylesheet: CSSStyleSheet

VisualCSSLocalizer+ __init__()+ set_html_from_url(url: str)
+ set_html_from_strings(html_strings: str)
+ localize(selector: str): str- __html: BeautifulSoup

VisualHTMLLocalizer

D