当我们使用 new
关键字创建对象后,给对象的属性赋值有很多方式,如果参数很多,有些参数可选、有些参数必选,哪种赋值方式最好?下面我们来分析一下。
最简单的方式是 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 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 模式只需要记住他们的代码编写方式即可,我总觉得这样写代码量超多。
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); } }
这种方式的使用场景是:传入的参数并不多,但是对于参数的处理及其复杂,需要生成的对象的入参也不多,这个时候这种模式就非常适用。