論路的凝集との向き合い方
概要
- この記事は「論理的凝集」に着目して、開発生産性の高いコードを書くために気をつけるべきことのメモ
- 良かれと思って処理の共通化を行い、コードの量こそ減らしたものの機能追加に弱く、開発生産性の低いコードを書いてしまうことを避けるためのヒント
凝集度
https://gihyo.jp/magazine/wdpress/archive/2022/vol127
https://fortee.jp/object-oriented-conference-2020/proposal/a826b6c6-167c-4c5c-bfc7-52bb8bc22ec1
- 凝集度について載っているのでこちらで理解すると良い
- 本を買って読ませていただいた内容を元にこの記事を書いています。本記事では凝集度の説明については割愛
- この記事では 「関数」 に対する役割の尺度という意味で凝集度を扱う
論理的凝集
論理的凝集とは
- 関連のない処理をフラグ等で切り替える関数
- 良くないコードであるということに気づくことが難しい
- 処理の共通化がされている
論理的凝集を使っている関数の例
def exec(usecase)
a()
b()
if usercase == "usecase1"
c()
end
d()
end
引数の値の種類や、関数が増えると・・・
- 仕様が追加されると処理が複雑化していく
- 可読性が低く、保守が難しいコードになっていく
def exec
a()
b()
if usercase == "usecase1"
c()
end
d()
if usercase == "usecase1"
e()
end
if usercase == "usecase3"
f()
end
end
改善方法
- ユースケースごとに関数分けることで、単一責任の関数となる
- フラグを削除し関数を分ける
- 関数のユースケースが一つになり、シンプルになる
- 一つの関数を変更した際に、他の関数への影響がない
- 単一責任の原則
- モジュールはたった一つのアクターに対して責務を負うべきである
# 分割した関数
def usercase1
a()
b()
c()
d()
end
def usercase2
a()
b()
d()
end
関数増加、ユースケースの増加にも対応しやすい
- シンプルで理解しやすい
- 処理の変更の影響が該当の関数のみとなる
-
a()
b()
の重複があるが、共通化と論理的凝集の回避はトレードオフと考える- 論理的凝集を回避した方が良いことが多い
def usercase1
a()
b()
c()
d()
e()
end
def usercase2
a()
b()
d()
end
def usercase3
a()
b()
d()
f()
end
継承による隠された論理的凝集
- 親クラスにて子クラスの処理の共通化が行われている
- 一見条件分岐されるようなフラグもなく、論理的凝集ではないように見えない
例
class Base
def exec()
a()
b()
usercase()
d()
end
# オーバーライド用関数を定義
def usercase
end
end
class Usercase1 < Base
# オーバーライド
def usercase
c()
end
end
class Usercase2 < Base
# オーバーライドしない
end
ユースケースや関数が増えていくと・・・
- どんどん複雑になっていく・・・(無理やり複雑化させている節はあるが)
- もし
a
の処理がusecase1
だけの不要になったら・・・
class Base
def exec()
a()
b()
usercase()
d()
usercase_additional()
end
# オーバーライド用関数を定義
def usercase
end
# オーバーライド用関数2つ目を定義・・・
def usercase_additional
end
end
class Usercase1 < Base
# オーバーライド
def usercase
c()
end
def usercase_additional
e()
end
end
class Usercase2 < Base
# オーバーライドしない
end
class Usercase3 < Base
# オーバーライド
def usercase_additional
f()
end
end
改善方法
- 継承をやめる。親クラスによる処理の共通化をしない
- インターフェースもしくは抽象クラスを利用する程度にする(必須ではない)
- 下記はrubyのため擬似的なインターフェースを設定している
class Interface
def exec
raise NotImplementedError # 擬似的なインターフェース
end
end
class Usercase1 < Interface
def exec
a()
b()
c()
d()
end
end
class Usercase2 < Interface
def exec
a()
b()
d()
end
end
仕様が追加された場合も・・・
- 関数、ユースケースが増えた場合もシンプルな構造を保つことができる
class Interface
def exec
raise NotImplementedError # 擬似的なインターフェース
end
end
class Usercase1 < Interface
def exec
a()
b()
c()
d()
e()
end
end
class Usercase2 < Interface
def exec
a()
b()
d()
end
end
class Usercase3 < Interface
def exec
a()
b()
d()
f()
end
end
Author And Source
この問題について(論路的凝集との向き合い方), 我々は、より多くの情報をここで見つけました https://qiita.com/omukaik/items/17e2da56a50f1aa4cec0著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .