FluentAPI - Usage
As a Java developer I always got the impression that my own code can be made better (unfortunately I am not perfect). That brought me to think about how I could improve the code.
So I thought several times about my code and while looking for example into my unit/integrations tests I found the following code snippet:
1AccessRules rules = new AccessRules();
2
3AccessRule rule1 = new AccessRule("/");
4rule1.add(UserFactory.createInstance("*"), AccessLevel.READ);
5rule1.add(UserFactory.createInstance("harry"), AccessLevel.READ);
6rule1.add(UserFactory.createInstance("brian"), AccessLevel.READ);
7
8rules.add(rule1);
So far so good but it basically violates the DRY rule.
If you take a deeper look into it you will recognize that AcessLevel.READ
and UserFactory.createInstance(..)
are repeated three times as well as rule1.add(...)
.
Let us change the perspective and move to the user`s point of view. So you can imagine that a user of this code needs to write such kind of code several times which probably is cumbersome.
After a time of thinking about the problem I came across a solution which is from the user`s point of view much more convenient, simpler to read and of course better to understand.
1AccessRules accessRules = new AccessRules.Builder()
2 .forRepository("/")
3 .forUser("*").and("harry").and("brian")
4 .with(AccessLevel.READ)
5 .build();
The only things I had to do was to write some supplemental code to get to the above result.
The following code lines are needed for the .forRepository("...")
part.
1public static class Builder {
2 private AccessRules accessRules;
3
4 public Builder() {
5 this.accessRules = new AccessRules();
6 }
7
8 public AccessRuleBuilder forRepository(String repositoryPath) {
9 AccessRule accessRule = new AccessRule(repositoryPath);
10 accessRules.add(accessRule);
11 return new AccessRuleBuilder(this, accessRule);
12 }
13
14 public AccessRules build() {
15 return this.accessRules;
16 }
17}
The next step was to get the .forUser("..")
working which can be achieved by the following
code part:
1public static class AccessRuleBuilder {
2 private Builder builder;
3 private AccessRule rule;
4
5 private AccessRuleBuilder(Builder builder, AccessRule rule) {
6 this.builder = builder;
7 this.rule = rule;
8 }
9
10 public UserBuilder forUser(String userName) {
11 return new UserBuilder(this.builder, rule).and(userName);
12 }
13}
And finally the following code snippet which solves the rest of the things like
.and("..")
and the last line .with(AccessLeve.READ)
.
1public static class UserBuilder {
2 private List<User> userList;
3 private AccessRule accessRule;
4 private Builder builder;
5
6 private UserBuilder(Builder builder, AccessRule accessRule) {
7 this.userList = new ArrayList<User>();
8 this.builder = builder;
9 this.accessRule = accessRule;
10 }
11
12 public UserBuilder and(String userName) {
13 this.userList.add(UserFactory.createInstance(userName));
14 return this;
15 }
16
17 public Builder with(AccessLevel level) {
18 for (User user : this.userList) {
19 accessRule.add(user, level);
20 }
21 return this.builder;
22 }
23}