簡體   English   中英

如何以優雅的方式初始化具有大量字段的類?

[英]How do I initialize classes with lots of fields in an elegant way?

在我的應用程序中,我必須實例化許多不同類型的對象。 每種類型都包含一些字段,需要添加到包含類型中。 我怎樣才能以優雅的方式做到這一點?

我當前的初始化步驟看起來像這樣:

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 ”? 你有沒有任何參考/例子來避免編寫這樣的代碼?

我真的很想實現干凈的代碼。


語境:

我正在開發一個RestClient應用程序,用於將發布請求發送到RestClient

API表示為xsd schema文件,我使用JAXB創建了所有對象

在發送請求之前,我必須實例化許多對象,因為它們彼此之間存在依賴關系。 (優惠有酒店,酒店有房間,房間有人......這些類是生成的)

謝謝你的幫助。

您可以使用構造函數或構建器模式構建器模式的變體來解決初始化步驟中包含太多字段的問題。

我將擴展你的例子以證明我為什么這些選項有用的觀點。

理解你的例子:

讓我們說, 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.
}

在您的示例中,要為這些字段設置值,您可以使用setter:

    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:構建器模式

如果您希望選擇為任何字段設置默認值,則構建器模式非常有用。

構建器模式解決的問題是以下亂碼:

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

}

看看它有多亂?

Builder模式是,你把你的類的類。

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:構建器模式的變化

您還可以通過簡單地使當前setter返回相同的Offer對象來使用構建器模式。 除了沒有額外的OfferBuilder類之外,它完全相同。

警告:正如用戶WW在下面所述,此選項打破了JavaBeans - 容器類(如Offer)的標准編程約定 因此,您不應將此用於專業目的,並應限制您在自己的實踐中使用。

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

最后一個報價只是一個包含所有默認值的報價。 其他是默認值,除了我設置的值。


因此,雖然這是很多工作,但如果要清理初始化步驟,則需要為每個包含字段的類使用其中一個選項。 然后使用我在每個方法中包含的初始化方法。

祝好運! 這有什么需要進一步解釋的嗎?

我總是喜歡使用builder-pattern-with-a-twist,因為它提供的不僅僅是構建器模式的基本方法。

但是當你想告訴用戶她必須調用一個構建器方法或另一個構建器方法時會發生什么,因為它對於你正在嘗試構建的類是至關重要的。

考慮一個URL組件的構建器。 如何考慮封裝對URL屬性的訪問的構建器方法,它們同樣重要,它們是否相互交互等等? 雖然查詢參數或片段是可選的,但主機名不是; 你可以說協議也是必需的,但為此你可以有一個有意義的默認值,比如http對嗎?

無論如何,我不知道這對你的特定問題是否有意義,但我認為值得一提的是讓其他人看看它。

這里已經給出了一些很好的回答!

我想到的另一個問題是Domain Driven Design 具體的構建塊部分,包括實體值對象聚合工廠等。

域驅動設計 - 快速 (pdf)給出了一個很好的介紹。

我只是提供這個答案,因為它在評論中提到過,我認為它也應該是這個枚舉設計模式的一部分。


空對象設計模式

意圖

Null對象的意圖是通過提供可替代的替代方法來封裝缺少對象,該替代方案提供合適的默認不執行任何行為。 簡而言之,一個“沒有任何東西將無所作為”的設計

使用Null對象模式時

  • 一個對象需要一個協作者。 Null對象模式不會引入此協作 - 它使用已存在的協作
  • 一些協作者實例應該什么都不做
  • 你想從客戶端抽象出null的處理

在這里,您可以找到“Null Object”設計模式的完整部分

理想情況下,對象不應該關心實例化其依賴項。 它應該只擔心它應該與它們做的事情。 您是否考慮過任何依賴注入框架? Spring或Google's Juice功能多樣,占地面積小。

這個想法很簡單,你聲明了依賴關系並讓框架決定何時/如何/在何處創建它們並將它“注入”你的類。

如果您不想使用任何框架,您可以從中獲取設計說明並嘗試模擬其設計模式並根據您的用例進行調整。

此外,您可以通過正確使用集合來在一定程度上簡化事物。 例如,什么樣的附加功能不Offers比存儲集合其他Offer 我不確定你的約束是什么,但是,如果你可以讓那部分更清潔一點,你將在你實例化對象的所有地方獲得巨大的收益。

Dozer框架提供了將值從ws對象復制到dto的好方法。 這是另一個例子 另外,如果getter / setter名稱與兩個類相同,則不需要自定義轉換器

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM