java 如何以优雅的方式初始化具有大量字段的类?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/33106041/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I initialize classes with lots of fields in an elegant way?
提问by Patrick
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)
).
我真的很想避免编写这样的代码,因为必须分别实例化每个对象,然后跨多行代码初始化每个字段(例如,必须调用new Offer()
然后setHotelOnly(hotelOnly)
然后调用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?
我可以使用哪些优雅的方法来代替我拥有的方法?有没有Factories
可以使用的“ ”?您是否有任何参考资料/示例可以避免编写这样的代码?
I'm really interested in implementing clean code.
我真的对实现干净的代码很感兴趣。
Context:
语境:
I'm developing a RestClient
Application for sending post requests to a Webservice.
我正在开发一个RestClient
用于向 Web 服务发送发布请求的应用程序。
The API is represented as a xsd schema
file and I created all the Objects with JAXB
API 表示为一个xsd schema
文件,我创建了所有对象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.
谢谢你的帮助。
回答by snickers10m
You can either use a constructor or a builder patternor 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:
假设 anOffer
只是一个包含 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.
}
As it stands in your example, to set values to these fields, you use setters:
在您的示例中,要为这些字段设置值,请使用 setter:
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!
选项 1:构造函数!
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.
默认构造函数以外的构造函数(当前默认构造函数为Offer()
)对于初始化类中的字段值很有用。
A version of Offer
using constructors would look like this:
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
}
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!
更好的是,如果您只使用offer
那一行:offers.add(offer);
您甚至不需要将其保存在变量中!
Offers offers = new Offers();
offers.add( new Offer(price, date, duration, hotelOnly) ); // Works the same as above
Option 2: Builder Pattern
选项 2:构建器模式
A builder patternis 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 insideyour class.
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);
}
}
}
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
选项 3:构建器模式的变化
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.
您还可以通过简单地让当前的 setter 返回相同的 Offer 对象来使用构建器模式。除了没有额外的OfferBuilder
课程外,它完全相同。
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.
警告:正如下面用户 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
}
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?
祝你好运!这是否需要进一步解释?
回答by Filip
I've always preferred using builder-pattern-with-a-twistbecause it provides much more than the basic approach of the builder pattern.
我一直更喜欢使用builder-pattern-with-a-twist,因为它提供的不仅仅是构建器模式的基本方法。
But what happens when you want to tell the user that she must call one builder method or the other, since it is crucial for the class you're trying to build.
但是当您想告诉用户她必须调用一个或另一个构建器方法时会发生什么,因为它对您尝试构建的类至关重要。
Think about a builder for a URL component. How would one think about the builder methods for encapsulating access to URL attributes, are they equally important, do they interact with each other, etc? While the query parameters or fragment are optional the hostname is not; you could say that protocol is also required but for that you can have a meaningful default, like httpright?
考虑一个 URL 组件的构建器。人们会如何看待封装对 URL 属性的访问的构建器方法,它们是否同样重要,它们是否相互交互等?虽然查询参数或片段是可选的,但主机名不是;你可以说协议也是必需的,但为此你可以有一个有意义的默认值,比如http对吗?
Anyway, I don't know if this makes sense to your particular problem but I thought it would be worth mentioning for others to have a look at it.
无论如何,我不知道这对您的特定问题是否有意义,但我认为值得其他人看看它。
回答by Verhagen
Some nice answeres are already given here!
这里已经给出了一些不错的答案!
What came to my mind as an addition is Domain Driven Design. Specific the Building blockspart, with Entity, Value Object, Aggregate, Factoryetc.
作为补充,我想到的是领域驱动设计。具体构建块部分,包括Entity、Value Object、Aggregate、Factory等。
A nice introduction is given in Domain Driven Design - Quickly(pdf).
Domain Driven Design - Quickly(pdf) 中给出了一个很好的介绍。
回答by Patrick
I just provide this answer because it was mentioned in a comment and I think it should also be a part of this enumeration of Design Patterns.
我只是提供这个答案,因为它在评论中提到过,我认为它也应该是设计模式枚举的一部分。
Null Object Design Pattern
空对象设计模式
Intent
意图
The intent of a Null Object is to encapsulate the absence of an object by providing a substitutable alternative that offers suitable default do nothing behavior. In short, a design where "nothing will come of nothing"
空对象的目的是通过提供可替代的替代方案来封装对象的缺失,该替代方案提供合适的默认不做任何行为。简而言之,“无中生有”的设计
Use the Null Object pattern when
在以下情况下使用空对象模式
- an object requires a collaborator. The Null Object pattern does not introduce this collaboration--it makes use of a collaboration that already exists
- some collaborator instances should do nothing
- you want to abstract the handling of null away from the client
- 一个对象需要一个合作者。空对象模式没有引入这种协作——它利用了一个已经存在的协作
- 一些协作者实例应该什么都不做
- 您想将 null 的处理从客户端抽象出来
回答by Prasoon Joshi
Ideally, an object should not be concerned about instantiating its dependencies. It should only worry about things that it is supposed to do with them. Have you considered any dependency injection framework? Spring or Google's Juiceare quite versatile and have a small footprint.
理想情况下,对象不应该关心实例化其依赖项。它应该只担心它应该用它们做的事情。你考虑过任何依赖注入框架吗?Spring 或Google 的 Juice用途广泛,占用空间小。
The idea is simple, you declare the dependencies and let the framework decide when/how/where to create them and 'inject' it into your classes.
这个想法很简单,您声明依赖项并让框架决定何时/如何/在何处创建它们并将其“注入”到您的类中。
If you don't want to use any framework, you can take design notes from them and try to emulate their design patterns and tweak it for your use-case.
如果您不想使用任何框架,您可以从他们那里获取设计笔记,并尝试模仿他们的设计模式并针对您的用例进行调整。
Also, you can simplify things to a certain extent by making proper use of Collections. For example, what additional feature does Offers
have other than storing a collection of Offer
? I'm not sure what your constraints there are but, if you can make that part a bit more cleaner you would have massive gains in all places where you are instantiating the objects.
此外,您可以通过正确使用集合在一定程度上简化事情。例如,除了Offers
存储一组Offer
? 我不确定你的限制是什么,但是,如果你能让那部分更干净一点,你会在你实例化对象的所有地方都有巨大的收益。
回答by HRgiger
Dozer frameworkprovides nice way to do copy values from ws object to your dto. Here is another example. Additionally if the getter/setter names are the same of both class you dont need custom converter
Dozer 框架提供了将值从 ws 对象复制到 dto 的好方法。这是另一个例子。此外,如果两个类的 getter/setter 名称相同,则不需要自定义转换器