Understanding value to reference and reference to value in Java refactoring - java

I was going through refactoring book and couldn't understand these two techniques.
https://refactoring.com/catalog/changeValueToReference.html
And
https://refactoring.com/catalog/changeReferenceToValue.html
Can you please help me understand with an example in Java?

Assume you have two Customer objects/instances, customer1 and customer2. They are actually the same customer, with same ID and name. However they were read at different moments from the database or such. A new Customer() was called twice.
Customer customer1 = new Customer(13L);
Customer customer2 = new Customer(13L);
The refactoring would involve a factory method:
Customer customer1 = customerRepository.findById(13L);
Customer customer2 = customerRepository.findById(13L);
Examples of the latter is for instance JPA, an O/R mapping.

In it's very simplest form it just means switching between the following two options:
class Order
{
// constructor etc...
private String customerName;
public String getCustomerName() {return customerName;}
}
or
class Customer
{
// constructor etc...
private String name;
public String getName() {return name;}
public String setName(String name) {this.name = name;}
}
class Order
{
// constructor etc...
private Customer customer;
public String getCustomerName() {return customer.getName();}
}
Now when a customer's name can change, the 2nd solution is prefered, because you don't need to update the customerName member in every order.
However if a customer's name never changes, the 1st solution might be prefered, because you don't need to manage Customer objects somewhere.
Which version you want to chose and if you need to refactor depends on your actual application.

Strictly speaking, I'm not sure whether this conversion is possible in Java, since you don't have both references and values of objects in Java - all objects are references (which are passed by value).
However, one can consider this similar to the following example:
"Value":
class Order
{
private int orderNum;
private Customer customer;
Order(int orderNum, String customerString)
{
this.orderNum = orderNum;
this.customer = new Customer(customerString);
}
}
// elsewhere:
Order order1 = new Order(1, "I am a customer");
Order order2 = new Order(2, "I am a customer");
Order order3 = new Order(3, "I am a customer");
Order order4 = new Order(4, "I am a customer");
Here each Order has it's own Customer object, even when all or most of those Customer objects are the same.
The above is, of course, just an example. Passing all parameters of another object into a constructor is not good design.
Reference:
class Order
{
private int orderNum;
private Customer customer;
Order(int orderNum, Customer customer)
{
this.orderNum = orderNum;
this.customer = customer;
}
}
Customer customer = new Customer("I am a customer");
Order order1 = new Order(1, customer);
Order order2 = new Order(2, customer);
Order order3 = new Order(3, customer);
Order order4 = new Order(4, customer);
Here we can have a single Customer for many Orders (if we want).

Related

how to exclude a property from object while putting into hashmap in java

Lets say, Employee class has three properties.
class Employee {
int id;
String name;
String team;
public Employee(){
this.id = id;
this.name = name;
this.team = team;
}
}
I want to remove team from the object before putting into HashMap.
Map<Integer, Employee> empMap = new HashMap<>();
Employee e1 = new Employee(100, "John", "Dev");
Employee e2 = new Employee(101, "Mary", "Dev");
Employee e3 = new Employee(103, "Andy", "QA");
empMap.put(e1.getId(), e1);
empMap.put(e2.getId(), e2);
empMap.put(e1.getId(), e3);
The values in empMap shouldn't have team property in it. Creating new objects would work but it is costly in real time. Is there a way to achieve this without creating new objects.
One option, as you have mentioned, is to create new objects without the team property. Another is to use a façade
public class MapEmpFacade extends Employee {
public MapEmpFacade(Employee emp) {
//define all methods to return the method results from emp, except for getTeam
}
public int getTeam() { return null; } //override getTeam
}
You may review your design to be sure you need the property to be there.
You are holding Employee references in the map, the team property is there and probably is correctly representing an Employee ... you can just avoid using the team property ... or maybe you want to have a Person class.
If you are using those instances from some framework to do something (e.g.: serialization, persistence, etc) it should probably provide a way to ignore/skip a property in your object.

Java OOP concept for library model

I am new to Java and I need some help implementing OOP rules for projects that are similar to the Library model (Car rental model).
I have 2 classes:
public class Book
public class Customer
Simply put, my classes look like this:
public class Customer
{
public String fullName;
public String address;
/* Constructor */
public Customer(String fullName, String address) {
this.fullName = fullName;
this.address = address;
}}
and
public class Book
{
private String bookName;
private int pageNumber;
public Customer customer; //I think this needs to be fixed
/* Constructor */
public Book(String bookName, int pageNumber, Customer customer) {
this.bookName = bookName;
this.pageNumber = pageNumber;
// ... There I am not sure how to do something like: this.customer = customer ...
// there should be another constructor without customer as a parameter but i can deal with that cuz its easy
}}
Each instance of the Customer class represents a person who borrowed the book. Now what I am struggling to implement is: How do I pass an instance of Customer class as a parameter for a constructor of the Book class? I would like something like this:
// Constructor in Class Book:
public Book(String bookName, int pageNumber, Customer customer) {
this.bookName = bookName;
this.pageNumber = pageNumber;
this.customer = customer; //I think this needs to be fixed
}
Another question is how do I create a record of a book boorowed by Sarah Connor living in London? Is this implementation correct Book book1 = new Book("The Hobbit", 300, "Sarah Connor", "London")?
Basic solution
Basically, your approach was completely correct:
public Book(String bookName, int pageNumber, Customer customer) {
this.bookName = bookName;
this.pageNumber = pageNumber;
this.customer = customer;
}
However, as your comment shows, you are still lacking some very basic understanding of Java. Let's recap what you tried:
Book book1 = new Book("Harry Potter", 500, "Mike", "London");
Why wouldn't this work?
First, you pass in 4 parameters instead of 3. But let's assume that was a typo and ignore "London". Still, there is an issue: "Mike" is a String, but your constructor expects a Customer. Hence, your code should look like this:
Customer mike = new Customer("Mike Malony", "N13BJ London");
Book book1 = new Book("Harry Potter", 500, mike);
Since you asked in the comments, this can be done in one line as well:
Book book1 = new Book("Harry Potter", 500, new Customer("Mike", "London"));
Further issues and advice
Now your issue seems solved, but your code still isn't very... versatile. The reason is that your model is severely limited: you can only ever instantiate a Book if you pass in a Customer. But most libraries I know can have books that are not currently borrowed. Also, whatever your pageNumber seems to represent (a bookmark, possibly?), I assume an untouched book doesn't need this information. I would therefore suggest this:
public Book(String bookName) {
this.bookName = bookName;
this.pageNumber = 0; // 0 => no page bookmarked
this.customer = null; // null => book is available
}
Now you need a way to hand out a book to a customer and also to check its status. I suggest:
public void lend(Customer customer) {
this.customer = customer;
}
public void receiveBack() {
customer = null;
pageNumber = 0;
}
public boolean isAvailable() {
return (customer == null);
}
And to bookmark a page:
public setBookmark(int pageNumber) {
this.pageNumber = pageNumber;
}
Of course, this is still far from optimal. What if a customer wants to borrow several books? Should the books really hold the reference to the customer? There are several approaches to this. One would be to have a dedicated class that holds the associations of customers to books. Another would be to use some sort of collection in either Book or Customer. However, that's probably all beyond the scope of your task, so I'll leave it at the above.
If you want to declare your instance like this
Book book1 = new Book("The Hobbit", 300, "Sarah Connor", "London")
You will need to define your constructor of Book Class like this :
public Book(String bookName, int pageNumber, String fullname, String address) {
this.bookName = bookName;
this.pageNumber = pageNumber;
this.customer = new Customer(fullname, address); //I think this needs to be fixed
}
Or other option is to declare it like this
Book book1 = new Book("The Hobbit", 300, new Customer("Sarah Connor", "London"));

Constructor with many parameters

I have a class with (say) 50 fields. I only use a few of them per deployment of the program per user need. Is there a way to make the constructor generic yet specific to the deployment?
e.g
public class Employee{
private String id = "default";
private String empcat = "default";
private String empfam = "default";
private String phychar = "default";
private String othchar = "default";
private String shoesty = "default";
private Double shoesiz = 0.0;
private String shoesty = "default";
private Double shirsiz = 0.0;
private String shirsty = "default";
.........50 other fields..
}
"User/Customer 1" - only wants to use the program for shoe and thus instantiates the object with :
Employee obemp = new Employee("John", 11.5, Dockers); (i.e. id, shoesiz and shoesty)
User/Customer 2 - only wants to use the program for shirt and thus instantiates the object with :
Employee obemp = new Employee("John", 42, ABC); (i.e. id, shirsiz and shirsty)
User/Customer 3 - only wants to use the program for family and thus instantiates the object with :
Employee obemp = new Employee("John", "Smith"); (i.e. id, empfam)
The order of the fields during the object creation can be different - depending on the usage in the model.
First of all, I'd suggest breaking your main class down into smaller pieces that manage data which typically goes together (Shoe information, Shirt information, Family information, etc.).
Secondly, I'd suggest you provide customers with a builder pattern to make it easy for them to construct an object with just the pieces that they're likely to need. That way, they can do something like this:
Employee emp = new EmployeeBuilder("John")
.withShirtInfo(shirsiz, shirsty)
.build();
There is no generic way in core java to do this. But you may use some design pattern like - builder pattern.
You may also create an Employee with some minimum criteria like - id. We can assume each Employee have an id. So create an Employee with the id using the Employee(String id) constructor -
public class Employee{
//all properties
public Employee(String id){
this.id = id;
}
//setter methods
}
Suppose you have create an Employee like this -
Employee employee = new Employee("eng134");
After that you can set only required property to employee object using the setter methods -
employee.setShoesiz(9);
employee.setShirsiz(26);

Adding multiplie objects to an ArrayList in java

I have a class named Customer which stores the following objects:
private String CustomerFirstName
private String CustomerLastName
private String CustomerID
private String CustomerEmail
now in order to pass data to jasper report, I decided to create an array list which contains these objects, so :
import java.util.ArrayList;
import java.util.Collection;
/* This is CustomerDataSource.java file */
public class CustomerDataSource {
public static Collection<Customer> loadCustomers() throws Exception {
Collection<Customer> customers = new ArrayList<Customer>();
Customer customer = new customer (
/* I need help getting the objects CustomerFirstName / CustomerLastName and etc */
);
customer.addBilling(new Billing ( /* Adding billing info */ ));
customer.getBilling(new Billing ( /* I need to get the object's values*/));
customer.balOwing();
customers.add (customer);
return customers;
}
}
can someone please explain how to add the objects in Customer.java to the array list? (and in general since I need to add objects from different files as well. Thank you
So as I see your problem in your comment, you want to create a constructor.
In your Costumer class
public Costumer(String firstName, String lastName, String ID, String email) {
this.CostumerFirstName = firstName;
this.CostumerLastName = lastName;
this.CostumerID = ID;
this.CostumerEmail = email;
}
So then you can create a new costumer like that:
Customer customer = new Customer ("SampleFirstName","SampleLastName","0000","address#web.com");
You could even add the costumer automatically to the ArrayList by adding it in the constructor.
From your comment, I am guessing you would like to use a constructor?
You will have to add a constructor on your Customer.java.
public Customer(String firstName, String lastName, String id, String email){
this.CustomerFirstName = firstName;
this.CustomerLastName = lastName;
this.CustomerID = id;
this.CustomerEmail = email;
}
You might want to make getter/setter methods for access to above variables.
ArrayList<E>.get(i) performs virtually the exact same function as [] in static arrays. The only difference between the two is that ArrayList<E>.get(i) is simply adapted into the object context. In other words, you can dereference it.
First, you'll need to change the privacy of Customer's fields to public to give the ArrayList<Customers> object access to it.
Then you'll be able to retrieve your class' fields with simply:
customers.get(index).FirstName //or whatever other field

Having troubles creating this object

I'm very new to java, and trying to grasp making an object with two different values.
I'm trying to create a Customer object called customer, with the initial values of 1 and cust1, and then display the customer object to the output with toString()
Thanks for any help in advance.
Here's what I have currently.
public class Customer {
private int id;
private String name;
public Customer(int id, String name) {
this.id = id;
this.name = name;
Customer customer = new Customer(1, "cust1");
}
You have no entry point to your program, which should look like this in your class
public static void main(String[] args)
{
//objects created here
}
You also create a Customer object as a member of your Customer class which means every Customer object contains another.
You can't set Customer members like this
Customer customer = new Customer(); //you also don't have a no argument constructor
customer = 1; //how would it know where to put this 1?
customer = cust1; //same as above
it would be like this (if they were in the right place, as mentioned above)
Customer customer = new Customer(); //if using this method you will need a no argument constructor
customer.id = 1;
customer.name = cust1;
or like this
new Customer(1,"cust1");
In Summary
You need an entry point
You are creating Customer with a no argument constructor but you only have one constructor which has two arguments
You are -for some reason- creating a Customer inside every Customer
You are not setting members of your Customer object in the correct (or even in a valid) way
Don't create a new object instance within a classes constructor — this will result in a StackoverFlowException.
public class Customer {
private final int id;
private final String name;
public Customer(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
}
In a separate class you can simply create a new instance by using
Customer customer = new Customer(1, "Name");

Categories