You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Zasady `SOLID` opisują pięć podstawowych reguł programowania obiektowego, których celem jest tworzenie zrozumiałego, elastycznego i łatwiejszego do utrzymania kodu. Mają zastosowanie nie tylko w projekcie zorientowanym obiektowo, ale także mogą stanowić fundamenty dla metodologi pracy takich jak np. metodyki zwinne.
14
14
15
15
## Single responsibility
16
-
Zasada pojedynczej odpowiedzialności (`Single responsibility principle`) mówi, że nigdy nie powinno być więcej niż jednego powodu do modyfikacji klasy. Innymi słow każda klasa powinna być odpowiedzialna za realizacje pojedynczego tematu. Jest jednym z podstawowych elementów refaktoryzacji i skutkuje wydzieleniem mniejszych klas z jednej większej. Tworzenie kodu w oparciu o wiele małych klas zamiast kilku dużych pozwala przede wszysktim na uniknięcie powtórzeń tego samego fragmentu kodu. Modularyzacja znacząco przyczynia się do budowania zrozumiałej struktury projektu łatwiejszego w rozwoju i utrzymaniu.
16
+
Zasada pojedynczej odpowiedzialności (`Single responsibility principle`) mówi, że nigdy nie powinno być więcej niż jednego powodu do modyfikacji klasy. Innymi słowy każda klasa powinna być odpowiedzialna za realizacje pojedynczego tematu. Jest jednym z podstawowych elementów refaktoryzacji i skutkuje wydzieleniem mniejszych klas z jednej większej. Tworzenie kodu w oparciu o wiele małych klas zamiast kilku dużych pozwala przede wszysktim na uniknięcie powtórzeń tego samego fragmentu kodu. Modularyzacja znacząco przyczynia się do budowania zrozumiałej struktury projektu łatwiejszego w rozwoju i utrzymaniu.
17
17
18
18
{% highlight kotlin %}
19
19
data class Person(val name: String, val surname: String, val bornYear: Int,
20
20
val city: String, val street: String, val houseNumber: String,
21
21
val email: String, val phoneNumber: String)
22
22
{
23
23
24
-
fun createPdfSummary(): ByteArray {
25
-
//create pdf file with the data and return its bytes
24
+
fun createSummary(): ByteArray {
25
+
//create file with the data and return its bytes
26
26
return byteArrayOf() //mock
27
27
}
28
28
@@ -40,7 +40,7 @@ data class Person(val name: String, val surname: String, val bornYear: Int,
40
40
}
41
41
{% endhighlight %}
42
42
43
-
//TODO
43
+
Klasa `Person` posiada zbiór właściwości, które mogłyby zostać podzielone na mniejsze obiekty, np. klasy `Address` i `ContactDetails`. Pociąga to za sobą również przeniesienie metod `isEmailValid` i `isPhoneNumberValid` do odpowiadającej im klasy `ContactDetails` ponieważ nie leżą one teraz w obowiązku typu `Person`. Ponadto metoda `createPdfSummary` realizująca generowanie pliku przypisuje dodatkową odpowiedzialność w związku z czym należy ją oddelegować do klasy `SummaryGenerator`. Dzięki takiemu podziałowi możliwe jest ponowne wykorzystanie klas w innych miejscach aplikacji bez tworzenia nadmiarowego kodu.
44
44
45
45
{% highlight kotlin %}
46
46
data class Person(val name: String, val surname: String, val bornYear: Int,
@@ -64,10 +64,10 @@ data class ContactDetails(val email: String, val phoneNumber: String) {
64
64
}
65
65
}
66
66
67
-
object PdfGenerator {
67
+
object SummaryGenerator {
68
68
69
-
fun createPdfSummary(person: Person): ByteArray {
70
-
//create pdf file with the data and return the bytes
69
+
fun createSummary(person: Person): ByteArray {
70
+
//create file with the data and return the bytes
71
71
return byteArrayOf() //mock
72
72
}
73
73
}
@@ -120,7 +120,7 @@ class Triangle(val a: Double, val b: Double, val c: Double)
120
120
class Circle (val r: Double)
121
121
{% endhighlight %}
122
122
123
-
//TODO
123
+
Dodanie nowej klasy z możliwością obliczenia pola i obwodu lub modyfikacja bieżących może skutkować zmianą w klasie `Calculator` co nie pozwoli na rozszerzenie funkcjonalności bez naruszenia bieżącej struktury klasy. W takiej sytuacji należy posłużyć się wspólnym typem bazowym `Figure` oraz wymusić implementacje szczegółów w klasach pochodnych `Rectangle`, `Triangle`, `Circle`. Następnie w klasie `Calculator` wystarczy wywołać metody typu bazowego co pozwoli na dodanie nowych figur do kalkulatora bez konieczności modyfikacji klasy `Calculator`.
124
124
125
125
{% highlight kotlin %}
126
126
object Calculator {
@@ -242,15 +242,30 @@ class TextNote(title: String, description: String, val text)
242
242
}
243
243
{% endhighlight %}
244
244
245
-
//TODO
245
+
Klient `NotesView` wykorzystuje zbiór obiektów typu `Note` w celu odtworzenia multimedialnej zawartości notatki bezpośrednio z widoku listy. Jednakże nie każdy obiekt rozszerzający typ bazowy posiada plik multimedialny. Przykładem takiej klasy jest `TextNote` której obiekty nie są zdolne do odtworzenia zawartości pomimo zgłoszonej deklaracji. `NotesView` nie musi znać szczegółów implementacji, oczekuje od wszystkich obiektów typu `Note` poprawnej implementacji i zdolności do wykonania zadania. Jednym z rozwiązań tego problemu może być stworzenie dodatkowego typu podstawowego dla notatek multimedialnych `MediaNote` rozszerzającego klasę `Note`, który przejmuje definicję funkcji `play`. Klient `NotesView` może teraz bez przeszkód wykorzystać wszystkie obiekty typu `MediaNote` do odtworzenia zawartości.
class Note(val title: String, val description: String) {
249
263
250
264
//some methods
251
265
}
252
266
253
-
class MediaNote(title: String, description: String, val file: File) {
267
+
class MediaNote(title: String, description: String, val file: File)
268
+
: Note(title, description) {
254
269
255
270
//some media variables
256
271
@@ -323,7 +338,7 @@ interface Printable {
323
338
}
324
339
{% endhighlight %}
325
340
326
-
//TODO
341
+
Klasy `Exam` oraz `ExamResults` implementują interfejs `Printable`, który deklaruje możliwość generowania dokumentu tekstowego (`printDocument`) oraz arkusza kalkulacyjnego (`printSheet`). Jednakże eksportowanie obiektu klasy `Exam` do arkusza kalkulacyjnego nie ma sensu w związku z czym implementacja metody `printSheet` zgłosi wyjątek lub pozostanie pusta. Klient oczekujący stworzenia pliku arkusza kalkulacyjnego dla egzaminu spotka się z nieoczekiwanym zachowaniem lub wystąpieniem błędu. W związku z czym warto podzielić interfejs `Printable` na mniejsze dedykowane interfejsy `PrintableDocument` i `PrintableSheet`.
327
342
328
343
{% highlight kotlin %}
329
344
class Exam() : PrintableDocument {
@@ -379,7 +394,7 @@ class Downloader {
379
394
}
380
395
{% endhighlight %}
381
396
382
-
//TODO
397
+
Klasa `Downloader` odpowiedzialna za pobieranie informacji z serwera dokonuje szczegółowej implementacji zachowania, polegając tym samym na zależnościach niskiego poziomu. Takie podejście nie jest wskazane, gdyż silnie łączy klasę wysokiego poziomu z niskopoziomowymi obiektami. Alternatywnym podejściem będzie wprowadzenie warstwy abstrakcji w postaci interfejsu `Network` wywoływanej z klienta `Downloader`. Szczegóły implementacji niskopoziomowych obiektów zostają przeniesione do klas `RestNetwork` i `SocketNetwork` implementujących interfejs `Network` co pozwala ukryć zależności niskiego poziomu przed klientem.
0 commit comments