Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add poo/value_objects #15

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
## Programmation orientée objet

- `📑` [Abstraction - définition, utilité et exemples](poo/abstraction)
- `📑` [Value objects - définition, explications et exemples](poo/value_objects)

## Java

Expand Down
11 changes: 11 additions & 0 deletions poo/value_objects/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Value objects

> Écrit par [Emalios](https://github.com/Emalios)

Cette présentation porte sur la notion de **value object**, un type d'objet que tout le monde devrait connaître.

Bien que ces objets peuvent exister dans de nombreux langages de programmation, les exemples seront écrits en `Java` pour cette présentation.

## Table des matières

- [Value objects - définition, explications et exemples](fr/VALUE_OBJECTS.MD)
15 changes: 15 additions & 0 deletions poo/value_objects/code/Address.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
public class Address {

private final Name cityName;
private final Name streetAddress;

public Address(Name cityName, Name streetAddress) {
this.cityName = cityName;
this.streetAddress = streetAddress;
}

@Override
public String toString() {
return String.format("%s - %s", this.cityName.toString(), this.streetAddress.toString());
}
}
26 changes: 26 additions & 0 deletions poo/value_objects/code/Currency.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
public enum Currency {

EUR(new Name("€"), new USDRate(0.5)),
USD(new Name("$"), new USDRate(1)),
TEST(new Name("T"), new USDRate(2));

private final Name formattedName;
private final USDRate usdRate;

Currency(Name formattedName, USDRate usdRate) {
this.usdRate = usdRate;
this.formattedName = formattedName;
}

public static double convertMoney(Currency from, Currency to, double amount) {
if(from.equals(to)) {
return amount;
}
return from.usdRate.convert(to.usdRate, amount);
}

@Override
public String toString() {
return this.formattedName.toString();
}
}
50 changes: 50 additions & 0 deletions poo/value_objects/code/Money.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import java.util.Objects;

public class Money {

private final double value;
private final Currency currency;

public Money(double value, Currency currency) {
this.value = Math.max(0, value);
this.currency = currency;
}

public Money(double value) {
this(value, Currency.USD);
}

public Money addMoney(Money moneyToAdd) {
double convertedAmount = this.convertAmount(moneyToAdd.currency, this.currency, moneyToAdd.value);
return new Money(this.value+convertedAmount, this.currency);
}

public Money removeMoney(Money moneyToRemove) {
double convertedAmount = this.convertAmount(moneyToRemove.currency, this.currency, moneyToRemove.value);
return new Money(this.value-convertedAmount, this.currency);
}

private double convertAmount(Currency from, Currency to, double amount) {
return Currency.convertMoney(from, to, amount);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Money money = (Money) o;
return value == money.value &&
currency == money.currency;
}

@Override
public int hashCode() {
return Objects.hash(value, currency);
}

@Override
public String toString() {
return String.format("%s%s", this.value, this.currency);
}

}
28 changes: 28 additions & 0 deletions poo/value_objects/code/Name.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import java.util.Objects;

public class Name {

private final String value;

public Name(String name) {
this.value = name;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Name name = (Name) o;
return Objects.equals(value, name.value);
}

@Override
public int hashCode() {
return Objects.hash(value);
}

@Override
public String toString() {
return this.value;
}
}
16 changes: 16 additions & 0 deletions poo/value_objects/code/Order.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import java.util.List;
import java.util.UUID;

public class Order {

//Not a value objet because have an id
private final UUID orderId;
private Products products;
private Address addressToDeliver;

public Order(Products products, Address addressToDeliver) {
this.orderId = UUID.randomUUID();
this.products = products;
this.addressToDeliver = addressToDeliver;
}
}
12 changes: 12 additions & 0 deletions poo/value_objects/code/Product.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
public class Product {

private final Money cost;

public Product(Money cost) {
this.cost = cost;
}

public Money addCostTo(Money money) {
return money.addMoney(cost);
}
}
24 changes: 24 additions & 0 deletions poo/value_objects/code/Products.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import java.util.ArrayList;
import java.util.List;

public class Products {

private final List<Product> products;

public Products() {
this.products = new ArrayList<>();
}

public void addProduct(Product product) {
this.products.add(product);
}

public Money getProductsCost() {
Money money = new Money(0);
for (Product product : products) {
money = product.addCostTo(money);
}
return money;
}

}
27 changes: 27 additions & 0 deletions poo/value_objects/code/USDRate.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import java.util.Objects;

public class USDRate {

private final double value;

public USDRate(double rate) {
this.value = rate;
}

public double convert(USDRate to, double amount) {
return (this.value / to.value) * amount;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
USDRate usdRate = (USDRate) o;
return Double.compare(usdRate.value, value) == 0;
}

@Override
public int hashCode() {
return Objects.hash(value);
}
}
38 changes: 38 additions & 0 deletions poo/value_objects/fr/VALUE_OBJECTS.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Value objects

## Qu'est-ce qu'un value object ?

Un *value object* se caractérise par deux caractéristiques principales que nous allons détailler ainsi que leurs conséquences.

## - Ils n'ont pas d'identité

En effet, les *value objects* ne possèdent pas d'identité, car ce n'est pas du tout ce qui nous intéresse dans ces objets, ce qui nous intéresse, c'est les valeurs qu'ils contiennent.
Pour détailler, deux *value objets* sans identités peuvent, s'ils sont équivalents, être partagés, échangés, remplacés entre eux, etc. sans aucun problème.
Alors que pour des objets classiques ça n'a pas de sens d'avoir plusieurs instances d'un objet avec le même identifiant.
Par exemple, prenons deux objets `Money`, un qui est composé d'un billet de 10€ et un autre qui est composé de deux billets de 5€.
Ces deux *value objects* **sont identiques** car ils ont la **même valeur** bien qu'ils n'aient **pas la même identité**.

## - Ils sont immuables

Pour faciliter leur utilisation, les *value objects* **doivent être immuables**.
Si vous souhaitez changer les valeurs dans le *value object*, il suffit d'en **créer un nouveau**. Bien souvent des méthodes pour faire cela sont présentes dans un *value object*.
Le fait que ces objets soient immuables leur permet d'être totalement **interchangeables** et **réutilisables**.
Par exemple, imaginons un *value objet* `Address` qui représente l'adresse d'une personne : si deux personnes habitent au même endroit, on peut **réutiliser** le même objet, ce qui optimise notre application.

## - Ils existent à travers d'autres objets

Si ces objets ne sont pas des attributs d'un « objet mère » ils n'auraient aucune utilité.
Si je reprends les exemples d'au-dessus on peut identifier certains « objets mères » potentiels :

- `Money` pourrait être l'attribut d'un portefeuille ou bien d'un compte bancaire ;

- `Address` pourrait être l'attribut d'une livraison ou bien d'une maison.

## Exemple d'implémentation

Voici une petite [implémention](../code) autour d'un système de livraison, qui lie les deux exemples `Money` et `Address`.
Ce n'est qu'une base, mais ça permet d'entrevoir ce qu'on peut faire avec les *value objects*.

## Conclusion

Les *value objects* sont des objets immuables, qui existent à travers d'autres objets, n'ayant aucune identité.