Первый вариант не так желателен, как вы думаете. Он не только проходит this
, тогда как объект не полностью инициализирован: this.wheel = new Wheel(this);
, в то время как здесь это не должно вызывать каких-либо проблем, поскольку это очень упрощенное использование, но эта опция также создает проблему проектирования: зависимость колеса жестко закодирована в конструкторе , Вы не можете переключить / смоделировать экземпляр зависимости.
В общем, двунаправленные отношения создают некоторые ограничения, которые вынуждают вас сделать некоторый компромисс дизайна: установка значения в два раза, отсутствие неизменности, по крайней мере, выставленный установщик, и так далее ... Обратите внимание, что вы можете справиться с некоторыми из этих вопросов, введя некоторые дополнительные абстракции.
Но для простоты здесь создание хотя бы одного из двух объектов «в одиночку» является правильным выбором, как показано в вашем втором варианте. Хотя вы могли бы сэкономить вызов части установки, если вы обрабатываете взаимоотношения в конструкторе:
Wheel wheel = new Wheel();
Bike bike = new Bike(wheel);
// wheel.setBike(bike); // not required now
Где велосипед определяется как:
Bike(Wheel wheel){
this.wheel = wheel;
wheel.setBike(this);
}
Чтобы пойти дальше, как объяснялось выше, вы можете уменьшить связь между двумя классами и способ связи двух экземпляров, введя некоторую абстракцию.
Например, вы можете представить интерфейс Bike и Wheel, который будет единственным способом, которым клиенты будут управлять Bike и Wheel. Таким образом, вы можете выполнить двустороннюю связь и даже определенный уровень неизменности с помощью внутренних реализаций, которые клиенты никогда не увидят (благодаря вложенной private
классы).
Когда вы разрабатываете API, это часто способ обеспечить некоторые инварианты и правила.
Идея заключается в том, что клиенты просто:
Bike bike = CarFactory.of("super bike", "hard wheel");
Wheel wheel = bike.getWheel();
Интерфейсы:
public interface Wheel {
String getBarProp();
Bike getBike();
}
public interface Bike {
String getFooProp();
Wheel getWheel();
}
И клиентский API, а не публичные реализации в CarFactory:
public class CarFactory {
private static class BikeDefaultImpl implements Bike {
private final String fooProp;
private Wheel wheel;
public BikeDefaultImpl(String fooProp) {
this.fooProp = fooProp;
}
@Override
public String getFooProp() {
return fooProp;
}
@Override
public Wheel getWheel() {
return wheel;
}
}
private static class WheelDefaultImpl implements Wheel {
private final String barProp;
private Bike bike;
public WheelDefaultImpl(String barProp) {
this.barProp = barProp;
}
@Override
public String getBarProp() {
return barProp;
}
@Override
public Bike getBike() {
return bike;
}
}
public static Bike of(String bikeProp, String wheelProp) {
BikeDefaultImpl bike = new BikeDefaultImpl(bikeProp);
WheelDefaultImpl wheel = new WheelDefaultImpl(wheelProp);
bike.wheel = wheel;
wheel.bike = bike;
return bike;
}
}
Вы также заметили бы, что использование this
в конструкторе больше не требуется таким образом, потому что мы можем свободно обращаться к полям реализации Bike и Wheel во внутреннем.