Compare commits

...

6 Commits

Author SHA1 Message Date
Namu
381e3cc9ac Feat: Decorator and facade implemented
Some checks failed
SonarQube Scan / SonarQube Trigger (push) Failing after 19s
2025-10-06 09:50:55 +02:00
Namu
d868ec63c9 Refactor: Make the observable match the new Notification logic 2025-10-06 09:15:36 +02:00
Namu
3065d1bfe3 Refactor: Make the sending strategy match the new Notification logic 2025-10-06 09:12:42 +02:00
Namu
5a97669665 Refactor: Make the Notification not send themself but rather create their content 2025-10-06 09:08:51 +02:00
Namu
2ad93f4b91 Doc: Add a text explaining why I won't implement de decorator pattern 2025-10-02 09:57:39 +02:00
Namu
a26a8c6cf1 Feat: Add facade pattern to send notification 2025-10-02 09:35:00 +02:00
23 changed files with 231 additions and 120 deletions

View File

@@ -1,31 +1,8 @@
import entities.User;
import utils.*;
import java.util.Arrays;
import static utils.NotificationType.*;
public class Application {
private final NotificationManager notificationManager;
public Application(NotificationManager notificationManager) {
this.notificationManager = notificationManager;
}
public void run() {
NotificationType[] typesThomas = {Sms, Mail, Push};
NotificationType[] typesAlexandre = {Sms, Mail};
SendNotificationStrategy secured = new SecureSendNotificationStrategy(123);
DifferedSendNotificationStrategy differed = new DifferedSendNotificationStrategy(300);
var thomas = new User(0, "Thomas", Arrays.asList(typesThomas), secured);
var alexandre = new User(1, "Alexandre", Arrays.asList(typesAlexandre), differed);
notificationManager.getObservable().addSubscribers(thomas);
notificationManager.getObservable().addSubscribers(alexandre);
notificationManager.getObservable().sendNotifications("C# > Java (mais Rust c'est le goat)");
SendNotificationFacade.sendNotifications();
}
}

View File

@@ -1,8 +1,4 @@
import utils.NotificationManager;
void main(String[] args) {
final var notificationManager = NotificationManager.getInstance();
final var app = new Application(notificationManager);
final var app = new Application();
app.run();
}

View File

@@ -0,0 +1,29 @@
package decorator;
import entities.Notification;
import notificationsDecorations.NotificationDecoration;
/**
* Ces approches ne seront pas implémentées ici, car elles pourraient alourdir le code sans apport clair.
* Par exemple, un builder pourrait être utile pour construire des notifications (objets complexes),
* ou des stratégies pourraient être envisagées pour des fonctionnalités comme la datation.
* Les design patterns ne sont pas des solutions universelles : leur utilisation doit être justifiée par un besoin concret.
* Le code actuel ne respecte pas pleinement les principes SOLID, ce qui peut rendre sa maintenance plus difficile.
* L'ajout de patterns comme le Singleton (considéré comme un anti-pattern dans certains contextes,
* notamment parmi les principes STUPID) ne résoudrait pas nécessairement ces problèmes.
* Pour s'entraîner, les Katas sont une excellente alternative : ce sont des exercices simples et variés,
* idéaux pour explorer les design patterns de manière propre et progressive.
* Ils permettent aussi de comparer différentes solutions pour un même problème, ce qui est très formateur !
*/
public abstract class BaseDecorator {
protected Notification notification;
protected NotificationDecoration decoration;
public BaseDecorator(Notification notification) {
this.notification = notification;
}
public abstract String getContent();
}

View File

@@ -0,0 +1,23 @@
package decorator;
import entities.Notification;
import notificationsDecorations.DateNotificationDecoration;
import java.util.Date;
public class DateDecorator extends BaseDecorator {
public DateDecorator(Notification notification, Date date) {
super(notification);
notification.addDecoration(new DateNotificationDecoration(date));
}
public DateDecorator(BaseDecorator decorator, Date date) {
super(decorator.notification);
notification.addDecoration(new DateNotificationDecoration(date));
}
@Override
public String getContent() {
return notification.getContent();
}
}

View File

@@ -0,0 +1,21 @@
package decorator;
import entities.Notification;
import notificationsDecorations.WatermarkNotificationDecoration;
public class WatermarkDecorator extends BaseDecorator {
public WatermarkDecorator(Notification notification, String watermark) {
super(notification);
notification.addDecoration(new WatermarkNotificationDecoration(watermark));
}
public WatermarkDecorator(BaseDecorator decorator, String watermark) {
super(decorator.notification);
notification.addDecoration(new WatermarkNotificationDecoration(watermark));
}
@Override
public String getContent() {
return this.notification.getContent();
}
}

View File

@@ -6,11 +6,4 @@ public class MailNotification extends Notification {
public MailNotification(String content, User user, SendNotificationStrategy strategy) {
super(content, user, strategy);
}
@Override
public void send() {
if (isSendable())
strategy.send(user.id() + " Mail: " + user.name() + " " + content);
}
}

View File

@@ -1,11 +1,20 @@
package entities;
import notificationsDecorations.NotificationDecoration;
import utils.NotificationState;
import utils.SendNotificationStrategy;
import java.util.ArrayList;
import java.util.List;
public abstract class Notification {
protected String content;
protected User user;
protected List<NotificationDecoration> decorations;
/**
* Pas de change state car chemin multiple. On reste sur des enum !
*/
protected NotificationState state;
protected SendNotificationStrategy strategy;
@@ -14,11 +23,54 @@ public abstract class Notification {
this.user = user;
this.state = NotificationState.PENDING;
this.strategy = strategy;
this.decorations = new ArrayList<>();
}
/**
* Adds a decoration to the notification. Please use a decorator for that
* @param decoration
*/
public void addDecoration(NotificationDecoration decoration) {
decorations.add(decoration);
}
protected boolean isSendable() {
return this.state != NotificationState.SENT && this.state != NotificationState.READ;
}
public abstract void send();
/**
* Transition d'état spéciale pour indiquer que l'envoie de notification est en échec
*/
public void setKoState() {
state = NotificationState.KO;
}
/**
* Fait la transition des états
*/
public void transitionState() {
switch (state) {
case PENDING -> state = NotificationState.SENT;
case SENT -> state = NotificationState.READ;
}
}
public String getContent() {
if (isSendable() && content != "") {
transitionState();
StringBuilder builder = new StringBuilder();
if (decorations.isEmpty())
return content;
builder.append(content);
for (final NotificationDecoration decoration : decorations)
builder.append(decoration.transformContent());
return builder.toString();
}
setKoState();
return "";
}
}

View File

@@ -6,10 +6,4 @@ public class PushNotification extends Notification {
public PushNotification(String content, User user, SendNotificationStrategy strategy) {
super(content, user, strategy);
}
@Override
public void send() {
if (isSendable())
strategy.send(user.id() + " Push: " + user.name() + " " + content);
}
}

View File

@@ -7,10 +7,4 @@ public class SmsNotification extends Notification {
public SmsNotification(String content, User user, SendNotificationStrategy strategy) {
super(content, user, strategy);
}
@Override
public void send() {
if (isSendable())
strategy.send(user.id() + " Sms: " + user.name() + " " + content);
}
}

View File

@@ -5,5 +5,10 @@ import utils.SendNotificationStrategy;
import java.util.List;
public record User(int id, String name, List<NotificationType> notificationsTypes, SendNotificationStrategy chosenNotificationStrategy) {
public record User(
int id,
String name,
List<NotificationType> notificationsTypes,
SendNotificationStrategy chosenNotificationStrategy
) {
}

View File

@@ -0,0 +1,17 @@
package notificationsDecorations;
import java.util.Date;
public class DateNotificationDecoration extends NotificationDecoration {
private final Date date;
public DateNotificationDecoration(Date date) {
super();
this.date = date;
}
@Override
public String transformContent() {
return ": " + date.toString();
}
}

View File

@@ -0,0 +1,21 @@
package notificationsDecorations;
import entities.Notification;
/**
* On cherche à pouvoir ajouter des décorations aux notifications.
*
* Pour ce faire, on part de cette classe abstraite qui permettra de faire du polymorphisme.
*
* Ensuite, les décorateurs ajouteront une décoration aux notifications qui ont une liste de
* décorations.
*/
public abstract class NotificationDecoration {
protected Notification notification;
/**
* Each decoration apply a transformation on the content of the Notifications
* @return the content transformed
*/
public abstract String transformContent();
}

View File

@@ -0,0 +1,14 @@
package notificationsDecorations;
public class WatermarkNotificationDecoration extends NotificationDecoration {
private String waterMark;
public WatermarkNotificationDecoration(String waterMark) {
this.waterMark = waterMark;
}
@Override
public String transformContent() {
return "\n" + waterMark;
}
}

View File

@@ -8,14 +8,14 @@ public class DifferedSendNotificationStrategy implements SendNotificationStrateg
}
@Override
public NotificationState send(String message) {
public String makeContent(String message) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
System.err.println("Cannot wait for differed strategy");
return NotificationState.KO;
message = "";
}
System.out.println(message);
return NotificationState.SENT;
return message;
}
}

View File

@@ -2,8 +2,7 @@ package utils;
public class FastSendNotificationStrategy implements SendNotificationStrategy {
@Override
public NotificationState send(String message) {
System.out.println(message);
return NotificationState.SENT;
public String makeContent(String message) {
return message;
}
}

View File

@@ -1,5 +0,0 @@
package utils;
public class NotificationFacade {
}

View File

@@ -1,8 +1,12 @@
package utils;
import decorator.BaseDecorator;
import decorator.DateDecorator;
import decorator.WatermarkDecorator;
import entities.User;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class NotificationObservable {
@@ -22,7 +26,9 @@ public class NotificationObservable {
for (final var subscriber : this.subscribers) {
for (final var notificationType : subscriber.notificationsTypes()) {
var notification = factory.createNotification(notificationType, content, subscriber, subscriber.chosenNotificationStrategy());
notification.send();
// Ici j'applique automatiquement les décorations, évidement, il serait mieux de la faire dynamiquement.
BaseDecorator decorator = new WatermarkDecorator(new DateDecorator(notification, new Date()), "Thomas");
System.out.println(decorator.getContent());
}
}
}

View File

@@ -1,17 +0,0 @@
package utils;
public class PriorityDecorator {
private boolean isPriority;
public PriorityDecorator(boolean isPriority) {
this.isPriority = isPriority;
}
public boolean isPriority() {
return isPriority;
}
public void setPriority(boolean priority) {
isPriority = priority;
}
}

View File

@@ -1,17 +0,0 @@
package utils;
public class ReceiptDecorator {
boolean received;
public ReceiptDecorator() {
this.received = false;
}
public boolean isReceived() {
return received;
}
public void setReceived(boolean received) {
this.received = received;
}
}

View File

@@ -8,8 +8,7 @@ public class SecureSendNotificationStrategy implements SendNotificationStrategy
}
@Override
public NotificationState send(String message) {
System.out.println(key+"Flcjsdjknesdlkgjsilkqzfjazio"+message+"kjhsdiuhisehifsdhfiuhsuidhuifhjsid");
return NotificationState.SENT;
public String makeContent(String message) {
return key+"Flcjsdjknesdlkgjsilkqzfjazio"+message+"kjhsdiuhisehifsdhfiuhsuidhuifhjsid";
}
}

View File

@@ -0,0 +1,29 @@
package utils;
import entities.User;
import java.util.Arrays;
import static utils.NotificationType.*;
import static utils.NotificationType.Mail;
import static utils.NotificationType.Sms;
public class SendNotificationFacade {
public static void sendNotifications() {
var notificationManager = NotificationManager.getInstance();
NotificationType[] typesThomas = {Sms, Mail, Push};
NotificationType[] typesAlexandre = {Sms, Mail};
SendNotificationStrategy secured = new SecureSendNotificationStrategy(123);
DifferedSendNotificationStrategy differed = new DifferedSendNotificationStrategy(300);
var thomas = new User(0, "Thomas", Arrays.asList(typesThomas), secured);
var alexandre = new User(1, "Alexandre", Arrays.asList(typesAlexandre), differed);
notificationManager.getObservable().addSubscribers(thomas);
notificationManager.getObservable().addSubscribers(alexandre);
notificationManager.getObservable().sendNotifications("C# > Java (mais Rust c'est le goat)");
}
}

View File

@@ -1,5 +1,5 @@
package utils;
public interface SendNotificationStrategy {
NotificationState send(String message);
String makeContent(String message);
}

View File

@@ -1,19 +0,0 @@
package utils;
import java.util.Date;
public class TimeStampDecorator {
private Date time;
public TimeStampDecorator(Date time) {
this.time = time;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
}