Until now we learned how to create a Value Object. But we’ve seen that 2 instances of the same value object created with the same value do not equal each other. How can we make sure that their equality is based on value instead of identity? Let’s take a more practical example!
Note: This is the fourth post in a series about the use of Value Objects in JavaScript.
Registration form
We are going to build a very simple registration form with an email field and two password fields. When the user submits the form we want to validate the input before posting it to the server. When validation fails we prevent the post and show the validation error to the user.
The HTML might look something like this:
1 2 3 4 5 6 7 8 9 10 11 |
<form action="/register" method="POST" onsubmit="return validate(this);"> <input id="email" type="email" placeholder="Email address"> <input id="password" type="password" placeholder="Password"> <input id="password_repeat" type="password" placeholder="Password repeat"> <input type="submit" value="Register"> </form> |
And the JavaScript like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
var emailInput = document.getElementById('email'); var passwordInput = document.getElementById('password'); var passwordRepeatInput = document.getElementById('password-repeat'); function validate() { try { // Validate, throw exception on fail return true; } catch (e) { alert(e.message); return false; } } |
In the validation part we need to verify that all fields are filled, that the email address is valid, that the password is strong enough and that the second password matches the first one. We will do this by first creating a Value Object for both email address and password.
EmailAddress and Password
We define EmailAddress in the same way as Integer in the previous post.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var EmailAddress = function(value) { if ( ! /^[^@]+@[^@]+\.[^@]+$/.test(value)) { throw new Error( value + ' is not a valid EmailAddress'); } this.value = value; } EmailAddress.prototype.valueOf = function() { return this.value; } new EmailAddress("www.google.com"); // throws exception new EmailAddress("test@example.com"); // success |
There is not much to it really. The only thing that makes the difference is the validation part. In this case we use a simple regular expression that tests the passed value for an @ and a dot with some other characters around it, but of course, you can make the regex as strict as you want. If the test fails we throw an exception stating that the value is not a valid email address.
Extra methods could be added to the object to access specific parts of the email address like the local part (before the @) and the domain part (after the @).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
EmailAddress.prototype.getLocalPart = function() { return this.value.split('@').shift(); } EmailAddress.prototype.getDomainPart = function() { return this.value.split('@').pop(); } var email = new EmailAddress('test@example.com'); email.getLocalPart(); // "test" email.getDomainPart(); // "example.com" |
Passwords have to be strong, so they cant be easily guessed. In this example we define strong as ‘containing at least 8 characters and including at least 1 lowercase character, 1 uppercase character and 1 digit’.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
var Password = function(value) { if ( ! /^(?=.*[a-z\W]+).*(?=.*[A-Z]+).*(?=.*[0-9]+).{8,}$/.test(value)) { throw new Error( 'A valid password should be at least 8 characters long and contain \ at least 1 lowercase character, 1 uppercase character and 1 digit.' ); } this.value = value; } Password.prototype.valueOf = function() { return this.value; } new Password("ILoveDaddy"); // throws exception new Password("Qwerty123"); // success |
Form validation
Validation is pretty simple now. We only have to create nwe instances of the defined Value Objects with the provided form input and all will be done automatically. The only thing we have to do ‘manually’ is checking if the two passwords are the same.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// .. function validate() { try { var email = new EmailAddress( emailInput.value ); var password = new Password( passwordInput.value ); var password_repeat = new Password( passwordRepeatInput.value ); if (password.value != password_repeat.value) { throw new Error('The values in the two password fields do not match.'); } return true; } catch (e) { alert(e.message); return false; } } |
Sadly, we can’t compare the password objects directly because objects are always unique. Instead we have to compare the values of the those objects. Although it isn’t the worst thing you could think off, it still looks and feels a bit weird. If we compare strings or numbers with each other we do not need to access its inner value (we actually need to if we create them from their objects instead of from literals).
But is there no way to fix this? Yes, there is!
Equality with the flyweight pattern
The Flyweight pattern is primarily used to reduce the number of objects created and to decrease memory footprint and increase performance. It will try to reuse already existing similar objects by storing them and creates new object when no matching object is found.
To simplify: Instead of creating 3 exactly the same objects only one instance is created and that one is referenced 3 times. Its works like a caching system. Of course this pattern has clear limitations. The main downside is that, if one of the references is changed, the original cached object is changed and therefor all references. So it is important that the object is immutable. Doesn’t sound that familiar?
Let’s build one for our Password VO. We start with the factory, the distributor of the flyweight objects.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var db = Object.create(null); function passwordFactory(value) { if (db[value]) { return db[value]; } return db[value] = new Password(value); } |
Simple as that! Instead of instantiating the password our self we let it be created by the factory. It will first check if it has created a password with the same value before, if so he returns it, if not he creates it, stores it and returns it.
1 2 3 4 |
passwordFactory('Qwerty123'); == passwordFactory('Qwerty123');; // true passwordFactory('Qwerty123'); === passwordFactory('Qwerty123');; // true |
We reached our goal: Value Objects being equal on value.
Refactor
Creating passwords like this doesn’t make beautiful code, but praise JavaScript for being awesome! We can rebuild the Password constructor to make it implement this pattern.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
(function() { var constructor = function(value) { if ( ! /^(?=.*[a-z\W]+).*(?=.*[A-Z]+).*(?=.*[0-9]+).{8,}$/.test(value)) { throw new Error( 'A valid password should be at least 8 characters long and contain \ at least 1 lowercase character, 1 uppercase character and 1 digit.' ); } this.value = value; } var db = Object.create(null); window.Password = function(value) { if (db[value]) { return db[value]; } return db[value] = new constructor(value); } })(); |
Defining the Password is now done inside a private scope to prevent variable name collisions, might you want to apply this pattern for more Value Objects (which you do, but more about that in the next post). The password constructor is now actually the flyweight factory that makes use of a private constructor defining the password. This gives us the ability to compare passwords that are created through pure instantiation.
1 2 3 4 5 6 7 |
new Password("Qwerty123") == new Password("Qwerty123"); // true new Password("Qwerty123") === new Password("Qwerty123"); // true new String("Qwerty123") == new String("Qwerty123"); // false new String("Qwerty123") === new String("Qwerty123"); // false |
I am really exited about this! As you see in the example, we actually beat the native data type objects, because they are not equal on value.
Back to our form validation we can now compare the two passwords without the need to access the value property of both objects.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// .. var password = new Password(passwordInput.value); var password_repeat = new Password(passwordRepeatInput.value); if (password != password_repeat) { throw new Error('The values in the two password fields do not match.'); } // .. |
Summary
We saw how Value Objects can be implemented in real life development with a practical example. Value Objects make validation easier and more readable, because they have high cohesion; everything about an email address is inside the object EmailAddress. There is no need to put any logic concerning the email address in the validation function.
A big improvement is the implementation of the Flyweight pattern that allows us to compare objects based on their value instead of their identity. This also makes our objects comply with the second key-property of values: ‘no life cycle’ (see post 2 in this series). Value Objects are no longer created (technically they are created the first time), but instead are references to an instance in the ‘universe of values’.
It is time to put all our discoveries together and create a library that helps us to generate our own Value Objects. To be continued…