当我们使用 new
关键字创建对象后,给对象的属性赋值有很多方式,如果参数很多,有些参数可选、有些参数必选,哪种赋值方式最好?下面我们来分析一下。
使用set方法
最简单的方式是 new
一个默认的对象,通过 set
方法给对象的属性赋值。
// JavaBeans Pattern - allows inconsistency, mandates mutability
public class NutritionFacts {
// Parameters initialized to default values (if any)
private int servingSize = -1; // Required; no default value
private int servings = -1; // Required; no default value
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public NutritionFacts() { }
// Setters
public void setServingSize(int val) { servingSize = val; }
public void setServings(int val) { servings = val; }
public void setCalories(int val) { calories = val; }
public void setFat(int val) { fat = val; }
public void setSodium(int val) { sodium = val; }
public void setCarbohydrate(int val) { carbohydrate = val; }
}
创建对象的时候,只需要 set 即可,参数多的时候似乎有些冗长。
NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);
使用重载的构造函数
public class XMLConfigBuilder extends BaseBuilder {
private boolean parsed;
private final XPathParser parser;
private String environment;
private final ReflectorFactory localReflectorFactory;
public XMLConfigBuilder(InputStream inputStream) {
this(inputStream, null, null);
}
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
this.localReflectorFactory = new DefaultReflectorFactory();
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
上面代码是 mybatis 里面 XMLConfigBuilder
对象创建的部分代码,使用重载函数创建对象。这种方式还不错。
静态工厂方法
静态工厂方法与设计模式中的工厂方法模式不同
如:构造一个 Double
对象,入参是 String
类型。
public static Double valueOf(String s) throws NumberFormatException {
return new Double(parseDouble(s));
}
如:构造一个 DateTimeFormatter
对象,入参是 String
类型。
public static DateTimeFormatter ofPattern(String pattern) {
return new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter();
}
上面的两个例子是JDK提供的,我们自己如何编写呢?
public static User of(Long userId, String userName, String type) {
User user = new User();
user.setUserId(userId);
user.setUserName(userName);
user.setType(type);
return user;
}
使用静态方法,传入入参,在方法对象内部 new 对象,然后返回。
优点:
- 可以返回其返回类型的任何子类型的对象
- 它们不需要每次调用时都创建一个新对象
- 与构造方法不同,它们是有名字的
- 返回对象的类可以根据输入参数的不同而不同
缺点:程序员很难找到它们,遵守标准的命名习惯,也可以弥补这一劣势
-
from —— 类型转换方法,它接受单个参数并返回此类型的相应实例,例如:
Date d = Date.from(instant);
-
of —— 聚合方法,接受多个参数并返回该类型的实例,并把他们合并在一起,例如
SetfaceCards = EnumSet.of(JACK, QUEEN, KING);
-
valueOf —— from 和 to 更为详细的替代 方式,例如:
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
-
getinstance —— 返回一个由其参数 (如果有的话) 描述的实例,但不能说它具有相同的值,例如:
StackWalker luke = StackWalker.getInstance(options);
使用builder模式
// Builder Pattern
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public static class Builder {
// 必选参数
private final int servingSize;
private final int servings;
// 可选参数,初始化默认值
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
calories = val;
return this;
}
public Builder fat(int val) {
fat = val;
return this;
}
public Builder sodium(int val) {
sodium = val;
return this;
}
public Builder carbohydrate(int val) {
carbohydrate = val;
return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
使用方式:
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8) .calories(100).sodium(35).carbohydrate(27).build();
builder 模式只需要记住他们的代码编写方式即可,我总觉得这样写代码量超多。
编码步骤:
- 编写原生对象
Person
的属性
public class Person {
private Integer id;
private String name;
private Integer age;
}
- 编写
Builder
静态内部类,属性将上面的属性复制即可。
public class Person {
private Integer id;
private String name;
private Integer age;
public static class Builder {
private Integer id;
private String name;
private Integer age;
}
}
- 编写私有的创建
Person
的构造器,入参是Builder
对象。
public class Person {
private Integer id;
private String name;
private Integer age;
public static class Builder {
private Integer id;
private String name;
private Integer age;
}
private Person(Builder builder) {
id = builder.id;
name = builder.name;
age = builder.age;
}
}
- Builder 对象创建构造器,将必填的属性作为入参,如 id
public class Person {
private Integer id;
private String name;
private Integer age;
public static class Builder {
private Integer id;
private String name;
private Integer age;
public Builder(Integer id) {
this.id = id;
}
}
private Person(Builder builder) {
id = builder.id;
name = builder.name;
age = builder.age;
}
}
- 将其它可选属性,通过方法为其赋值,并且返回 Builder 对象。如下面的 name 和 age 。
public class Person {
private Integer id;
private String name;
private Integer age;
public static class Builder {
private Integer id;
private String name;
private Integer age;
public Builder(Integer id) {
this.id = id;
}
public Builder name(String name) {
this.name = name;
return this;
}
public Builder age(Integer age) {
this.age = age;
return this;
}
}
private Person(Builder builder) {
id = builder.id;
name = builder.name;
age = builder.age;
}
}
- 使用 build() 方法返回 Person 对象。
public class Person {
private Integer id;
private String name;
private Integer age;
public static class Builder {
private Integer id;
private String name;
private Integer age;
public Builder(Integer id) {
this.id = id;
}
public Builder name(String name) {
this.name = name;
return this;
}
public Builder age(Integer age) {
this.age = age;
return this;
}
public Person build() {
return new Person(this);
}
}
private Person(Builder builder) {
id = builder.id;
name = builder.name;
age = builder.age;
}
}
通过以上几步,就可以完成 Builder 模式的设计。
mybatis 中的 SqlSessionFactoryBuilder
使用了 builder 模式的变体。
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment) {
return build(inputStream, environment, null);
}
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
return build(inputStream, null, properties);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
这种方式的使用场景是:传入的参数并不多,但是对于参数的处理及其复杂,需要生成的对象的入参也不多,这个时候这种模式就非常适用。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。