T: / Corrigés des challenges / TypeScript
Découpage de la logique grâce à la programmation orientée objet en TypeScript
On poursuit les corrigés de l’édition 2025 de la Battle Dev Thales. Dans ce challenge, il fallait identifier les créneaux respectant à la fois des contraintes temporelles et des seuils d’intensité sonore.
Ce challenge qui présentait beaucoup de données à structurer correctement en amont, est une bonne occasion pour mettre en pratique la programmation orientée objet en TypeScript.
On va créer 3 classes :
On va réaliser le parsing des données issues du challenge dans le constructeur de la classe :
class Detection {
time: number;
intensity: number;
constructor(rawData: string) {
const [time, intensity] = rawData.split(":").map(Number);
this.time = time;
this.intensity = intensity;
}
}
Un peu d’explications :
On va à nouveau réaliser le parsing des données dans le constructeur, mais on aura également une méthode permettant de calculer l’intensité totale du créneau :
class InterventionWindow {
start: number;
end: number;
total: number = 0;
constructor(rawData: string) {
const [start, end] = rawData.split("_").map(Number);
this.start = start;
this.end = end;
}
computeIntensity(detections: Detection[]): void {
this.total = detections
.filter(d => d.time >= this.start && d.time <= this.end)
.reduce((sum, d) => sum + d.intensity, 0);
}
}
Un peu d’explications :
Les deux premières classes définissent nos briques de base. Il nous reste maintenant à les faire collaborer dans une structure centrale : MissionControl.
On commence par son constructeur qui va permettre de stocker les données correctement :
class MissionControl {
interventionWindows: InterventionWindow[];
detections: Detection[];
threshold: number;
latency: number;
constructor(windowsData: string[], detectionsData: string[], threshold: number, latency: number) {
this.interventionWindows = windowsData.map(w => new InterventionWindow(w));
this.detections = detectionsData.map(d => new Detection(d));
this.threshold = threshold;
this.latency = latency;
}
}
Un peu d’explications :
On crée ensuite 3 méthodes :
computeAllIntensities(): void {
this.interventionWindows.forEach(w =>
w.computeIntensity(this.detections)
);
}
filterValidWindows(): InterventionWindow[] {
return this.interventionWindows
.filter(w => w.total <= this.threshold)
.sort((a, b) => a.start - b.start);
}
selectSequentialWindows(validWindows: InterventionWindow[]): InterventionWindow[] {
const selected: InterventionWindow[] = [];
let lastEnd = -Infinity;
for (const w of validWindows) {
if (w.start - lastEnd >= this.latency) {
selected.push(w);
lastEnd = w.end;
}
}
return selected;
}
Un peu d’explications :
Et une dernière méthode qui permet d’orchestrer la résolution du challenge :
execute(): string {
// Mécaniques
this.computeAllIntensities();
const valid: InterventionWindow[] = this.filterValidWindows();
const selected: InterventionWindow[] = this.selectSequentialWindows(valid);
// Construction de la réponse
const count: number = selected.length;
const totalIntensity: number = selected.reduce((sum, w) => sum + w.total, 0);
return `${count}:${totalIntensity}`;
}
Un peu d’explications :
Et enfin, l’appel de cette classe dans le programme principal :
const mission = new MissionControl(windows, detections, threshold, latency);
console.log(mission.execute());
Ce challenge est un excellent exemple de la puissance combinée de la programmation orientée objet (POO) et du typage fort en TypeScript.
Grâce à la POO, chaque entité du problème possède sa propre responsabilité et son comportement clairement définis. Le code devient ainsi plus expressif, plus facile à maintenir, et surtout plus proche du langage métier.
Annexe, le code complet de MissionControl :
class MissionControl {
interventionWindows: InterventionWindow[];
detections: Detection[];
threshold: number;
latency: number;
constructor(windowsData: string[], detectionsData: string[], threshold: number, latency: number) {
this.interventionWindows = windowsData.map(w => new InterventionWindow(w));
this.detections = detectionsData.map(d => new Detection(d));
this.threshold = threshold;
this.latency = latency;
}
computeAllIntensities(): void {
this.interventionWindows.forEach(w =>
w.computeIntensity(this.detections)
);
}
filterValidWindows(): InterventionWindow[] {
return this.interventionWindows
.filter(w => w.total <= this.threshold)
.sort((a, b) => a.start - b.start);
}
selectSequentialWindows(validWindows: InterventionWindow[]): InterventionWindow[] {
const selected: InterventionWindow[] = [];
let lastEnd = -Infinity;
for (const w of validWindows) {
if (w.start - lastEnd >= this.latency) {
selected.push(w);
lastEnd = w.end;
}
}
return selected;
}
execute(): string {
// Mécaniques
this.computeAllIntensities();
const valid: InterventionWindow[] = this.filterValidWindows();
const selected: InterventionWindow[] = this.selectSequentialWindows(valid);
// Construction de la réponse
const count: number = selected.length;
const totalIntensity: number = selected.reduce((sum, w) => sum + w.total, 0);
return `${count}:${totalIntensity}`;
}
}
Other content to discover