プログラミングにおける循環参照 なぜいけないのか

循環参照という言葉はExcelで見たことがある人もいるかと思いますが、プログラミングの現場においても、起こることはあります。

普通に循環参照って検索すると、大体Excelの話が出てきますが、循環参照自体は下記のサイトの説明がわかりやすいと思います。

https://gimo.jp/glossary/details/circular_definition.html

今回は、弊社のプログラマーさんと話していて、このような構成になっていたところ

「なんでいけないんですか?」

と言われてパッと答えられなかったので書いておきます。

例えば、ここに構成が複雑なオブジェクト、Itemクラスがあります。

Itemオブジェクトはあまりに複雑なので、ユーザーもItemからコピーして作りたいという要望があったとします。

なので、ItemMasterというクラスが爆誕します。で、ItemMasterはItemを参照して、そのItemのコピーをバンバン作っていきます。

ちなみに、できたItemはユーザーが個別にプロパティをちょこちょこ編集できる仕様です。

クラスとプロパティを抜粋して書くと、次のようになります。

<?php


class ItemMaster{

    public $id;
    public $item_id;

}


class Item{

    public $id;
    public $item_master_id;


}

文章で書くと、

「10番のアイテムができがよいので、コピペして作りたい。そこで、10番をマスターとして、アイテムマスター(Id:1)を作る。アイテムマスターから、11番、12番、13番のアイテムを作る。」

ということです。

ここまでは問題がなさそうです。

実際の問題①

アイテムを更新することを考えてみましょう。

「10番のアイテムのプロパティにちょっと変更を加えた、アイテムマスター (Id:1) から作られた アイテムたちに都度変更を加えるのは大変だから、マスターが更新されたらマスターから作られたアイテムはマスターの変更を反映して変更する。つまり11番、12番、13番のアイテムに10番の変更が反映される。」

そのために、次のようにアップデートメソッドを実装します。

class ItemMaster{

    public $id;
    public $item_id;

    public function updateItem(){
        //このマスターから作られたItemを更新する
    }
    //以下続く
}


class Item{

    public $id;
    public $item_master_id;

    public function updateItemMaster(){
        //このアイテムから作られたItemMasterを更新する
    }
    //以下続く

}

ここで、もしItemのプロパティ $item_master_id と ItemMasterのプロパティ $item_id がお互い同じものを見ることになったとします。

つまり

Item $id=10, $item_master_id=1

ItemMaster $id=1, $item_id=10

文章にすると

「10番のアイテムは、アイテムマスター (Id:1) から作られた。」

「アイテムマスター (Id:1) は10番のアイテムから作られた。」

が両立します。すでに頭の中がぐちゃぐちゃになっちゃいますよね。とりあえず、次のような状態です。

「10番のアイテムができがよいので、コピペして作る。そこで、10番をマスターとして、アイテムマスター(Id:1)を作る。アイテムマスターから、11番、12番、13番のアイテムを作る。後日、誤操作で10番もアイテムマスター (Id:1)から作られたことになった。」

実際の問題①としては、では10番のアイテムを更新したとき、10番をもとに作られたアイテムマスター(Id:1)が更新され、そのアイテムマスターから作られたアイテム10番、11番、12番、13番のアイテムが更新されることになります

10番はそこで更新されているので、またアイテムマスター (Id:1) を更新します。 そのアイテムマスターから作られたアイテム10番、11番、12番、13番のアイテムが更新されます。10番が更新されたので…以下無限に続く。

という無限ループになってしまいます。

実際の問題②

実際の問題②として、ではアイテムマスターから生まれたアイテムをまたアイテムマスターにした時はどうでしょうか?実際の問題①を忘れて頂いて、初期状態になったとします。

「13番のアイテムもできがいいので、アイテムマスターにしよう。」

となった場合

「13番のアイテムをもとに、アイテムマスター(Id:2)を作る。アイテムマスター (Id:2) から、14番、15番のアイテムを作る。13番のアイテムは、アイテムマスター (Id:1)から作られており、 アイテムマスター (Id:1) はアイテム10番から作られている。」

こうなった場合、アイテム10番に更新をした場合、

「 アイテムマスター(Id:1)が更新される。 アイテムマスター(Id:1) から作られた11番、12番、13番が10番と同じ内容に更新される。13番が更新されたので、 アイテムマスター (Id:2) を更新する。 アイテムマスター (Id:2) から、14番、15番のアイテムを更新する。内容は、14番、15番は10番と同じ内容のアイテムになる。 」

ということになります。


この話は、絶対に循環参照がダメという話ではないです。やむを得ずこういう設計になってしまうこともあるでしょう(´ω`)。その場合、こういうリスクがありますよ。ということなので、上記の例でいえば

・絶対にアイテムマスターとアイテムは同じオブジェクトを参照しない

・アイテムマスターから作られたアイテムを、アイテムマスターにしない

などを気を付ければうまく使えるかもしれません。

複雑なオブジェクト イメージ
この世のものは全部複雑ですよねー。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です