位置のコナーセンス

位置のコナーセンスは複数のエンティティが値の順序に関して一致しなければならないときに生じます。

データ構造

たとえば、ユーザーの詳細を取得する関数を考えます。

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!")

この2つの関数は*位置のコナーセンス*で結びついています。ユーザーの詳細リストにある値の順序が変更されれば、両方の箇所を更新する必要があります(この例で特にあぶなっかしいのは、誰かがユーザーの詳細リストを [first_name, initials, last_name, year_of_birth, is_admin] に変更して、launch_nukes の中の確認ロジックを更新せずにいる場合です)。

このコナーセンスはリストを辞書やクラスに変えることで名前のコナーセンスに改善できます。以下の例では上の関数を辞書で書き換えています。

def get_user_details():
    return {
        "first_name": "Thomas",
        "last_name": "Richards",
        "year_of_birth": 1984,
        "is_admin": True,
    }


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

注意点していただきたいのですが、2つの関数はまだ結合しています。けれども、位置のコナーセンスをより弱い名前のコナーセンスに変換しました。この変更によって、get_user_details 関数の可読性も上がりました。キーの順序を注釈するためのコメントはもう必要ありません。

同じような解決策として、辞書ではなくクラスを使うこともできます。返却するデータ構造が関連する制約や操作をもつ場合にはそのほうが便利でしょう。

関数の引数

位置のコナーセンスは関数の引数リストの中にもよく現れます。想像上のEメール送信ライブラリの一部にある以下のような関数宣言を考えてみてください。

def send_email(firstname, lastname, email, subject, body, attachments=None):

この send_email 関数を呼び出すコードは引数の順序を覚えておかなければなりません。万が一順序が変更されると、関数を呼び出すコードもすべて変更する必要があります。この例も複数のパラメータを順番に渡す代わりに構造化されたオブジェクト(クラスや辞書)を渡すことによって名前のコナーセンスへと改善することができます。