Design: Convention, Status: Accepted
This page describes the structure and conventions used for client configuration objects. Client configuration objects are any objects used to configure an AWS client builder.
This section walks through an example configuration class structure and describes each of its components at a high level.
/**
* Configuration Description // (1)
*/
@Immutable
@ThreadSafe // (2)
public final class SdkConfiguration // (3)
implements ToCopyableBuilder<SdkConfiguration.Builder, SdkConfiguration> { // (4)
private final String option; // (5)
/**
* @see #builder() // (6)
*/
private SdkClientConfiguration(DefaultSdkConfigurationBuilder builder) {
this.option = builder.option;
}
public static Builder builder() {
return new DefaultSdkConfigurationBuilder();
}
/**
* @see #Builder#option(String) // (7)
*/
public String option() {
return this.option;
}
@Override
public ClientHttpConfiguration.Builder toBuilder() {
return builder().option(option);
}
@NotThreadSafe
public interface Builder extends CopyableBuilder<Builder, SdkConfiguration> { // (8)
/**
* Configuration Option Description // (9)
*/
Builder option(String option);
}
private static final class DefaultSdkConfigurationBuilder implements Builder { // (10)
private String option;
@Override
public Builder option(String option) { // (11)
this.option = option;
return this;
}
public void setOption(String option) { // (12)
this.option = option;
}
@Override
public SdkConfiguration build() {
return new SdkConfiguration(this);
}
}
}
- A detailed description should be given of what types of options the user might find in this object.
- Configuration objects should be
@Immutable
, and therefore@ThreadSafe
. - Configuration classes should be defined as
final
to prevent extension. - Configuration classes should extend
ToCopyableBuilder
to ensure they can be converted back to a builder object. - All configuration fields should be defined as
private final
to prevent reassignment. - Configuration constructors should be
private
to enforce creation by theBuilder
and refer curious eyes to thebuilder()
method for creating the object. - One "get" method should be created for each configuration field. This method's name should exactly match the name of the field it is retrieving.
- Each builder should have its own interface. This allows hiding certain public methods from auto-complete (see below).
- A detailed description of each option should be given, including what it does, and why a user could want to change its default value.
- A
private static final
implementation of theBuilder
interface should be created that is not exposed outside of the scope of the configuration class. - One "set" method should be created for each option to mutate the value in this builder. This method's name should exactly match the name of the field it is setting.
- Each option should have a bean-style setter to allow configuring the object reflectively using
Inspector
-based frameworks, like spring XML.
This section details the semantics of configuration fields.
- Configuration fields must be immutable.
- Fields must be marked as
final
. - Mutable types, like
List
andSet
should be wrapped in a type that prevents their modification (eg.Collections.unmodifiableList
) when referenced through the "get" method. - Mutable types, like
List
andSet
should be copied in the "set" method to prevent their modification by mutating the original object.
- Fields must be marked as
- Configuration fields must be reference types. Primitive types like
boolean
orint
should not be used because they can not convey the absence of configuration. - Configuration field names should not start with a verb (eg.
config.enableRedirect()
should instead beconfig.redirectEnabled()
). This is to avoid confusing their "get" methods for a mutating action.
Special notes for collection types, like List
, Set
, and Map
:
- Collection type field names must be plural.
- There should be two methods provided to modify the collection.
- method with plural field name which permits adding a collection. It should override any values currently configured in the builder.
- method that permits adding one item to the collection.
- If the collection is a list, the method name should use
add
prefix, eg:addApiName
- If the collection is a map, the method name should use
put
prefix, eg:putHeader
- If the collection is a list, the method name should use
public interface Builder {
/**
* Sets options.
*
* <p>
* This overrides any option values already configured in the builder.
*/
Builder options(List<String> options);
/**
* Add a single option to the collection.
*/
Builder addOption(String option);
/**
* Sets headers to be set on the HTTP request.
*
* <p>
* This overrides any header values already configured in the builder.
*/
Builder headers(Map<String, String> headers);
/**
* Add a single header to be set on the HTTP request.
*
* <p>
* This overrides any values already configured with this header name in the builder.
*/
Builder putHeader(String key, String value);
}