面白駆動人生

やっほー

【読書メモ】Fundamentals of Software Architecture ~第三章 Modularity~

第三章は、Modularity(モジュラリティー)について。

MDN web docでModularityの定義をみると、以下のように説明されています。

システムのコンポーネントを分離して再結合できる程度を指しており、またソフトウェアパッケージを論理ユニットに分割することもあります。モジュラーシステムの利点は、部品を独立して考えることができることです。

この章では、モジュラリティーを理解するための三つの指標が提示されています。
cohesion(凝集度), coupling(結合度) それとconnascence(コナーセンス)です。

凝集度と結合度については、わかりやすい記事が既にあるので、ここでは飛ばします。

本記事では、残りのconnascenceについて詳しく触れていきます。

connascenceとは

凝集度と結合度は耳にすることも多いですが、connascenceは日本語だと情報がなかなか出てこないです。 connascenceは、Meilir Page-Jonesにより発明されたソフトウェア品質を測る指標だそうです。

coはtogather、 nascenceは'to be born' を意味するようです。
(ルネッサンス(Renaissance)は、re + nascence で復活の意味)
connascenceは複数のものが、同時に生まれるという意味になります。

では、ソフトウェア開発の文脈で、connascenceであるとはどういうことを指すのでしょうか。

connascenceの提唱者、Meilir Page-Jonesは、connascenceを以下のように定義しています。

two components are connascent if a change in one would require the other to be modified in order to maintain the overall correctness of the system.
二つのコンポーネントがconnascentである時、一方の変更が、システム全体の整合性を保つために、他方のコンポーネントの変更を要求する。

Type of connascence

次に、connascenceの種類について。全部で9種類存在するようです。
connascence.ioを元に、詳しく見ていきます。

1. Connascence of Name (CoN)

名前についての合意
function Aは、A()と呼び出す、という結合。
発見しやすく、リファクタもrenameで容易に可能。

2. Connascence of Type (CoT)

型についての合意 主に、静的型付け言語での話。例えば、以下のような例。

int age;
age = 10.5 // TypeError

こちらも、静的型付け言語であれば発見しやすくリファクタも容易。

3. Connascence of Convention (CoC) / Connascence of Meaning (CoM)

意味についての合意
例えば、次のような関数はCoCによる結合を生み出す。

def get_user_role(username):
    user = database.get_user_object_for_username(username)
    if user.is_admin:
        return 2
    elif user.is_manager:
        return 1
    else:
        return 0

0, 1, 2のそれぞれの「意味」をget_user_roleを利用する側は知っている必要が出てしまう。 Enumとしてまとめることで、CoCの結合にすることができる。

4. Connascence of Algorithm (CoA)

アルゴリズムについての合意
複数のエンティティが同一のデータを扱う際にしばしば発生する。 例えば、次のようなコードは、エンコーディングUTF-8である、という合意を元にしている。

def write_data_to_cache(data_string):
    with open('/path/to/cache', 'wb') as cache_file:
        cache_file.write(data_string.encode('utf8'))

def read_data_from_cache():
    with open('/path/to/cache', 'rb') as cache_file:
        return cache_file.read().decode('utf8')

また、サーバサイドとクライアントサイドの両方に実装される、 ハッシュアルゴリズムや、バリデーションロジックなどでも起きうる。

5. Connascence of Position (CoP)

位置についての合意
次のようなコードは、connascence of positionによって結合されている例だ。

def get_user_details():
    # Returns a user's details as a list:
    # first_name, last_name, year_of_birth, is_admin
    return ["Thomas", "Richards", 1984, True]

def launch_nukes(user):
    if user[3]:
        # actually launch the nukes
    else:
        raise PermissionDeniedError("User is not an administrator!")

user = get_user_details()
launch_nukes(user)

userというリストのうち、4つめの要素が権限を示すということを知らないといけない。 dictやエンティティにすることで、CoNの結合にすることができる。

6. Connascence of Execution (CoE)

実行順序への合意
例えば、リソースのロック/アンロックや、カプセル化されたステートマシンで起きうる。

以下のようなEmailSenderは、最後の2行が不正となる。

email = Email()
email.setRecipient("foo@example.comp")
email.setSender("me@mydomain.com")
email.send()
email.setSubject("Hello World")

このケースでは、実装箇所が近いため、発見は用意だが、 最後の2行が別スレッドで実行されるようなケース(high locality)では、発見が困難になる。

7. Connascence of Timing (CoT)

実行タイミングについての合意
例えば、複数スレッドで並行処理などで起きうる。

8. Connascence of Values (CoV)

値についての合意
例えば、以下のようなコードではArticleState.Draftが初期状態である、という合意が元にある。

class ArticleState(Enum):
    Draft = 1
    Published = 2


class Article(object):

    def __init__(self, contents):
        self.contents = contents
        self.state = ArticleState.Draft

    def publish(self):
        # do whatever is required to publish the article.
        self.state = ArticleState.Published

テストコードでArticleの状態をチェックする際など、Articleクラスの初期状態を知っていなければいけなくなる。 しかし、そうなるとArticleクラスの初期状態がDraftではなくなった場合、テストケースは壊れることになる。

以下のように、初期状態を示す値を持つことで、その問題を回避できる。

class ArticleState(Enum):
    Draft = 1
    Published = 2
    InitialState = Draft


class Article(object):

    def __init__(self, contents):
        self.contents = contents
        self.state = ArticleState.InitialState

例えば、初期状態がDraftから、Preproductionになった際は、以下のように変更すればよい。

class ArticleState(Enum):
    Preproduction = 1
    Draft = 2
    Published = 3
    InitialState = Preproduction

9. Connascence of Identity (CoI)

同一エンティティについての合意
一般的な例としては、2つの別々のコンポーネントが、共通のデータ構造(例えば分散キュー)を共有・更新する時など。

3つの性質(Properties)

また、connascenceは3つの性質で見ることができるようです。

  • degree:より強いconnascencesは、より発見しづらく、リファクタリングしづらい。
  • locality:より多くのエンティティと、connascentであるエンティティは、問題の影響が大きくなる。
  • strength:Connascentな要素は、コード上で近い位置に存在する方がよい

1. Strength

Connascenceには、強度が存在し、コードはConnascenceが弱くなる方にリファクタするべきとされている。 f:id:yktm31:20200720042908p:plain CoNは、renameによって変更可能であるため結合が弱く、CoCはコード全体から変更を洗い出すことがより難しいため、結合は強くなる。 また、Static connascenceに比べ、Dynamic connascenceは実行時の挙動について知る必要があり、より強いconnascenceである。

2. locality

コンポーネント同士の近さ、を示す軸。 より強いconnascencesは、同一モジュール内などの近い関係では許容しやすく、 離れたコンポーネント間では、より弱いconnascencesであるべきである。

3. degree

connascenceが関与する影響度を示す軸。 結合が2つのコンポーネントに影響するのは、200のコンポーネントに影響するのかを見る。

参考

日本語での情報が少なく、あまり認知されていない(?)概念のように思われる。 以下、参考にしたリソースのメモ。

www.youtube.com

practicingruby.com

英単語メモ

■untangle (verb)
def: to separate pieces of string, hair, wire, etc. that have become twisted or have knots in them
def: to make something that is complicated or confusing easier to deal with or understand

■incarnation (noun)
def: a period of life in a particular form def: a person who represents a particular quality, for example, in human form