Skip to content

Commit eda486d

Browse files
committed
solid examples description added
1 parent 423067d commit eda486d

File tree

1 file changed

+27
-12
lines changed

1 file changed

+27
-12
lines changed

_drafts/2019-10-07-solid.md

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,16 @@ keywords: "zasady, reguły, obiektowe, solid, single responsibility, open-close,
1313
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.
1414

1515
## 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.
1717

1818
{% highlight kotlin %}
1919
data class Person(val name: String, val surname: String, val bornYear: Int,
2020
val city: String, val street: String, val houseNumber: String,
2121
val email: String, val phoneNumber: String)
2222
{
2323

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
2626
return byteArrayOf() //mock
2727
}
2828

@@ -40,7 +40,7 @@ data class Person(val name: String, val surname: String, val bornYear: Int,
4040
}
4141
{% endhighlight %}
4242

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.
4444

4545
{% highlight kotlin %}
4646
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) {
6464
}
6565
}
6666

67-
object PdfGenerator {
67+
object SummaryGenerator {
6868

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
7171
return byteArrayOf() //mock
7272
}
7373
}
@@ -120,7 +120,7 @@ class Triangle(val a: Double, val b: Double, val c: Double)
120120
class Circle (val r: Double)
121121
{% endhighlight %}
122122

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`.
124124

125125
{% highlight kotlin %}
126126
object Calculator {
@@ -242,15 +242,30 @@ class TextNote(title: String, description: String, val text)
242242
}
243243
{% endhighlight %}
244244

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.
246246

247247
{% highlight kotlin %}
248+
class NotesView() {
249+
250+
val notes = mutableListOf<MediaNote>()
251+
252+
fun init() {
253+
notes.add(AudioNote("audio", "description", File("audio.mp3"), "transcription"))
254+
notes.add(VideoNote("video", "description", File("video.mp4"), File("subtitlex.txt")))
255+
}
256+
257+
fun playNote(index: Int) {
258+
notes(index).play()
259+
}
260+
}
261+
248262
class Note(val title: String, val description: String) {
249263

250264
//some methods
251265
}
252266

253-
class MediaNote(title: String, description: String, val file: File) {
267+
class MediaNote(title: String, description: String, val file: File)
268+
: Note(title, description) {
254269

255270
//some media variables
256271

@@ -323,7 +338,7 @@ interface Printable {
323338
}
324339
{% endhighlight %}
325340

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`.
327342

328343
{% highlight kotlin %}
329344
class Exam() : PrintableDocument {
@@ -379,7 +394,7 @@ class Downloader {
379394
}
380395
{% endhighlight %}
381396

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.
383398

384399
{% highlight kotlin %}
385400
class Downloader(val network: Network) {

0 commit comments

Comments
 (0)