Jak w elegancki sposób zainicjować klasy z wieloma polami?

W mojej aplikacji muszę tworzyć instancje wielu różnych typów obiektów. Każdy typ zawiera pola i musi zostać dodany do typu zawierającego. Jak mogę to zrobić w elegancki sposób?

Mój bieżący krok inicjalizacji wygląda mniej więcej tak:

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
    } 

Naprawdę chcę uniknąć pisania kodu w ten sposób, ponieważ jest to trochę niechlujne konieczności tworzenia instancji każdego obiektu osobno, a następnie inicjalizacji każdego pola w wielu liniach kodu (np. konieczności wywołania new Offer(), a następnie setHotelOnly(hotelOnly) a następnie add(offer)).

Jakich eleganckich metod mogę użyć zamiast tego, co mam? Czy są jakieś "Factories", które można użyć? Czy masz jakieś referencje/przykłady, aby uniknąć pisania takiego kodu?

Jestem naprawdę zainteresowany implementacją czystego kodu.


Kontekst:

Opracowuję RestClient aplikację do wysyłania zapytań post do Webservice.

API jest reprezentowane jako plik xsd schema i stworzyłem wszystkie obiekty za pomocą JAXB

Przed wysłaniem Prośba muszę utworzyć instancję wielu obiektów, ponieważ mają ze sobą zależności. (oferta ma hotele, Hotel ma pokoje, pokój ma osoby... I te klasy są generowane)

Dzięki za pomoc.
Author: Patrick, 2015-10-13

6 answers

Możesz użyć konstruktora lub wzorca konstruktora lub wariantu wzorca konstruktora, aby rozwiązać problem posiadania zbyt wielu pól w kroku inicjalizacji.

Zamierzam rozszerzyć twój przykład trochę, aby udowodnić mój punkt, dlaczego te opcje są przydatne.

Zrozumienie twojego przykładu:

Powiedzmy, że Offer jest po prostu klasą kontenera dla 4 pól:

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.
}

W twoim przykładzie, aby ustawić wartości dla tych pól, używasz seterów:

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

Niestety, oznacza to, że jeśli potrzebujesz oferty z wartościami we wszystkich polach, musisz zrobić to, co masz:

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

Spójrzmy teraz na poprawę tego.

Wariant 1: Konstruktorzy!

Konstruktor inny niż domyślny (konstruktor domyślny to obecnie Offer()) jest przydatny do inicjalizacji wartości pól w twojej klasie.

Wersja Offer z użyciem konstruktorów wyglądałaby następująco to:

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
}

Teraz możemy zainicjować to w jednej linii!

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

Nawet lepiej, jeśli nigdy nie używasz offer innej niż ta pojedyncza linia: offers.add(offer); nie musisz nawet zapisywać jej w zmiennej!

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

Opcja 2: Wzorzec Konstruktora

Wzorzec konstruktora jest przydatny, jeśli chcesz mieć opcję domyślnych wartości dla dowolnego z twoich pól.

Problem, który rozwiązuje wzorzec konstruktora, to następujący bałagan w kodzie:

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

}

Zobacz jak bałagan, który może być?

Wzorzec budowniczy to kolejna klasa, którą wkładasz do swojej klasy.

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);
        }
    }
}

Teraz nie musisz mieć tak wielu konstruktorów, ale nadal możesz wybrać, które wartości chcesz pozostawić domyślne, a które chcesz zainicjować.

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());

Ta ostatnia oferta jest po prostu taka z wszystkimi domyślnymi wartościami. Pozostałe są wartościami domyślnymi, z wyjątkiem tych, które ustawiłem.

Widzisz, jak to ułatwia sprawę?

Wariant 3: Zmiana wzoru budowniczego

Możesz również użyć wzorca builder, po prostu sprawiając, że Twoje obecne setery zwrócą ten sam obiekt oferty. Jest dokładnie taki sam, tylko bez dodatkowej klasy OfferBuilder.

Ostrzeżenie: jak użytkownik WW stwierdza poniżej, ta opcja łamie JavaBeans-standardową konwencję programowania dla klas kontenerów, takich jak Offer. Nie powinieneś więc używać tego do celów zawodowych i powinieneś ograniczyć swoje użycie we własnym zakresie praktyki.

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
}

A Twój nowy kod inicjalizacyjny to

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());

Ta ostatnia oferta jest po prostu taka z wszystkimi domyślnymi wartościami. Pozostałe są wartościami domyślnymi, z wyjątkiem tych, które ustawiłem.


Więc, chociaż jest to dużo pracy, jeśli chcesz wyczyścić swój krok inicjalizacji, musisz użyć jednej z tych opcji dla każdej z klas, które mają pola w nich. Następnie użyj metod inicjalizacji, które dołączałem do każdej metody.

Powodzenia! Czy którakolwiek z to wymaga dalszych wyjaśnień?
 44
Author: snickers10m,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:22

Zawsze wolałem używać builder-pattern-with-a-twist ponieważ zapewnia znacznie więcej niż podstawowe podejście do builder pattern.

Ale co się dzieje, gdy chcesz powiedzieć użytkownikowi, że musi wywołać jedną lub drugą metodę builder, ponieważ jest to kluczowe dla klasy, którą próbujesz zbudować.

Pomyśl o budowniczym komponentu URL. Jak można by pomyśleć o metodach builder do enkapsulacji dostępu do atrybutów URL, czy są one równie ważne, czy wchodzą ze sobą w interakcję itp.? Podczas gdy parametry zapytania lub fragment są opcjonalne, Nazwa hosta nie jest; można powiedzieć, że protokół jest również wymagany, ale do tego można mieć znaczące domyślne, jak http prawda?

W każdym razie, Nie wiem, czy to ma sens dla Twojego konkretnego problemu, ale pomyślałem, że warto wspomnieć o innych, aby spojrzeć na to.

 10
Author: Filip,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-22 22:43:33

kilka miłych odpowiedzi już tu podano!

To, co przyszło mi do głowy jako dodatek, to Domain Driven Design . Konkretna część bloków konstrukcyjnych , z podmiotem, obiekt Value, Agregat, Fabryka itd.

[[0]} miłe wprowadzenie znajduje się w Domain Driven Design-Quickly (pdf).
 3
Author: Verhagen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-21 08:23:08

po prostu podaję tę odpowiedź, ponieważ została ona wspomniana w komentarzu I myślę, że powinna być również częścią tego wyliczenia wzorców projektowych.


Null Obiekt Design Pattern

Intent

Intencją obiektu Null jest hermetyzacja braku obiektu poprzez dostarczenie zastępowalnej alternatywy, która oferuje odpowiednie domyślne zachowanie do nothing. W skrócie, projekt, w którym "nic nie przyjdzie z niczego"

Użyj obiektu Null pattern when

  • obiekt wymaga kolaboranta. Wzorzec obiektu Null nie wprowadza tej współpracy-wykorzystuje współpracę, która już istnieje
  • niektóre instancje kolaborantów nie powinny nic robić
  • chcesz abstrakcji obsługi null z dala od klienta

Tutaj znajdziesz pełną część wzorca projektowego "Null Object"

 1
Author: Patrick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-21 12:31:07

Idealnie, obiekt nie powinien martwić się o tworzenie instancji jego zależności. Powinien martwić się tylko o rzeczy, które ma z nimi zrobić. Czy zastanawiałeś się nad jakąkolwiek strukturą wtrysku zależności? Spring lub Google ' s Juice {[4] } są dość wszechstronne i mają niewielką powierzchnię.

Pomysł jest prosty, deklarujesz zależności i pozwalasz frameworkowi decydować kiedy/jak / gdzie je utworzyć i 'wstrzykiwać' to do Twoich klas.

Jeśli nie chcesz używać żadnego framework, możesz pobierać z nich notatki projektowe i próbować naśladować ich wzorce projektowe i dostosować je do swojego przypadku użycia.

Ponadto, można uprościć rzeczy do pewnego stopnia poprzez odpowiednie wykorzystanie zbiorów. Na przykład, jakie dodatkowe funkcje Offers mają inne niż przechowywanie kolekcji Offer? Nie jestem pewien, jakie są Twoje ograniczenia, ale jeśli możesz sprawić, że ta część będzie bardziej czystsza, będziesz miał ogromne zyski we wszystkich miejscach, w których tworzysz obiekty.

 1
Author: Prasoon Joshi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-22 10:45:31

Framework Dozer zapewnia przyjemny sposób kopiowania wartości z obiektu ws do twojego dto. Oto kolejny Przykład. Dodatkowo, jeśli nazwy getter/setter są takie same dla obu klas, nie potrzebujesz niestandardowego konwertera

 0
Author: HRgiger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-22 14:54:01