Двунаправленные ссылки на объекты Java, использующие это в конструкторе - PullRequest
1 голос
/ 10 мая 2019

Я пытаюсь создать и объект, и компонент объекта, которые имеют двунаправленные ссылки друг на друга.В этом примере у меня есть класс Bike и класс Wheel.Один из вариантов, который я рассмотрел (Вариант 1), - создать велосипед для создания велосипеда, а затем передать ссылку на себя в его конструкторе.Тем не менее, я прочитал, что не должен передавать «this» вне конструктора, и что лучше создать объект Wheel вне конструктора Bike.Поэтому сначала я должен создать Wheel, затем передать его в Bike, а затем вызвать метод setBike () для Wheel (вариант 2).Мне кажется, вариант 1 - это самый простой способ создания двунаправленных ссылок между велосипедом и колесом, но он также нарушает некоторые принципы проектирования.Может кто-нибудь объяснить, почему вариант 2 предпочтительнее, чем вариант 1?

Вариант 1:

public class Wheel {

    private Bike bike;

    Wheel(Bike bike) {
        this.bike = bike;
    }
}

public class Bike {

    private Wheel wheel;

    Bike() {
        this.wheel = new Wheel(this);
    }
}

Bike bike = new Bike();

Вариант 2:

public class Wheel {

    private Bike bike;

    public void setBike(Bike bike) {
        this.bike = bike;
    }
}

public class Bike {

    private Wheel wheel;

    Bike(Wheel wheel) {
        this.wheel = wheel;
    }
}

Wheel wheel = new Wheel();
Bike bike = new Bike(wheel);
wheel.setBike(bike);

1 Ответ

2 голосов
/ 10 мая 2019

Первый вариант не так желателен, как вы думаете. Он не только проходит 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 во внутреннем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...