如何以优雅的方式初始化具有大量字段的类? [英] How do I initialize classes with lots of fields in an elegant way?

查看:207
本文介绍了如何以优雅的方式初始化具有大量字段的类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用程序中,我必须实例化许多不同类型的对象。每种类型都包含一些字段,需要添加到一个包含类型。我如何以优雅的方式做这个?



我当前的初始化步骤看起来像这样:

  public void testRequest(){

//以下所有使用的类都是来自xsd模式文件的生成类。

CheckRequest checkRequest = new CheckRequest();

优惠=新优惠();
优惠优惠=新优惠();
HotelOnly hotelOnly = new HotelOnly();
酒店hotel = new酒店();
酒店酒店=新酒店();
Touroperator touroperator = new Touroperator();
提供商provider = new Provider();
房间房=新房间();
房间=新房间();
PersonAssignments personAssignments = new PersonAssignments();
PersonAssignment personAssignment = new PersonAssignment();
人员=新人();
person person = new Person();
Amounts amount = 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);

// ...等等
}

我真的想避免编写这样的代码,因为它有点混乱,必须分别实例化每个对象,然后初始化每个字段跨多行代码(例如,必须调用 new Offer(),然后 setHotelOnly(hotelOnly),然后 add(offer))。

我可以使用什么优雅的方法,而不是我有什么?是否有任何工厂可以使用?你有任何参考/例子,以避免编写这样的代码吗?



我真的有兴趣实现干净的代码。






上下文:



我正在开发一个 RestClient



API表示为 xsd模式文件,所有对象与 JAXB



在发送请求之前,我必须实例化多个对象,因为他们有彼此的依赖。



p>感谢您的帮助。

解决方案

您可以使用构造函数或 builder模式或变体的构建器模式来解决在初始化步骤中有太多字段的问题。



我将扩展你的例子,以证明我的观点,为什么这些



c $ c> Offer 只是4个字段的容器类:

  public class Offer {
private int price;
private Date dateOfOffer;
private double duration;
private酒店
//等于你需要的字段数量。

public int getPrice(){
return price;
}

public Date getDateOfOffer(){
return dateOfOffer;
}

//等
}

如您在示例中所示,要为这些字段设置值,请使用setters:

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

不幸的是,这意味着如果您需要在所有字段,你必须做你有的:

 优惠=新优惠(); 
优惠优惠=新优惠();
offer.setPrice(price);
offer.setDateOfOffer(date);
offer.setDuration(duration);
offer.setHotelOnly(hotelOnly);
offers.add(offer);

现在让我们来看看改善。



选项1:构造函数!



默认构造函数之外的构造函数



提供的版本$ c>使用构造函数看起来像这样:

  public class Offer {
private int price;
private Date dateOfOffer;
//等。

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

//您的getters和/或setters
}

现在,我们可以在一行中初始化它!

  
优惠优惠=新优惠(价格,日期,持续时间,hotelOnly);
offers.add(offer);

更棒的是,如果你从不使用除了单行之外: offers.add(offer); 你甚至不需要将它保存在变量中!

 优惠优惠=新优惠(); 
offers.add(new Offer(price,date,duration,hotelOnly)); //工作方式与上面一样

选项2:Builder模式 p>

A 生成器模式是非常有用的,如果你想要任何字段有默认值的选项。



构建器模式解决的问题是以下杂乱代码:

  public class Offer {
private int price ;
private Date dateOfOffer;
//等等。

//原始构造函数。将所有字段设置为指定的值
public Offer(int price,Date dateOfOffer,double duration,HotelOnly hotelOnly){
this.price = price;
this.dateOfOffer = dateOfOffer;
//等
}

//使用所有字段的默认值的构造函数
public Offer(){
// Calls默认值为
的顶层构造函数this(100,new Date(10-13-2015),14.5,new HotelOnly());
}

//使用除价格之外的所有字段的默认值的构造函数
public Offer(int price){
//调用顶层构造函数(价格,新日期(10-13-2015),14.5,新HotelOnly());除非价格

}

//使用除Date和HotelOnly之外的所有字段的默认值的构造函数
public Offer(日期日期,HotelOnly hotelOnly){
this 100,日期,14.5,hotelOnly);
}

//一组更多的默认值和指定值的不同组合的构造函数

}

查看可以获得多么凌乱?



构建器模式是另一个类, 您的课程。

  public class Offer {
private int price;
//等

public Offer(int price,...){
//与上面相同
}

public static class OfferBuilder {
private int buildPrice = 100;
private Date buildDate = new Date(10-13-2015);
//等用默认值初始化所有这些新的构建字段

public OfferBuilder setPrice(int price){
//覆盖默认值
.buildPrice = price;

//为什么这里会变得很明显
return this;
}

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

//等每个字段

public Offer build(){
//构建一个包含存储值的提议
return new Offer(price,date,duration,hotelOnly);
}
}
}

有这么多的构造函数,但仍然能够选择你想要保留的值,你想要初始化。

 优惠优惠=新优惠(); 
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());

最后一个报价只是一个带有所有默认值的报价。



看看如何使事情更容易?



strong>选项3:构建器模式的变化



您还可以通过简单地使当前setter返回相同的Offer对象来使用构建器模式。它完全一样,除了没有额外的 OfferBuilder 类。



警告:As 以下WW用户状态,此选项可打断 JavaBeans - 容器类的标准编程惯例,例如Offer

  public class Offer { 
private int price = 100;
private Date date = new Date(10-13-2015);
//等用默认值初始化

//不要任何构造函数

//为每个字段都有一个getter
public int getPrice(){
return price;
}

//让你的setters返回相同的对象
public Offer setPrice(int price){
//与生成器类相同的结构
this.price = price;
return this;
}

//等每个字段

//不需要OfferBuilder类或build()方法
}

您的新初始化代码是

 优惠优惠=新优惠(); 
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());

最后一个报价只是一个带有所有默认值的报价。其他是我设置的默认值。






所以,虽然这是很多工作,要清理初始化步骤,您需要为其中包含字段的每个类使用这些选项之一。然后使用每个方法包含的初始化方法。



祝你好运!这是否需要进一步解释?


In my application, I have to instantiate many different types of objects. Each type contains some fields and needs to be added to a containing type. How can I do this in an elegant way?

My current initialization step looks something like this:

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
    } 

I really want to avoid writing code like this, because it's a little messy having to instantiate each object separately and then initialize each field across multiple lines of code (e.g. having to call new Offer() and then setHotelOnly(hotelOnly) and then add(offer)).

What elegant methods can I use instead of what I have? Are there any "Factories" that can be used? Do you have any references/examples to avoid writing code like this?

I'm really interested in implementing clean code.


Context:

I'm developing a RestClient Application for sending post requests to a Webservice.

The API is represented as a xsd schema file and I created all the Objects with JAXB

Before sending a request I have to instantiate many Objects because they have dependencies with each other. (An Offer has Hotels, a Hotel has Rooms, a Room has Persons... And these Classes are the generated ones)

Thanks for your help.

解决方案

You can either use a constructor or a builder pattern or a variation of the builder pattern to fix the problem of having too many fields in your initialization step.

I'm going to extend your example a bit to prove my point of why these options are useful.

Understanding your example:

Lets say an Offer is simply a container class for 4 fields:

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

As it stands in your example, to set values to these fields, you use setters:

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

Unfortunately, this means if you need an offer with values in all of the fields, you have to do what you have:

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

Now let's look at improving this.

Option 1: Constructors!

A constructor other than the default constructor (the default constructor is currently Offer() ) is useful for initializing the values of the fields in your class.

A version of Offer using constructors would look like this:

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
}

Now, we can initialize it in one line!

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

Even better, if you never use offer other than that single line: offers.add(offer); you don't even need to save it in a variable!

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

Option 2: Builder Pattern

A builder pattern is useful if you want the option of having default values for any of your fields.

The problem a builder pattern solves is the following messy code:

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

}

See how messy that can get?

The builder pattern is another class that you put inside your class.

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

Now, you can not have to have so many constructors, but still are able to choose which values you want to leave default, and which you want to initialize.

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

That last offer is simply one with all default values. The others are default values except the ones that I set.

See how that makes things easier?

Option 3: Variation of Builder Pattern

You can also use the builder pattern by simply making your current setters return the same Offer object. It's exactly the same, except without the extra OfferBuilder class.

Warning: As user WW states below, this option breaks JavaBeans - a standard programming convention for container classes such as Offer. So, you shouldn't use this for professional purposes, and should limit your use in your own practices.

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
}

And your new initialization code is

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

That last offer is simply one with all default values. The others are default values except the ones that I set.


So, while it's a lot of work, if you want to clean up your initialization step, you need to use one of these options for each of your classes that have fields in them. Then use the initialization methods that I included with each method.

Good luck! Does any of this need further explanation?

这篇关于如何以优雅的方式初始化具有大量字段的类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆