LoginSignup
4
1

CohesionでPythonの凝集度を計測する

Last updated at Posted at 2023-12-18

Pythonでの凝集度の測り方

凝集度とは

凝集度はソフトウェアのメトリクスで、関連するメソッドや変数が近くに置かれているかを測る指標です。

例えばLCOM4だと、とあるクラスにおいて変数にアクセスしている関数を辿っていってクラスタリングして、クラスタ数が1なら良い状態、2以上ならクラスを分割すべきなので悪い状態、0(メソッドなし)も悪い状態としています。

今回、Pythonで凝集度を測れるcohesionをみつけたので試してみます。

cohesionでの凝集度の定義はとあるクラスにおいて、とあるメソッドが全てのインスンス変数、クラス変数を使用していると100%としているようです。

環境構築

実際のコードベースに対して評価したいのでPython製のチャットサービスのZulipのコードを持ってきます。

依存を見る限りDjangoとTornadoを両方使用しているようです。複雑。

$ git clone --depth 1 git@github.com:zulip/zulip.git
# 1e0339c18bb46a1c502b01a71c2d66471848cf36

cohesionのインストール

$ python -m pip install cohesion
$ python -m cohesion -h
usage: __main__.py [-h] [-v | -x] (-f FILES [FILES ...] | -d DIRECTORY) [-b BELOW | -a ABOVE]

        A tool for measuring Python class cohesion.


optional arguments:
  -h, --help            show this help message and exit
  -v, --verbose         print more verbose output
  -x, --debug           print debugging output
  -f FILES [FILES ...], --files FILES [FILES ...]
                        analyze these Python files
  -d DIRECTORY, --directory DIRECTORY
                        recursively analyze this directory of Python files
  -b BELOW, --below BELOW
                        only show results with this percentage or lower
  -a ABOVE, --above ABOVE
                        only show results with this percentage or higher

Zulipにcohesionをかけてみる

Zulipにcohesionをかけています。

DjangoはMVCでなくMTCなのでざっくりMVCのModel=DjangoのModel, MVCのView = DjangoのTemplate, MVCのController=DjangoのView。

ロジックがありそうなのはmodelとviewだとあたりをつけて1ファイルについてcohesionをかけてみます。

$ python -m cohesion -f zerver/models/alert_words.py
File: zerver/models/alert_words.py
  Class: AlertWord (14:0)
    Total: 0.0%
  Class: Meta (24:4)
    Total: 0.0%

$ python -m cohesion -f zerver/views/auth.py
File: zerver/views/auth.py
  Class: TwoFactorLoginView (794:0)
    Function: __init__ 1/3 33.33%
    Function: get_context_data 2/3 66.67%
    Function: done 0/3 0.00%
    Total: 33.33%

定義に従って、ダブルアンダースコアのメソッドも対象になります。

__init__ はまだ許せるのですが、 __str__ が出る場合もあるのでクラス設計の良さの指標の要素に入れてしまうのはノイズな気もしますね。

ディレクトリ指定で一括測定

続いて -d でディレクトリ一括でcohesionをかけます。

$ python -m cohesion -d zerver/models/
File: zerver/models/custom_profile_fields.py
  Class: CustomProfileField (59:0)
    Function: __str__ 4/20 20.00%
    Function: as_dict 7/20 35.00%
    Function: is_renderable 1/20 5.00%
    Total: 20.0%
  Class: CustomProfileFieldValue (182:0)
    Function: __str__ 3/4 75.00%
    Total: 75.0%
  Class: Meta (188:4)
    Total: 0.0%
File: zerver/models/linkifiers.py
  Class: RealmFilter (48:0)
    Function: __str__ 3/4 75.00%
    Function: clean 2/4 50.00%
    Total: 62.5%
  Class: Meta (60:4)
    Total: 0.0%
File: zerver/models/onboarding_steps.py
  Class: OnboardingStep (8:0)
    Total: 0.0%
  Class: Meta (13:4)
    Total: 0.0%
(後略)

$ python -m cohesion -d zerver/views
File: zerver/views/registration.py
File: zerver/views/user_settings.py
File: zerver/views/digest.py
File: zerver/views/auth.py
  Class: TwoFactorLoginView (794:0)
    Function: __init__ 1/3 33.33%
    Function: get_context_data 2/3 66.67%
    Function: done 0/3 0.00%
    Total: 33.33%
File: zerver/views/custom_profile_fields.py
File: zerver/views/realm_linkifiers.py
File: zerver/views/documentation.py
  Class: DocumentationArticle (35:0)
    Total: 0.0%
  Class: ApiURLView (66:0)
    Function: get_context_data 1/1 100.00%
    Total: 100.0%
  Class: MarkdownDirectoryView (78:0)
    Function: get_path 4/5 80.00%
    Function: get_context_data 4/5 80.00%
    Function: get 2/5 40.00%
    Total: 66.67%
  Class: IntegrationView (323:0)
    Function: get_context_data 1/2 50.00%
    Total: 50.0%
(後略)

別でactionsというディレクトリも含めてかけてみたのですが、全体的にクラスのないファイルが多いですね。。。

コードの品質メトリクスはJava由来のクラスベースの思想を基準にしていることが多いのですが、クラスをあまり使わないアーキテクチャだと流用はできないですね。

上位、下位のファイルの洗い出し

-bで一定以下のファイル、-aで一定以上のファイルを出せます。

前述の通り、クラスのないファイルがだいぶノイズになるので適当にフィルターしています。

$ python -m cohesion -d zerver/views -b 30 |grep -v 'File:'
  Class: DocumentationArticle (35:0)
    Total: 0.0%
  Class: VideoCallSession (39:0)
    Function: __init__ 0/0 0.00%
    Total: 0.0%
  Class: InvalidZoomTokenError (44:0)
    Function: __init__ 0/1 0.00%
    Total: 0.0%
  Class: InvalidMirrorInputError (29:0)
    Total: 0.0%
  Class: SentryTunnelSession (25:0)
    Function: __init__ 0/0 0.00%
    Total: 0.0%

$ python -m cohesion -d zerver/views -a 70 |grep -v 'File:'
  Class: ApiURLView (66:0)
    Function: get_context_data 1/1 100.00%
    Total: 100.0%

最後に

以上です。

お手軽に凝集度を測れるツールとしては有用なんですが、Zulipのつくりだとそこまであっていないといった感じでしょうか。

ライブラリーのコードも比較的短いので自身のユースケースに合わせて自作してみるのも面白そうですね。

4
1
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
4
1