Подтвердить что ты не робот

Как инициализировать классы с большим количеством полей в элегантном стиле?

В моем приложении мне нужно создать много разных типов объектов. Каждый тип содержит некоторые поля и должен быть добавлен к содержащему типу. Как я могу сделать это элегантным способом?

Мой текущий шаг инициализации выглядит примерно так:

public void testRequest() {

        //All these below used classes are generated classes from xsd schema file.

        CheckRequest checkRequest = new CheckRequest();

        Offers offers = new Offers();
        Offer offer = new Offer();
        HotelOnly hotelOnly = new HotelOnly();
        Hotel hotel = new Hotel();
        Hotels hotels = new Hotels();
        Touroperator touroperator = new Touroperator();
        Provider provider = new Provider();
        Rooms rooms = new Rooms();
        Room room = new Room();
        PersonAssignments personAssignments = new PersonAssignments();
        PersonAssignment personAssignment = new PersonAssignment(); 
        Persons persons = new Persons();
        Person person = new Person();
        Amounts amounts = new Amounts();

        offers.getOffer().add(offer);
        offer.setHotelOnly(hotelOnly);

        room.setRoomCode("roomcode");
        rooms.getRoom().add(room);

        hotels.getHotel().add(hotel);
        hotel.setRooms(rooms);

        hotelOnly.setHotels(hotels);

        checkRequest.setOffers(offers);

        // ...and so on and so on
    } 

Я действительно хочу избежать написания кода, подобного этому, потому что немного беспорядочно создавать экземпляр каждого объекта отдельно, а затем инициализировать каждое поле через несколько строк кода (например, нужно вызвать new Offer(), а затем setHotelOnly(hotelOnly), а затем add(offer)).

Какие элегантные методы я могу использовать вместо того, что у меня есть? Можно ли использовать "Factories"? Есть ли у вас какие-либо ссылки/примеры, чтобы избежать написания такого кода?

Мне действительно интересно реализовать чистый код.


Context:

Я разрабатываю приложение RestClient для отправки почтовых запросов в Webservice.

API представлен как файл xsd schema, и я создал все объекты с JAXB

Перед отправкой запроса мне нужно создать экземпляр многих объектов, потому что они имеют зависимости друг с другом. (В отеле есть отели, в отеле есть номера, в номере есть люди... И эти классы являются сгенерированными)

Спасибо за вашу помощь.

4b9b3361

Ответ 1

Вы можете использовать конструктор или шаблон конструктора или вариант шаблона построителя, чтобы исправить проблему наличия слишком большого количества полей в вашем шаг инициализации.

Я немного расширю ваш пример, чтобы доказать свою точку зрения, почему эти параметры полезны.

Понимание вашего примера:

Допустим, что Offer - это просто контейнерный класс для 4 полей:

public class Offer {
    private int price;
    private Date dateOfOffer;
    private double duration;
    private HotelOnly hotelOnly;
    // etc. for as many or as few fields as you need

    public int getPrice() {
        return price;
    }

    public Date getDateOfOffer() {
        return dateOfOffer;
    }

    // etc.
}

Как показано в вашем примере, чтобы установить значения в эти поля, вы используете сеттеры:

    public void setHotelOnly(HotelOnly hotelOnly) {
        this.hotelOnly = hotelOnly;
    }

К сожалению, это означает, что если вам нужно предложение со значениями во всех полях, вы должны сделать то, что у вас есть:

Offers offers = new Offers();
Offer offer = new Offer();
offer.setPrice(price);
offer.setDateOfOffer(date);
offer.setDuration(duration);
offer.setHotelOnly(hotelOnly);
offers.add(offer);

Теперь посмотрим на улучшение этого.

Вариант 1: Конструкторы!

Конструктор, отличный от конструктора по умолчанию (конструктор по умолчанию в настоящее время Offer()), полезен для инициализации значений полей в вашем классе.

Версия Offer с использованием конструкторов будет выглядеть так:

public class Offer {
    private int price;
    private Date dateOfOffer;
    //etc.

    // CONSTRUCTOR
    public Offer(int price, Date dateOfOffer, double duration, HotelOnly hotelOnly) {
        this.price = price;
        this.dateOfOffer = dateOfOffer;
        //etc.
    }

    // Your getters and/or setters
}

Теперь мы можем инициализировать его в одной строке!

Offers offers = new Offers();
Offer offer = new Offer(price, date, duration, hotelOnly);
offers.add(offer);

Еще лучше, если вы никогда не используете Offer, кроме этой отдельной строки: offers.add(offer); вам даже не нужно сохранять его в переменной!

Offers offers = new Offers();
offers.add( new Offer(price, date, duration, hotelOnly) ); // Works the same as above

Вариант 2: шаблон построителя

A шаблон компоновщика полезен, если вы хотите иметь значения по умолчанию для любого из ваших полей.

Проблема, которую решает шаблон строителя, - это следующий беспорядочный код:

public class Offer {
    private int price;
    private Date dateOfOffer;
    // etc.

    // The original constructor. Sets all the fields to the specified values
    public Offer(int price, Date dateOfOffer, double duration, HotelOnly hotelOnly) {
        this.price = price;
        this.dateOfOffer = dateOfOffer;
        // etc.
    }

    // A constructor that uses default values for all of the fields
    public Offer() {
        // Calls the top constructor with default values
        this(100, new Date("10-13-2015"), 14.5, new HotelOnly());
    }

    // A constructor that uses default values for all of the fields except price
    public Offer(int price) {
        // Calls the top constructor with default values, except price
        this(price, new Date("10-13-2015"), 14.5, new HotelOnly());
    }

    // A constructor that uses default values for all of the fields except Date and HotelOnly
    public Offer(Date date, HotelOnly hotelOnly) {
        this(100, date, 14.5, hotelOnly);
    }

    // A bunch more constructors of different combinations of default and specified values

}

Посмотрите, как это может получиться?

Шаблон компоновщика - это еще один класс, который вы помещаете в свой класс.

public class Offer {
    private int price;
    // etc.

    public Offer(int price, ...) {
        // Same from above
    }

    public static class OfferBuilder {
        private int buildPrice = 100;
        private Date buildDate = new Date("10-13-2015");
        // etc. Initialize all these new "build" fields with default values

        public OfferBuilder setPrice(int price) {
            // Overrides the default value
            this.buildPrice = price;

            // Why this is here will become evident later
            return this;
        }

        public OfferBuilder setDateOfOffer(Date date) {
            this.buildDate = date;
            return this;
        }

        // etc. for each field

        public Offer build() {
            // Builds an offer with whatever values are stored
            return new Offer(price, date, duration, hotelOnly);
        }
    }
}

Теперь вам не нужно иметь так много конструкторов, но все же можете выбрать, какие значения вы хотите оставить по умолчанию, и которые вы хотите инициализировать.

Offers offers = new Offers();
offers.add(new OfferBuilder().setPrice(20).setHotelOnly(hotelOnly).build());
offers.add(new OfferBuilder().setDuration(14.5).setDate(new Date("10-14-2015")).setPrice(200).build());
offers.add(new OfferBuilder().build());

Это последнее предложение просто одно со всеми значениями по умолчанию. Остальные - значения по умолчанию, кроме тех, которые я установил.

Посмотрите, как это облегчает работу?

Вариант 3: Изменение шаблона Builder

Вы также можете использовать шаблон построителя, просто сделав свои текущие сеттеры возвратом одного и того же объекта Предложения. Это точно так же, за исключением без дополнительного класса OfferBuilder.

Предупреждение. В качестве пользовательских WW-состояний ниже, эта опция ломает JavaBeans - стандартное соглашение о программировании для контейнерных классов, таких как Предложение. Таким образом, вы не должны использовать это для профессиональных целей и должны ограничивать свое использование в своих собственных практиках.

public class Offer {
    private int price = 100;
    private Date date = new Date("10-13-2015");
    // etc. Initialize with default values

    // Don't make any constructors

    // Have a getter for each field
    public int getPrice() {
        return price;
    }

    // Make your setters return the same object
    public Offer setPrice(int price) {
        // The same structure as in the builder class
        this.price = price;
        return this;
    }

    // etc. for each field

    // No need for OfferBuilder class or build() method
}

И ваш новый код инициализации

Offers offers = new Offers();
offers.add(new Offer().setPrice(20).setHotelOnly(hotelOnly));
offers.add(new Offer().setDuration(14.5).setDate(new Date("10-14-2015")).setPrice(200));
offers.add(new Offer());

Это последнее предложение просто одно со всеми значениями по умолчанию. Остальные - значения по умолчанию, кроме тех, которые я установил.


Итак, хотя вам очень нужна работа, если вы хотите очистить свой шаг инициализации, вам нужно использовать один из этих параметров для каждого из ваших классов, в которых есть поля. Затем используйте методы инициализации, которые я включил в каждый метод.

Удачи! Любое из этого нуждается в дополнительном объяснении?

Ответ 2

Я всегда предпочитал использовать builder-pattern-with-a-twist, потому что он обеспечивает гораздо больше, чем базовый подход шаблона построителя.

Но что происходит, когда вы хотите сказать пользователю, что она должна называть один метод-строитель или другой, так как он имеет решающее значение для класса, который вы пытаетесь создать.

Подумайте о построителе для компонента URL. Как можно подумать о методах построения для инкапсуляции доступа к атрибутам URL-адреса, являются ли они одинаково важными, взаимодействуют ли они друг с другом и т.д.? Хотя параметры запроса или фрагмент являются необязательными, имя хоста не указано; вы можете сказать, что протокол также требуется, но для этого вы можете иметь значимый дефолт, например, http-право?

В любом случае, я не знаю, имеет ли это смысл для вашей конкретной проблемы, но я подумал, что стоит упомянуть, чтобы другие взглянули на нее.

Ответ 3

Некоторые приятные ответы уже приведены здесь!

Что пришло мне в голову, так как добавление Domain Driven Design. Конкретная часть Building blocks, с Entity, Value Object, Aggregate, Factory и т.д.

Хорошее введение дано в Домен Driven Design - быстро (pdf).

Ответ 4

Я просто предоставляю этот ответ, потому что он упоминается в комментарии, и я думаю, что он также должен быть частью этого перечисления шаблонов проектирования.


Шаблон проектирования нулевых объектов

Намерение

Цель Null Object - инкапсулировать отсутствие объекта, предоставляя заменяемую альтернативу, которая предлагает подходящие значения по умолчанию, ничего не делает. Короче говоря, дизайн, в котором "ничего не будет ничего"

Используйте шаблон Null Object, если

  • для объекта требуется соавтор. Шаблон Null Object не представляет этого сотрудничества - он использует уже существующее сотрудничество.
  • некоторые экземпляры соавторов ничего не должны делать
  • вы хотите абстрагироваться от обработки null от клиента

Здесь вы найдете полную часть шаблона проектирования Null Object

Ответ 5

В идеале, объект не должен заботиться о создании экземпляров его зависимостей. Он должен только беспокоиться о вещах, которые он должен делать с ними. Рассматривали ли вы какие-либо рамки внедрения инъекций? Spring или Google Juice довольно универсальны и имеют небольшой след.

Идея проста: вы объявляете зависимости и позволяете фреймворку решать, когда/как/где их создавать и "вводить" в свои классы.

Если вы не хотите использовать какую-либо фреймворк, вы можете взять на себя проектные заметки и попытаться подражать их шаблонам проектирования и настроить его для своего использования.

Кроме того, вы можете упростить вещи в определенной степени, используя надлежащее использование Коллекций. Например, какая дополнительная функция имеет Offers, кроме хранения коллекции Offer? Я не уверен, каковы ваши ограничения, но если вы можете сделать эту часть немного более чистым, у вас будет массивная прибыль во всех местах, где вы создаете объекты.

Ответ 6

Структура Dozer обеспечивает хороший способ копирования значений с объекта ws на ваш dto. Вот еще один пример. Кроме того, если имена getter/setter одинаковы для обоих классов, вам не нужен специальный конвертер