Programowanie Obiektowe Wykad 7 dr Piotr Jastrzbski Wydzia
Programowanie Obiektowe – Wykład 7 dr Piotr Jastrzębski Wydział Matematyki i Informatyki http: //wmii. uwm. edu. pl/~piojas/ piojas@matman. umw. edu. pl
Tematyka wykładu Zasady dobrego programowania Zasady programowania obiektowego S. O. L. I. D. Obsługa wyjątków
Filary programowania obiektowego Abstrakcja Hermetyzacja Dziedziczenie Polimorfizm
Zasady dobrego oprogramowania Keep it Simple Stupid (KISS) - Buzi (Bez Udziwnień Zapisu, Idioto) Don’t Repeat Yourself (DRY) Tell Don’t Ask You Aren't Gonna Need It (YAGNI) Separation of Concerns
Keep it Simple Stupid (KISS) - Buzi (Bez Udziwnień Zapisu, Idioto) kod ma być prosty i zrozumiały unikanie skomplikowanych zapisów nie oznacza upraszczania za wszelką cenę
Don’t Repeat Yourself (DRY) unikanie powtórzeń w kodzie – u nas to np. polimorficzność, stałe oddzielenie powtarzającej się części od metody zalety: unikanie błędów, poprawki możemy dodać w jednym miejscu
Tell Don’t Ask związane z hermetyzacją i podziałem obowiązków należy mówić obiektom jakie akcje mają wykonywać nie należy uzależnia wykonania akcji od stanu w jakim znajdują się obiekty
public class TDA_Zły. Przykład { public void Oblicz. Cene(Pojazd pojazd, Kalkulator kalkulator, int znizka) { var cena. Netto = znizka > 5 ? pojazd. cena. Koncowa - 5000 : pojazd. cena. Koncowa - 3000; if (cena. Netto > kalkulator. cena. Podstawowa) { kalkulator. Dolicz. Akcesoria(); } else { // bez akcesoriów } } } public class Kalkulator { internal int cena. Podstawowa; public void Dolicz. Akcesoria() { } } public class Pojazd { internal int cena. Koncowa; }
public class TDA_Dobry. Przyklad { public void Oblicz. Cene(Pojazd pojazd, Kalkulator kalkulator, int znizka) { kalkulator. Oblicz. Cene. Netto(pojazd, znizka); } } public class Kalkulator { internal int cena. Podstawowa; public void Oblicz. Cene. Netto(Pojazd pojazd, int znizka) { var cena. Netto = znizka > 5 ? pojazd. cena. Koncowa - 10000 : pojazd. cena. Koncowa 20000; if (cena. Netto > this. cena. Podstawowa) { this. Dodaj. Akcesoria(); } else { // bez akcesoriów } } private void Dodaj. Akcesoria() { } } public class Pojazd { internal int cena. Koncowa; }
You Aren't Gonna Need It (YAGNI) tworzenie tego co jest potrzebne i niezbędne usuwanie dodatków, które mogą się przydać np. usunięcie zbędnych usingów, zwolnienie zasobów, usuwanie używanych zmiennych, metod Cytat: Antoine de Saint-Exupéry Perfekcję osiąga się wtedy, gdy nie można już nic odjąć, a nie dodać.
Separation of Concerns elementy składowe (np. klasy i metody) powinny być rozłączne i mieć oddzielne zastosowanie te elementy nie powinny współdzielić odpowiedzialności Cytat: Albert Einstein Wszystko trzeba robić tak prosto, jak to tylko jest możliwe, ale nie prościej.
Zasady programowania obiektowego S. O. L. I. D. Single Responsibility Principle (SRP) – zasada jednej odpowiedzialności Open-Closed Principle (OCP) – zasada otwarte-zamknięte Liskov Substitution Principle (LSP) – Zasada podstawienia Liskov Interface Segregation Principle (ISP) – Zasada segregacji interfejsów Dependency Inversion Principle (DIP) – Zasada odwrócenia zależności
Zasada jednej odpowiedzialności każdy obiekt powinien mieć tylko jeden cel i odpowiedzialność nie powinien istnieć więcej niż jeden powód do modyfikacji klasy jeśli jest kilka odpowiedzialności, powinniśmy podzielić klasę podobne do Separation of Concerns
Przykład public interface ISilnik { void Zmniejsz. Obroty(); void Zwiejsz. Obroty(); void Aktualne. Obroty(); }
Przykład class Silnik : ISilnik { public void Aktualne. Obroty() { throw new Not. Implemented. Exception(); } public void Zmniejsz. Obroty() { throw new Not. Implemented. Exception(); } public void Zwiejsz. Obroty() { throw new Not. Implemented. Exception(); } }
Przykład – źle! public interface ISilnik { void Zmniejsz. Obroty(); void Zwiejsz. Obroty(); void Aktualne. Obroty(); //biegi void Bieg. WDol(); void Bieg. WGore(); }
Przykład – dobrze! public interface ISkrzynia { void Bieg. WDol(); void Bieg. WGore(); } class Skrzynia : ISkrzynia { public void Bieg. WDol() { throw new Not. Implemented. Exception(); } public void Bieg. WGore() { throw new Not. Implemented. Exception(); } }
Zasada otwarte-zamknięte klasa powinna być otwarta na rozbudowę , ale zamknięta do jej własnej modyfikacji możemy dodawać nowe pola i metody, ale bez zmiany w wewnętrzną strukturę zmiana istniejącej struktury może mieć wpływ na inne elementy hermetyzacja, dziedziczenie, polimorfizm, delegaty unikamy instrukcji warunkowych
Przykład enum Diet. Type { Lose, Keep, Put } class Person { internal Diet. Type Diet; internal string Name; }
Przykład – źle! public void Serve. AMeal(List<Person> persons) { foreach (var person in persons) { if(person. Diet == Diet. Type. Lose) Console. Write. Line("Serwuj mniejsza porcje"); else if(person. Diet == Diet. Type. Keep) Console. Write. Line("Serwuj standardowa porcje"); else if(person. Diet == Diet. Type. Put) Console. Write. Line("Serwuj powiekszona porcje"); } }
Przykład – dobrze! public void Serve. AMeal(List<Person> persons, Dictionary<Diet. Type, string> meals) { foreach (var person in persons) { serve(person, meals); } } private void serve(Person person, Dictionary<Diet. Type, string> meals) { string meal; if (meals. Try. Get. Value(person. Diet, out meal)) Console. Write. Line(meal); }
Zasada podstawienia Liskov powinniśmy być w stanie używać klasy pochodnej w miejsce klasy nadrzędnej i ona zachowuje się w taki sam sposób, bez modyfikacji klasa pochodna nie ma wpływu na zachowanie klasy nadrzędnej w hierarchii klas powinno dać się traktować obiekt klas pochodneh jak obiekt klas bazowej dziedziczenie ma być stosowane tylko gdy chcemy skorzystać z polimorfizmu, a nie tylko gdy chcemy wyciągnąć wspólne cechy
Przykład – źle! class Frezarka { public void Operacja() { Console. Write. Line("Frezarka robi coś"); } } class Obrabiarka : Frezarka { public void Operacja() { Console. Write. Line("Obrabiarka robi coś"); } }
Przykład – źle! Obrabiarka obr = new Obrabiarka(); obr. Operacja(); //Wynik: "Obrabiarka robi coś" Frezarka frez = obr; frez. Operacja(); //Wynik: "Frezarka robi coś"
Przykład – dobrze! class Frezarka { public virtual void Operacja() { Console. Write. Line("Frezarka robi coś"); } } class Obrabiarka : Frezarka { public override void Operacja() { Console. Write. Line("Obrabiarka robi coś"); } }
Przykład – dobrze! Obrabiarka obr = new Obrabiarka(); obr. Operacja(); //Wynik: "Obrabiarka robi coś" Frezarka frez = obr; frez. Operacja(); //Wynik: "Obrabiarka robi coś"
Jeszcze lepiej? Refactoring interface IMaszyna { void Operacja(); } class Frezarka : IMaszyna { public virtual void Operacja() { Console. Write. Line("Frezarka robi coś"); } } class Obrabiarka : Frezarka { public override void Operacja() { Console. Write. Line("Obrabiarka robi coś"); } }
Zasada segregacji interfejsów dzielenie interfejsów na mniejsze grupy, tak by klasa dziedzicząca po nich, nie miała do dyspozycji niepotrzebnych metod nie powinno być jednego dużego interfejsu
Zasada odwrócenia zależności Moduły wysokopoziomowe nie powinny zależeć od modułów niskopoziomowych. Obie grupy modułów powinny zależeć od abstrakcji. moduły wysokopoziomowe – logika biznesowa moduły niskopoziomowe – komunikacją z bazą danych, ftp, API, algorytmy do liczenia Abstrakcje nie powinny zależeć od szczegółowych rozwiązań. To szczegółowe rozwiązania powinny zależeć od abstrakcji.
Przykład – źle! class B { public void Zrob. Cos() { } } class A { public void Metoda() { B b = new B(); b. Zrob. Cos(); } }
Przykład – dobrze! interface IB { void Zrob. Cos() { } } class B : IB { public void Zrob. Cos() { } } class A { public void Metoda() { IB b = new B(); b. Zrob. Cos(); } }
Wyjątki i obsługa błędów Wyjątek - mechanizm kontroli przepływu występujący w językach programowania i służący do obsługi zdarzeń wyjątkowych
Blok try catch try { // kod } catch { // kod w razie wystąpienia wyjątku }
try { //kod } catch (Exception e) { Console. Write. Line(e. Message); }
Właściwości klasy System. Exception Data Dodatkowe informacje na temat źródła wystąpienia wyjątku. Help. Link Umożliwia odczytanie lub ustawienie linka (np. do pomocy) związanego z błędem. Message Komunikat błędu. Source Umożliwia odczytanie lub przypisanie nazwy aplikacji lub obiektu, w którym wystąpił błąd. Target. Site Umożliwia odczytanie metody, w której wystąpił błąd.
Celowe wywołanie wyjątku try { throw new Index. Out. Of. Range. Exception(); } catch (Exception e) { Console. Write. Line(e. Message); }
„Najpopularniejsze” wyjątki Stack. Overflow. Exception - nieskończona rekurencja Out. Of. Memory. Excetpion Path. Too. Long. Exception Directory. Not. Found. Exception Unauthorized. Access. Exception File. Not. Found. Exception Argument. Out. Of. Range. Exception Argument. Null. Exception Null. Reference. Excetpion Index. Out. Of. Range. Exception Divide. By. Zero. Exception Not. Finite. Number. Exception Overflow. Exception Not. Implemented. Exception
Własna klasa na wyjątki – dziedziczy po System. Exception public class Nasz. Wyjątek. Exception : System. Exception { public Nasz. Wyjątek. Exception (string Message) : base(Message) { this. Source = „Nasz własny wyjątek"; this. Help. Link = "http: //google. pl/"; } }
Połykanie kodu – źle! try { // warunek do sprawdzenia } catch(Exception e} { // brak jakiegokolwiek kodu }
Wyrzucanie ponowne – źle! try { // warunek do sprawdzenia } catch(Exception e) { // instrukcje do wykonania. throw e; // źle }
Wyrzucanie ponowne – dobrze! try { // warunek do sprawdzenia } catch(Exception e) { // instrukcje do wykonania. throw; // dobrze! }
- Slides: 41