Zasada DRY, to po przełożeniu na język polski: nie powtarzaj się. Brzmi bardzo banalnie, jednak dosyć często okazuje się, że mamy problem z jej stosowaniem w kodzie. Dziś dowiesz się, jak nie łamać tej reguły, a co za tym idzie nie powtarzać się.
Co znaczy DRY?
Zasada „don’t repeat yourself” mówi nam, abyśmy wydzielali w naszym kodzie miejsca, które będą realizowały konkretną logikę. Dzięki odwoływaniu się do takich funkcji czy komponentów, które są jedynym źródłem prawdy dla realizowanych operacji, nie powielamy tego samego kodu, a nasz system staje się spójniejszy.
Kod odpowiedzialny za realizację zadania X nie powinien być nigdy powielany. Umieść go w miejscu najbardziej dla niego odpowiednim i odwołuj się do niego za pomocą funkcji i metod.
Zasada DRY w praktyce!
Wyobraźmy sobie klasę typu serwis, która realizuje pewne zadania operacji na danych. Udostępnia ona między innymi metody: delete(), update() czy notify(). W każdej z tych metod musimy najpierw sprawdzić czy wpis istnieje, zanim wykonamy jakąś operację. Jeżeli zasób nie istnieje zwracamy stosowny błąd.
class PostsService {
constructor(
private readonly postRepository: IPostRepository,
private readonly notificationsService: INotificationsService
) {}
async delete(postId: string) {
const post = await this.postRepository.findOneById(postId);
if (!post) {
throw new LogicError(`Selected post ${postId} does not exist.`);
}
await this.postRepository.delete({ id: postId });
}
async notifyAbout(postId: string) {
const post = await this.postRepository.findOneById(postId);
if (!post) {
throw new LogicError(`Selected post ${postId} does not exist.`);
}
await this.notificationsService.notify(
new PostNotification(post)
);
}
async update({ postId, ...dataToUpdate }: UpdatePostDto) {
const post = await this.postRepository.findOneById(postId);
if (!post) {
throw new LogicError(`Selected post ${postId} does not exist.`);
}
await this.postRepository.update({ id: postId }, dataToUpdate);
}
}
Jak się pewnie domyślasz, w każdej z powyższych metod powieliliśmy operację pobierania zasobu i reagowania na sytuację, kiedy on nieistnieje. Przez to złamaliśmy zasadę DRY!
Programowanie z DRY
Zaprogramowanie powyższego przykładu zgodnie z DRY nie jest trudne i zrobić to możemy na naprawdę wiele sposobów. W naszym repozytorium możemy stworzyć metodę findOneByIdOrFail(), która w przypadku nieistniejącego zasobu zwróci stosowny błąd. Możemy także wydestylować powielaną logikę do prywatnej metody w serwisie.
class PostsService {
constructor(
private readonly postRepository: IPostRepository,
private readonly notificationsService: INotificationsService
) {}
async delete(postId: string) {
const post = await this.getByIdOrFail(postId);
await this.postRepository.delete({ id: postId });
}
async notifyAbout(postId: string) {
const post = await this.getByIdOrFail(postId);
await this.notificationsService.notify(
new PostNotification(post)
);
}
async update({ postId, ...dataToUpdate }: UpdatePostDto) {
const post = await this.getByIdOrFail(postId);
await this.postRepository.update({ id: postId }, dataToUpdate);
}
private async getByIdOrFail(postId: string): Promise<Post> {
const post = await this.postRepository.findOneById(postId);
if (!post) {
throw new LogicError(`Selected post ${postId} does not exist.`);
}
return post;
}
}
Osobiście, wolę jednak wersję z repozytorium, ponieważ takie zachowanie, może być potrzebne np. w innym serwisie.
class PostsService {
constructor(
private readonly postRepository: IPostRepository,
private readonly notificationsService: INotificationsService
) {}
async delete(postId: string) {
const post = await this.postRepository.getByIdOrFail(postId);
await this.postRepository.delete({ id: postId });
}
async notifyAbout(postId: string) {
const post = await this.postRepository.getByIdOrFail(postId);
await this.notificationsService.notify(
new PostNotification(post)
);
}
async update({ postId, ...dataToUpdate }: UpdatePostDto) {
const post = await this.postRepository.getByIdOrFail(postId);
await this.postRepository.update({ id: postId }, dataToUpdate);
}
}
DRY vs. SRP
DRY można mylić z Single Responsibility Principle z SOLID-a. Nie należy ich jednak postrzegać, jako tożsamych samych zasad, ponieważ możemy spełniać SRP, zaś nie spełniać DRY i na odwrót. Metoda może mieć jeden powód do zmiany, jedno zadanie do realizacji i tym samym spełniać solid-owe S, ale może także duplikować kod znajdujący się w trzech innych metodach i nie spełniać DRY.
Podsumowanie
- DRY to zasada mówiąca o tym, aby nie powtarzać swojego kodu.
- Zgodnie z „don’t repeat yourself”, każda funkcjonalność powinna posiadać dedykowane dla siebie miejsce w systemie.
- Nie należy mylić SRP z DRY, ponieważ są to reguły dotyczące innych aspektów.
Dodaj komentarz