Make Your Life Better With
Immutable Objects
Maxim Novak, Wix
https://github.com/maximn@maximnovakmaximn@wix.com
I’m Maxim Novak
▪ Avid Scala advocate
▪ Team leader at Wix
▪ Building core services
▪ 10 years developing software
▪ TDD and clean code enthusiast
Lithuania
Vilnius
Kyiv
Dnipro
Wix Engineering Locations
Israel
Tel-Aviv
Be’er Sheva
Ukraine
What’s an immutable class?
An object is considered immutable if its state
cannot change after it is constructed.
Maximum reliance on immutable objects is
widely accepted as a sound strategy for
creating simple, reliable code.
...
- Oracle Java documentation
https://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html
GOOD QUESTION
But they made the most important one
immutable
java.lang.String
So why are most Java classes mutable?
What do you mean can’t change it ?!
So how do I work with it?
Create a new instance with the modified state
Person
Name
Birth
Date
Address
Huge data
Person moved =
person.moveTo(newAddress);
Something
Something
Something
Person’
Person
Name
Birth
Date
Address
Huge data
Address’
Something
Something
Something
Advantages
(Why do you want it)
You get a much simpler
programming model
“I think that large objected-oriented programs struggle with increasing complexity
as you build this large object graph of mutable objects. You know, trying to
understand and keep in your mind what will happen when you call a method and
what will the side effects be.”
- Rich Hickey (2010)
Easier to reason about
https://pixabay.com/en/fly-swatter-flyswatter-fly-flap-bug-149265/
void foo(SomeInterface func) {
Data data = new Data("initial state");
func.apply(data);
log(data);
}
Easier to reason about
Side-effect free (share safely)
https://pixabay.com/en/fly-swatter-flyswatter-fly-flap-bug-149265/
https://pixabay.com/en/chemistry-science-antique-bottles-3188870/
Easier to test
Easier to debug
https://pixabay.com/en/ladybugs-ladybirds-bugs-insects-1593406/
Avoid temporal coupling
void addItemToCart() throws IOException {
Request req = Request.Post("http://example.com");
req.setHeader("Authorization", "token");
req.bodyForm(
new BasicNameValuePair("action", "create-cart"));
req.execute();
req.bodyForm(
new BasicNameValuePair("action", "add-item"));
req.execute();
}
AddItem
CreateCart
void addItemToCart() throws IOException {
Request req = Request.Post("http://example.com");
req.setHeader("Authorization", "token");
req.bodyForm(
new BasicNameValuePair("action", "create-cart"));
req.execute();
req.bodyForm(
new BasicNameValuePair("action", "add-item"));
req.execute();
}
AddItem
CreateCart
void addItemToCart() throws IOException {
Request req = Request.Post("http://example.com");
req.setHeader("Authorization", "token");
req.bodyForm(
new BasicNameValuePair("action", "create-cart"));
req.execute();
req.bodyForm(
new BasicNameValuePair("action", "add-item"));
req.execute();
}
AddItem
CreateCart
void addItemToCart() throws IOException {
Request req = Request.Post("http://example.com");
req.setHeader("Authorization", "token");
req.bodyForm(
new BasicNameValuePair("action", "create-cart"));
req.execute();
req.bodyForm(
new BasicNameValuePair("action", "add-item"));
req.execute();
}
AddItem
CreateCart
void addItemToCart() throws IOException {
Request req = Request.Post("http://example.com");
req.setHeader("Authorization", "token");
req.bodyForm(
new BasicNameValuePair("action", "create-cart"));
req.execute();
addItem(req);
}
private void addItem(Request req) throws IOException {
req.bodyForm(
new BasicNameValuePair("action", "add-item"));
req.execute();
}
CreateCart
void addItemToCart() throws IOException {
Request req = Request.Post("http://example.com");
createCart(req);
addItem(req);
}
private void createCart(Request req) throws IOException {
req.setHeader("Authorization", "token");
req.bodyForm(
new BasicNameValuePair("action", "create-cart"));
req.execute();
}
void addItemToCart() throws IOException {
Request req = Request.Post("http://example.com");
createCart(req);
addItem(req);
}
private void createCart(Request req) throws IOException {
req.setHeader("Authorization", "token");
req.bodyForm(
new BasicNameValuePair("action", "create-cart"));
req.execute();
}
https://pixabay.com/en/fingerprint-brush-identity-search-2108872/
Avoiding Identity Mutability
Set<Date> set = new HashSet<>();
Date date = new Date(2);
set.add(date);
date.setTime(4);
System.out.println(set.contains(date));
HashCode Object
1
2
3
4
...
...
n
date(2)
date
Avoiding Identity Mutability
https://pixabay.com/en/hexanol-model-molecule-carbon-3d-835644/
Always have failure atomicity
Prevent NULL references
public class Person {
private String firstName;
private String lastName;
public Person() {
}
public void setName(String firstName, String lastName) {
throwOnInvalid(firstName);
this.firstName = firstName;
throwOnInvalid(lastName);
this.lastName = lastName;
}
...
BOOM!!!
http://www.freestockphotos.biz/stockphoto/17639
Always Thread safe
Advantages
(Why do you want it)
● Easier to reason about
● Side-effect free (share safely, no defensive copies)
● Easier to test
● Easier to debug
● Avoid temporal coupling
● Avoiding Identity Mutability
● Always have failure atomicity / Prevent NULL references
● Always Thread safe
Disadvantages
(What you should beware of )
● Performance under certain conditions.
● Brings some difficulties when working in Java
So how do I start?
(some rules of thumb)
● Make all fields final & private
● Create methods that create a copy
of the class with a modified state
● Disallow inheritance (final class)
So how do I start?
(some rules of thumb)
● If you’re using mutable classes inside
○ Copy them on construction
○ Don’t expose references to internal fields
(copy defensively)
○ Don’t provide any method that changes
the state (setters)
So how do I start?
(How can I make it easier?)
https://immutables.github.io/
Lombok, AutoValue, and Immutables
Project sample?
https://github.com/yegor256/takes
- Yegor Bugayenko (http://www.yegor256.com/)
1. not a single null (why NULL is bad?)
2. not a single public static method (why they are bad?)
3. not a single mutable class (why they are bad?)
4. not a single instanceof keyword, type casting, or reflection (why?)
‫״‬Simplicity is hard work. But, there's a huge payoff. The person
who has a genuinely simpler system - a system made out of
genuinely simple parts, is going to be able to affect the greatest
change with the least work. He's going to kick your ass. He's
gonna spend more time simplifying things up front and in the long
haul he's gonna wipe the plate with you because he'll have that
ability to change things when you're struggling to push elephants
around.‫״‬
— Rich Hickey, Creator of the Clojure programming language
Make Your Life Better With
Immutable Objects
Maxim Novak, Wix
https://github.com/maximn@maximnovakmaximn@wix.com

Make your life better with immutable objects

  • 1.
    Make Your LifeBetter With Immutable Objects Maxim Novak, Wix https://github.com/maximn@maximnovakmaximn@wix.com
  • 2.
    I’m Maxim Novak ▪Avid Scala advocate ▪ Team leader at Wix ▪ Building core services ▪ 10 years developing software ▪ TDD and clean code enthusiast Lithuania Vilnius Kyiv Dnipro Wix Engineering Locations Israel Tel-Aviv Be’er Sheva Ukraine
  • 3.
    What’s an immutableclass? An object is considered immutable if its state cannot change after it is constructed. Maximum reliance on immutable objects is widely accepted as a sound strategy for creating simple, reliable code. ... - Oracle Java documentation https://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html
  • 4.
    GOOD QUESTION But theymade the most important one immutable java.lang.String So why are most Java classes mutable?
  • 5.
    What do youmean can’t change it ?! So how do I work with it? Create a new instance with the modified state
  • 6.
    Person Name Birth Date Address Huge data Person moved= person.moveTo(newAddress); Something Something Something
  • 7.
  • 8.
    Advantages (Why do youwant it) You get a much simpler programming model
  • 9.
    “I think thatlarge objected-oriented programs struggle with increasing complexity as you build this large object graph of mutable objects. You know, trying to understand and keep in your mind what will happen when you call a method and what will the side effects be.” - Rich Hickey (2010) Easier to reason about https://pixabay.com/en/fly-swatter-flyswatter-fly-flap-bug-149265/
  • 10.
    void foo(SomeInterface func){ Data data = new Data("initial state"); func.apply(data); log(data); } Easier to reason about Side-effect free (share safely)
  • 11.
  • 12.
  • 13.
    void addItemToCart() throwsIOException { Request req = Request.Post("http://example.com"); req.setHeader("Authorization", "token"); req.bodyForm( new BasicNameValuePair("action", "create-cart")); req.execute(); req.bodyForm( new BasicNameValuePair("action", "add-item")); req.execute(); } AddItem CreateCart
  • 14.
    void addItemToCart() throwsIOException { Request req = Request.Post("http://example.com"); req.setHeader("Authorization", "token"); req.bodyForm( new BasicNameValuePair("action", "create-cart")); req.execute(); req.bodyForm( new BasicNameValuePair("action", "add-item")); req.execute(); } AddItem CreateCart
  • 15.
    void addItemToCart() throwsIOException { Request req = Request.Post("http://example.com"); req.setHeader("Authorization", "token"); req.bodyForm( new BasicNameValuePair("action", "create-cart")); req.execute(); req.bodyForm( new BasicNameValuePair("action", "add-item")); req.execute(); } AddItem CreateCart
  • 16.
    void addItemToCart() throwsIOException { Request req = Request.Post("http://example.com"); req.setHeader("Authorization", "token"); req.bodyForm( new BasicNameValuePair("action", "create-cart")); req.execute(); req.bodyForm( new BasicNameValuePair("action", "add-item")); req.execute(); } AddItem CreateCart
  • 17.
    void addItemToCart() throwsIOException { Request req = Request.Post("http://example.com"); req.setHeader("Authorization", "token"); req.bodyForm( new BasicNameValuePair("action", "create-cart")); req.execute(); addItem(req); } private void addItem(Request req) throws IOException { req.bodyForm( new BasicNameValuePair("action", "add-item")); req.execute(); } CreateCart
  • 18.
    void addItemToCart() throwsIOException { Request req = Request.Post("http://example.com"); createCart(req); addItem(req); } private void createCart(Request req) throws IOException { req.setHeader("Authorization", "token"); req.bodyForm( new BasicNameValuePair("action", "create-cart")); req.execute(); }
  • 19.
    void addItemToCart() throwsIOException { Request req = Request.Post("http://example.com"); createCart(req); addItem(req); } private void createCart(Request req) throws IOException { req.setHeader("Authorization", "token"); req.bodyForm( new BasicNameValuePair("action", "create-cart")); req.execute(); }
  • 20.
  • 21.
    Set<Date> set =new HashSet<>(); Date date = new Date(2); set.add(date); date.setTime(4); System.out.println(set.contains(date)); HashCode Object 1 2 3 4 ... ... n date(2) date Avoiding Identity Mutability
  • 22.
  • 23.
    public class Person{ private String firstName; private String lastName; public Person() { } public void setName(String firstName, String lastName) { throwOnInvalid(firstName); this.firstName = firstName; throwOnInvalid(lastName); this.lastName = lastName; } ... BOOM!!!
  • 24.
  • 25.
    Advantages (Why do youwant it) ● Easier to reason about ● Side-effect free (share safely, no defensive copies) ● Easier to test ● Easier to debug ● Avoid temporal coupling ● Avoiding Identity Mutability ● Always have failure atomicity / Prevent NULL references ● Always Thread safe
  • 26.
    Disadvantages (What you shouldbeware of ) ● Performance under certain conditions. ● Brings some difficulties when working in Java
  • 27.
    So how doI start? (some rules of thumb) ● Make all fields final & private ● Create methods that create a copy of the class with a modified state ● Disallow inheritance (final class)
  • 28.
    So how doI start? (some rules of thumb) ● If you’re using mutable classes inside ○ Copy them on construction ○ Don’t expose references to internal fields (copy defensively) ○ Don’t provide any method that changes the state (setters)
  • 29.
    So how doI start? (How can I make it easier?) https://immutables.github.io/ Lombok, AutoValue, and Immutables
  • 30.
    Project sample? https://github.com/yegor256/takes - YegorBugayenko (http://www.yegor256.com/) 1. not a single null (why NULL is bad?) 2. not a single public static method (why they are bad?) 3. not a single mutable class (why they are bad?) 4. not a single instanceof keyword, type casting, or reflection (why?)
  • 31.
    ‫״‬Simplicity is hardwork. But, there's a huge payoff. The person who has a genuinely simpler system - a system made out of genuinely simple parts, is going to be able to affect the greatest change with the least work. He's going to kick your ass. He's gonna spend more time simplifying things up front and in the long haul he's gonna wipe the plate with you because he'll have that ability to change things when you're struggling to push elephants around.‫״‬ — Rich Hickey, Creator of the Clojure programming language
  • 32.
    Make Your LifeBetter With Immutable Objects Maxim Novak, Wix https://github.com/maximn@maximnovakmaximn@wix.com