using enum with pattern builder in java - java

im trying to learn how to use pattern builder. i could get it to work until i tried to use enum.
I tried to change the code couple of times and each time had different error. right now the error is Incompatible types.
Please can you help bringing this code to working state and if you have suggestions to improve the code it would be great.
thanks.
EDIT:
now it seems to be okay, but how do i use it with the builder inside the main?
this was the code i used
main:
Person person3 = new Person.PersonBuilder("Julliete", "Kaplan" )
.status(); // what should i write here to set the status?
person class
public class Person
{
private final String name;
private final String lastname;
private final int age;
//My enum im trying to use
private Status status;
public enum Status
{
SINGLE ("Single"), MARRIED ("Married"), WIDOWER ("Widower");
private String status;
private Status(String status)
{
this.status = status;
}
public String getStatus()
{
return this.status;
}
}
//builder
private Person(PersonBuilder builder) {
this.name = builder.name;
this.lastname = builder.lastname;
this.age = builder.age;
this.status = builder.status;
}
//GETTERS
public String getName() {
return name;
}
public String getLastname() {
return lastname;
}
public int getAge() {
return age;
}
#Override
public String toString() {
return "Person : "+this.name+", "+this.lastname+", "+this.age;
}
//PersonBuilder
public static class PersonBuilder
{
private final String name;
private final String lastname;
private int age;
private Status status;
public PersonBuilder(String name, String lastname) {
this.name = name;
this.lastname = lastname;
}
public PersonBuilder age(int age) {
this.age = age;
return this;
}
public PersonBuilder status(Status status)
{
this.status = status;
return this;
}
public Person build() {
Person person = new Person(this);
return person;
}
}

Don't define another Status enum inside the builder: reuse the one defined in the Person class.
Otherwise, you've got to map from instances of PersonBuilder.Status to instances of Person.Status: they are entirely separate types.
Currently this mapping is trivial: you can use Person.Status.valueOf(personBuilderStatus.name()) - but you have to ensure that you update both at the same time to have identical values (or at least that PersonBuilder.Status maps to a subset of Person.Status), which is an unnecessary maintenance burden going forwards.

Related

Java Builder Object Printing Null

I have created a Person, class and a Professor class that both use the Builder Pattern to create objects. The Professor class takes a Person object as an argument in its constructor. I am trying to use both classes together, but when I attempt to print out a professor, get the following output: null null (instead of Bob Smith).
Here's what I tried so far:
Person:
public class Person {
private String firstname;
private String lastname;
private int age;
private String phoneNumber;
private String emailAddress;
private char gender;
public Person(){}
// builder pattern chosen due to number of instance fields
public static class PersonBuilder {
// required parameters
private final String firstname;
private final String lastname;
// optional parameters
private int age;
private String phoneNumber;
private String emailAddress;
private char gender;
public PersonBuilder(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
public PersonBuilder age(int age) {
this.age = age;
return this;
}
public PersonBuilder phoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
return this;
}
public PersonBuilder emailAddress(String emailAddress) {
this.emailAddress = emailAddress;
return this;
}
public PersonBuilder gender(char gender) {
this.gender = gender;
return this;
}
public Person build() {
return new Person(this);
}
}
// person constructor
private Person(PersonBuilder builder) {
this.firstname = builder.firstname;
this.lastname = builder.lastname;
this.age = builder.age;
this.phoneNumber = builder.phoneNumber;
this.emailAddress = builder.emailAddress;
this.gender = builder.gender;
}
#Override
public String toString() {
return this.firstname + " " + this.lastname;
}
}
Here's the Professor class:
package com.example.hardcodedloginform;
import java.util.List;
public class Professor extends Person{
private Person professor;
private double salary;
private String courseTaught;
private List<Student> students;
private int professorID;
public static class ProfessorBuilder {
// required fields
private Person professor;
private int professorID;
// optional fields
private double salary;
private String courseTaught;
private List<Student> students;
public ProfessorBuilder(Person professor, int professorID) {
this.professor = professor;
this.professorID = professorID;
}
public ProfessorBuilder salary(double salary) {
this.salary = salary;
return this;
}
public ProfessorBuilder courseTaught(String courseTaught) {
this.courseTaught = courseTaught;
return this;
}
public ProfessorBuilder students(List<Student> students) {
this.students = students;
return this;
}
public Professor build() {
return new Professor(this);
}
}
private Professor(ProfessorBuilder builder) {
this.salary = builder.salary;
this.courseTaught = builder.courseTaught;
this.students = builder.students;
}
#Override
public String toString() {
return "" + super.toString();
}
}
And here is the Main class where I try to print out a professor object:
public class Main {
public static void main(String[] args) {
Person profBobs = new Person.PersonBuilder("Bob", "Smith")
.age(35)
.emailAddress("bob.smith#SNHU.edu")
.gender('M')
.phoneNumber("818-987-6574")
.build();
Professor profBob = new Professor.ProfessorBuilder(profBobs, 12345)
.courseTaught("MAT101")
.salary(15230.01)
.build();
System.out.println(profBob);
}
}
I would like the printout in the console to be "Bob Smith", but what I am seeing is: null null. I checked and found that the Person object profBobs is, in fact, created properly and does print out the name "Bob Smith" when I attempt to print it the same way. I don't know why my Professor prints: null null.
Your Professor constructor fails to initialise any member fields of its base class.
There are multiple ways to solve this. One solution has ProfessorBuilder extend PersonBuilder:
public class Professor extends Person {
// Remove the `person` field! A professor *is-a* person, it does not *contain* it.
private double salary;
private String courseTaught;
private List<Student> students;
private int professorID;
public static class ProfessorBuilder extends Person.PersonBuilder {
// required fields
private int professorID;
// optional fields
private double salary;
private String courseTaught;
private List<Student> students;
public ProfessorBuilder(Person professor, int professorID) {
super(professor);
this.professorID = professorID;
}
// …
}
private Professor(ProfessorBuilder builder) {
super(builder);
this.salary = builder.salary;
this.courseTaught = builder.courseTaught;
this.students = builder.students;
}
}
For this to work you also need to mark the Person constructor as protected rather than private.
Furthermore, your Professor.toString method implementation made no sense: it essentially just called the base class method, so there’s no need to override it. And prepending the empty string does nothing.

Refactoring in Java: Duplicated attributes

I am supposed to refactor duplicated attributes in Student class. I have Student and Professor classes as below. I am really confused about how to do refactoring with attributes. Should I add a new class, or made modifications in one of the classes. If so, how? I could not understand how to proceed with this to-do.
private final String matrNr;
private final String name;
private final int age;
private int semester;
private final String email;
public Student(String name, int age, String email, String matrNr, int semester) {
this.matrNr = matrNr;
this.name = name;
this.age = age;
this.semester = semester;
this.email = email;
}
public String getEmail() {
return email;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public int getSemester() {
return semester;
}
public String getMatrNr() {
return matrNr;
}
public void increaseSemester(){
semester = semester + 1;
}
}
And the professor is a like:
private final String persNr;
private final String name;
private final int age;
private final String email;
public Professor(String name, int age, String email, String persNr) {
this.persNr = persNr;
this.name = name;
this.age = age;
this.email = email;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public String getPersNr() {
return persNr;
}
}
Thanks for any kind of helps!
Your goal is to refactor duplicated attributes in the Student and Professor classes. The way to do this is to create a parent class which defines the common attributes (like "name"), and modify Student and Professor classes to extend the common parent class. In this way, both Students and Professors can have a "name", even though you have defined "name" only once in the common parent.
Below shows how you could do this with a common "Human" parent class, how the constructors would work, and how you could define a Student-only attribute (semester).
Here is a simple version a common Human class:
common "Human" class
each Human has a "name"
the name is set in the constructor (so when you're creating an object) and cannot be changed later ("name" is final; also no "setHuman()")
class Human {
private final String name;
public Human(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Here's a simple Professor class:
by definition, a Professor is a Human (Professor extends Human)
when creating a Professor, you must specify the "name" (which is then passed to the Human constructor)
once you have a Professor, you can call getName() (which is defined on the Human class)
class Professor extends Human {
public Professor(String name) {
super(name);
}
}
Here's a simple Student class:
Student is a little different - in addition to a name, it also has a "semester"
when creating a Student, the constructor requires a name and semester, and the Student class itself keeps track of "semester" – so it's fine to have semester defined on Student, and name defined on Human.
you can call getName() (defined on Human)
you can call getSemester() (defined on Student)
class Student extends Human {
private final int semester;
public Student(String name, int semester) {
super(name);
this.semester = semester;
}
public int getSemester() {
return semester;
}
}

Use of private data variables in simple POJO [duplicate]

This question already has answers here:
Why use getters and setters/accessors?
(37 answers)
Closed 1 year ago.
Recently I was going through the concept of Encapsulation in Java. I was wondering if making data variables private along with public setter methods really make sense in simple POJO class? Please refer below POJO:
public class Employee{
private String id;
private String name;
private String department;
private int age;
public Employee(){
}
public Employee(String id, String name, String department, int age){
this.id = id;
this.name = name;
this.department = department;
this.age = age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
I mean why am I making the name variable private when I can anyway change it using the setter method?
In the general case, it'll be the very basic
public void setName(String name) {
this.name = name;
}
Where it's identical to just doing employee.name = "william hammond". But imagine a case where you'd like to do implement something like a private String normalize(string username) method where you maybe make it all lower case, check for a valid name or prevent unicode entries. If you make name public initially you'll have users doing employee.name = "whatever they want :) 123" and you'll lose the ability to enforce that constraint.
Also see Why use getters and setters/accessors?
Using getters/setters is just considered good practice, but it can often be overkill - like in your example.
If you have methods that mutate the variable before setting, then it's nice to have getters/setters for the basic fields as well to maintain consistent code style.
Here's a good article on it:
https://dzone.com/articles/getter-setter-use-or-not-use-0
Let's have an example:
public class Example {
private String firstName;
private String lastName;
public Example(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getFullName() {
return firstName + " " + lastName;
}
}
This class has 3 properties (firstName, lastName, fullName), but only two fields (firstName, lastName). It makes sense, because a full name can be retrieved by combining first and last name.
However, I've noticed that I call getFullName() a lot of times in my program, but I almost never call getFirstName() and getLastName(). This slows down my program, because I need to create a new string each time getFullName() is called. So, I've refactored my code to have a better performance:
public class Example {
private String fullName;
public Example(String firstName, String lastName) {
this.fullName = firstName + " " + lastName;
}
public String getFirstName() {
return fullName.split(" ")[0];
}
public String getLastName() {
return fullName.split(" ")[1];
}
public String getFullName() {
return fullName;
}
}
Now my code works faster when calling getFullName(), but slower when calling getFirstName() and getLastName(), however It's exactly what I needed. From outside the class, nothing really've changed.
As you can see by the given example, fields describe how your class uses the computer's memory, but not necessarily which properties your class has. This is why fields should be considered an implementation detail and therefore be private to a class.

How to implement the Builder Design Pattern in the right way?

I have found different implementations of the Builder pattern when learning about design patterns. Some implementations use an interface/abstract-class to represent the builder, others use just an static class.
Which one is the right way to implement the Builder Design Pattern?
Below, an implementation using an abstract class (ComputerBuilder) (Source)
public class LaptopBuilder : ComputerBuilder
{
Computer computer;
public LaptopBuilder()
{
computer = new Computer("Laptop");
}
public override void BuildOS()
{
//TODO
}
public override void BuildDevice()
{
//TODO
}
public Computer ComputerType
{
get { return computer; }
}
}
public class DesktopBuilder : ComputerBuilder
{
Computer computer;
public DesktopBuilder()
{
computer = new Computer("Desktop");
}
public override void BuildOS()
{
//TODO
}
public override void BuildDevice()
{
//TODO
}
public Computer ComputerType
{
get { return computer; }
}
}
Below, another implementation, Neither using abstract class nor interface, but an static class instead. (Source)
public class User
{
//All final attributes
private final String firstName; // required
private final String lastName; // required
private final int age; // optional
private final String phone; // optional
private final String address; // optional
private User(UserBuilder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
this.age = builder.age;
this.phone = builder.phone;
this.address = builder.address;
}
//All getter, and NO setter to provde immutability
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
public String getPhone() {
return phone;
}
public String getAddress() {
return address;
}
#Override
public String toString() {
return "User: "+this.firstName+", "+this.lastName+", "+this.age+", "+this.phone+", "+this.address;
}
public static class UserBuilder
{
private final String firstName;
private final String lastName;
private int age;
private String phone;
private String address;
public UserBuilder(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public UserBuilder age(int age) {
this.age = age;
return this;
}
public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}
public UserBuilder address(String address) {
this.address = address;
return this;
}
//Return the finally consrcuted User object
public User build() {
User user = new User(this);
validateUserObject(user);
return user;
}
private void validateUserObject(User user) {
//Do some basic validations to check
//if user object does not break any assumption of system
}
}
}
If you actually read the second article you may notice that it fairly early on states that
"I want to make it clear that the builder pattern which I am going to discuss in this post, is slightly different from what is mentioned in GangOfFour “Design Patterns” book." (Author's emphasis)
A little later, he or she writes:
"For me, a builder pattern is more like fluent interface."
Notice the little qualifier, for me.
This should tell you that what you see isn't the 'canonical' representation of the pattern, but a variation.
That said, these representations are variations of the same underlying idea.
It's okay to look at alternative ways to express a concept. We should be careful that we don't elevate the Gang of Four book to unassailable status.
For me (pun intended) Builder is the best example of a GoF pattern that has been improved since the book was published. I believe it was Josh Bloch's version from Effective Java that popularized the static approach; but there are versions originating from blog posts that are more useful and/or less complicated than the GoF version.
As often as I refer back to the GoF book, I never reread the Builder chapter, because better alternatives are available. Of course, you have to decide which alternative is better for you.

How to modify the already built object in Java, when the Object is built using Builder Pattern?

I have built (Using Builder Pattern) an Employee object with three fields Name, Age and Gender.
public class Employee {
private String name;
private String age;
private String gender;
// Constructor
private Employee(Builder builder) {
this.name = builder.name;
this.age = builder.age;
this.gender = builder.gender;
}
// Employee Builder
public static class Builder {
private String name;
private String age;
private String gender;
public Builder name(String name) {
this.name = name;
return this;
}
public Builder age(String age) {
this.age = age;
return this;
}
public Builder gender(String gender) {
this.gender = gender;
return this;
}
}
// Getters
public String getName() {
return name;
}
public String getAge() {
return age;
}
public String getGender() {
return gender;
}
}
Now in the Following Class I have built my Employee Object,
public class TestEmployee {
public static void main(String[] args) throws IOException {
Employee employee = new Employee.Builder().age("23").gender("Male").name("John").build();
System.out.println("Name : " + employee.getName());
System.out.println("Age : " + employee.getAge());
System.out.println("Gender : " + employee.getGender());
}
}
How can I modify the Age of the Employee "John" by breaking the already built employee object?
FYI : I don't want to have Setters in my Employee object.
You want to modify an immutable object. Do you see the problem there?
Either add setters (or any methods that mutate the state) or accept that the object is immutable.
You can of course create a new object based on the values of the old one, but it won't be the same object then.
Build another one using Copy-On-Write (reuse existing fields but change age).
Employee.Builder()
.age(employee.getAge() + 1)
.gender(employee.getGender())
.name(employee.getName())
.build();
Keep in mind, it will be another object.
ahh I was also trying to solve my problem with this approach and ironically I stumbled upon this question. I understand the challenge here, we are trying to modify an object with same builder setters but doing so we'll end up with an new object.
I figured out that there is a solution, so after approx 5yrs here's my answer LOL(to someone who's gonna end up here)
first, instead of creating duplicate properties of OuterClass in inner static Builder class. *declare an instance of outer class in Builder class. which means your build() method will return that instance.
this *declaration of instance is intended, as I am planning to create it internally or gonna ask for its memory from outside.
public class Employee {
private String name;
private String age;
private String gender;
public Static Builder builder()
{
return new Builder();
}
public Static Builder modifier(Employee employee)
{
return new Builder(employee);
}
// Employee Builder
public static class Builder {
private Employee employee;
public Builder(Employee employee)
{
this.employee = employee;
}
public Builder()
{
this.employee = new Employee();
}
public Builder name(String name) {
this.employee.name = name;
return this;
}
public Builder age(String age) {
this.employee.age = age;
return this;
}
public Builder gender(String gender) {
this.gender = gender;
return this;
}
public Employee build()
{
return this.employee;
}
}
// Getters
public String getName() {
return name;
}
public String getAge() {
return age;
}
public String getGender() {
return gender;
}
}
notice the tweak here, I introduced a modifier that takes Employee object and further allows client to modify it with Builder pattern.
So your client will use it like this...
to create new Employee
Employee employee = Employee.builder().name("abc).age(20).build();
to modify same instance
Employee.modifier(employee)
.name("xyz")
.build();
If you don't want to put setters (and make Employee mutable) you can't modify age of john... instead of this, what you can do is:
employee = new Employee.Builder()
.age("21")
.gender(employee.getGender())
.name(employee.getName())
.build();
If you don't want setters or public non-final fields, then you can add an extra constructor to the builder which will cause an initial state matching the instance, but with the builder setters available. This won't modify the original object, but create a new one based on it.
public static class Builder {
private String name;
private String age;
private String gender;
public Builder(Employee employee) {
this.name = employee.getName();
this.age = employee.getAge();
this.gender = employee.getGender();
}
public Builder name(String name) {
this.name = name;
return this;
}
public Builder age(String age) {
this.age = age;
return this;
}
public Builder gender(String gender) {
this.gender = gender;
return this;
}
public Employee build() {
return new Employee(this);
}
}
You can then use it as following.
Employee employee = new Employee.Builder().age("23").gender("Male").name("John").build();
Employee employee2 = new Employee.Builder(employee).name("Jane").build();

Categories

Resources