在面向对象中处理双向关联的最佳方法


Best way to handle a Bidirectional Association in OOP

我想知道在O.O.P.中处理双向关联的最佳方法。我在Google和SO上找到了多个解决方案,但它们中的每一个似乎都有缺点。语言无关紧要,但让我们用PHP来说明我的意思:

假设我有一个简单的States. [1..n]..城市协会:

public class State {
    public $cities;
    public function add_city($city) {}
}
public class City {
    public $state;
    public function set_state($state) {}
}
实现# 1:

public class State {
    public $cities;
    public function add_city($city) {
        $this->cities[] = $city;
        $city->state = $this;
    }
}
public class City {
    public $state;
    public function set_state($state) {
        $this->state = $state;
        $state->cities[] = $state;
    }
}
这个实现的两个问题是:
  • "$state"answers"$cities"必须是公共的(所以任何人都可以添加城市而不使用公共函数add_city…)。没有所谓的"朋友"类"的概念。
  • 公共函数在添加
  • 之前可能需要做一些操作。
实现# 2:

public class State {
    public $cities;
    public function add_city($city) {
        $this->cities[] = $city;
        if ($city->state != $this) {
            $city->set_state($this);
        }
    }
}
public class City {
    public $state;
    public function set_state($state) {
        $this->state = $state;
        if (!in_array($this, $state->cities)) {
            $state->add_city($this);
        }
    }
}

比#1稍微好一点,但是"set_state"函数必须调用"in_array",在必须的语言中是O(n)(将快速的O(1)操作变成O(n)操作)

实现# 3:

public class State {
    public $cities;
    public function add_city($city, $call_the_other_function = true) {
        $this->cities[] = $city;
        if ($call_the_other_function) {
            $city->set_state($this, false);
        }
    }
}
public class City {
    public $state;
    public function set_state($state, $call_the_other_function = true) {
        $this->state = $state;
        if ($call_the_other_function) {
            $state->add_city($this, false);
        }
    }
}

实现#3非常有效,但有点"丑陋"(因为没有更好的术语),因为额外的可选参数

无论如何,如果有人知道什么是"正确的方式",我很想知道。

编辑:如果可能的话,我想要一个解决方案:

  • 不使用其他类
  • 不知道对象创建的顺序(即不是"构造函数"解决方案)

我会尝试使用构造函数,这样,当你创建一个城市时,你可以直接传递它的状态。

public class State {
    private $cities;
    public function add_city($city) {
        $this->cities[] = $city;
    }
}
public class City {
    private $state;
    function __construct($state) {
        $state->add_city($this)
        $this->state=$state
    }
}

在您的所有提案中,City都知道State中的方法,反之亦然。如果您引入第三个类,负责将城市与州(例如LocationService)连接起来,并使用一种方法(例如linkCityToState),情况会如何?有了这个,以后可以用linkCityToCountry或一些高级逻辑(如getPostalCodeFromApi)扩展它。

如果你担心性能,把你的列表转换成哈希集,这将减少查找复杂性到仅仅O(log n)

同样在你的实现#1你有city->cities…?

在任何情况下,我都不会使用解决方案#3,除非我正在编写压缩算法,驱动程序,大量数据库查询等。