-
BuilderModeling/DesignPattern 2020. 2. 24. 22:00
1. Overview
The intent of the Builder design pattern is to separate the construction of a complex object from its representation. We have a complex process to construct an object involving multiple steps, then the builder design pattern can help us. In builder, we remove the logic related to object construction from client code and abstract it in separate classes.
2. Description
2.1 Motivation
2.1.1 Multiple parameters
// Assume Product instance are immutable class Product { public Product(int weight, double price, int shipVolume, int shipCode) { //initialize } // other code }
Usually, we distribute our code in a compiled format in a jar file to other developers so the parameter name itself is of no use. The only way they can find out about these parameters is to refer to your good documentation.
First of all, it will make it easy to use such constructors so they begin to create objects of this class and second it will help us and avoid writing such constructors in the first place and still have other objects immutable.
2.1.2 Objects that need other objects to construct
class User { // we have to create Address and Role object to create User object public User(String name, Address address, LocateDate birthdate, List<Role> roles) { //initialize } // other code }
2.2 Consideration
2.2.1 Implement a Builder
- Identify the parts of the product and provide methods to create those parts
- It should provide a method to assemble or build the product/object
- It must provide a way/method to get fully built object out. Optionally builder can keep a reference to a product it has built so the same can be returned again in the future.
- A director can be a separate class or client can play the role of director
- You can easily create an immutable class by implementing a builder as an inner static class.
2.2.2 Design
- The director role is rarely implemented as a separate class, typically the consumer of the object instance or the client handles that role.
- Abstract builder is also not required if the product itself is not part of any inheritance hierarchy. You can directly create a concrete builder
- If you are running into a "too many constructor arguments" problem then it's a good indication that builder pattern may help
2.3 Pitfalls
- Possibility of a partially initialized object. User code can set only a few or none of the properties using withXXX and call build(). If required properties are missing, build method should provide suitable defaults or throw exception.
3. Usage
3.1 StringBuilder
But with an understanding that StringBuilder don't match 100% with GoF definition of Builder.
3.2 java.util.Calendar.Builder
4. Comparison with Prototype
Builder Prototype We have complex constructor and builder allows us to work with that Prototype allows to altogether skip using constructor We can create a builder as separate class and so it can work with legacy code In java Prototype works using clone method, and needs to modify existing code so may not work with legacy code 5. Example
4.1 Case 1
class Address { private String houseNumber; private String street; private String city; private String zipcode; private String state; public String getHouseNumber() { return houseNumber; } public void setHouseNumber(String houseNumber) { this.houseNumber = houseNumber; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getZipcode() { return zipcode; } public void setZipcode(String zipcode) { this.zipcode = zipcode; } public String getState() { return state; } public void setState(String state) { this.state = state; } } class User { private String firstName; private String lastName; private LocalDate birthday; private Address address; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public LocalDate getBirthday() { return birthday; } public void setBirthday(LocalDate birthday) { this.birthday = birthday; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } } interface UserDTO { String getName(); String getAddress(); String getAge(); } class UserWebDTO implements UserDTO { private String name; private String address; private String age; public UserWebDTO(String name, String address, String age) { this.name = name; this.address = address; this.age = age; } public String getName() { return name; } public String getAddress() { return address; } public String getAge() { return age; } @Override public String toString() { return "name=" + name + "\nage=" +age+"\naddress=" +address; } } interface UserDTOBuilder { UserDTOBuilder withFirstName(String fname); UserDTOBuilder withLastName(String lname); UserDTOBuilder withBirthday(LocalDate date); UserDTOBuilder withAddress(Address address); UserDTO build(); UserDTO getUserDTO(); } class UserWebDTOBuilder implements UserDTOBuilder { private String firstName; private String lastName; private String age; private String address; private UserWebDTO dto; @Override public UserDTOBuilder withFirstName(String fname) { firstName = fname; return this; } @Override public UserDTOBuilder withLastName(String lname) { lastName = lname; return this; } @Override public UserDTOBuilder withBirthday(LocalDate date) { Period ageInYears = Period.between(date, LocalDate.now()); age = Integer.toString(ageInYears.getYears()); return this; } @Override public UserDTOBuilder withAddress(Address address) { this.address = address.getHouseNumber() + ", " + address.getStreet() + "\n" + address.getCity() + "\n" + address.getState() + " " + address.getZipcode(); return this; } @Override public UserDTO build() { dto = new UserWebDTO(firstName + " " + lastName, address, age); return dto; } @Override public UserDTO getUserDTO() { return dto; } } public class Client { public static void main(String[] args) { User user = createUser(); UserDTOBuilder builder = new UserWebDTOBuilder(); UserDTO dto = directBuild(builder, user); System.out.println(dto); } // Director using the builder and the user entity in order to create our entity. private static UserDTO directBuild(UserDTOBuilder builder, User user) { return builder.withFirstName(user.getFirstName()).withLastName(user.getLastName()) .withAddress(user.getAddress()) .withBirthday(user.getBirthday()) .build(); } public static User createUser() { User user = new User(); user.setBirthday(LocalDate.of(1960, 5, 6)); user.setFirstName("Ron"); user.setLastName("Swanson"); Address address = new Address(); address.setHouseNumber("100"); address.setStreet("State Street"); address.setCity("Pawnee"); address.setState("Indiana"); address.setZipcode("47998"); user.setAddress(address); return user; } }
4.2 Case 2
public class Client { public static void main(String[] args) { User user = createUser(); // Client has to provide director with concrete builder UserDTO dto = directBuild(UserDTO.getBuilder(), user); System.out.println(dto); } /** * This method serves the role of director in builder pattern. */ private static UserDTO directBuild(UserDTOBuilder builder, User user) { return builder.withFirstName(user.getFirstName()).withLastName(user.getLastName()) .withBirthday(user.getBirthday()).withAddress(user.getAddress()).build(); } /** * Returns a sample user. */ public static User createUser() { User user = new User(); user.setBirthday(LocalDate.of(1960, 5, 6)); user.setFirstName("Ron"); user.setLastName("Swanson"); Address address = new Address(); address.setHouseNumber("100"); address.setStreet("State Street"); address.setCity("Pawnee"); address.setState("Indiana"); address.setZipcode("47998"); user.setAddress(address); return user; } } class UserDTO { private String name; private String address; private String age; public String getName() { return name; } public String getAddress() { return address; } public String getAge() { return age; } private void setName(String name) { this.name = name; } private void setAddress(String address) { this.address = address; } private void setAge(String age) { this.age = age; } @Override public String toString() { return "name=" + name + "\nage=" + age + "\naddress=" + address ; } //Get builder instance public static UserDTOBuilder getBuilder() { return new UserDTOBuilder(); } //Builder public static class UserDTOBuilder { private String firstName; private String lastName; private String age; private String address; private UserDTO userDTO; public UserDTOBuilder withFirstName(String fname) { this.firstName = fname; return this; } public UserDTOBuilder withLastName(String lname) { this.lastName = lname; return this; } public UserDTOBuilder withBirthday(LocalDate date) { age = Integer.toString(Period.between(date, LocalDate.now()).getYears()); return this; } public UserDTOBuilder withAddress(Address address) { this.address = address.getHouseNumber() + " " +address.getStreet() + "\n"+address.getCity()+", "+address.getState()+" "+address.getZipcode(); return this; } public UserDTO build() { this.userDTO = new UserDTO(); userDTO.setName(firstName+" " + lastName); userDTO.setAddress(address); userDTO.setAge(age); return this.userDTO; } public UserDTO getUserDTO() { return this.userDTO; } } }
5. Reference
'Modeling > DesignPattern' 카테고리의 다른 글
Facade (0) 2020.02.24 Observer (0) 2020.02.24 Singleton (0) 2020.02.24 Model–view–controller (MVC) (0) 2020.02.23 Categorizing Design Pattern (0) 2019.09.29