Metodyki zwinne wytwarzania oprogramowania Wykład 7 Marcin Młotkowski 23 listopada 2016
Plan wykładu 1 2 3 Kilka negatywnych przykładów Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 2 / 48
Single Responsibility Principle (SRP) Definicja Za chwilę... Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 3 / 48
Przykład Prostokąt +rysowanie() +pole(): double Aplikacja graficzna Geomertia obliczeniowa Interfejs graficzny Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 4 / 48
Co robi klasa Prostokąt Geometria Modeluje matematyczny obiekt prostokąt, dzięki czemu może obliczyć jego pole. Grafika Wizualizuje prostokąt za pomocą interfejsu graficznego. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 5 / 48
Zmiana projektu Zmiana interfejsu graficznego z 2D na 3D. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 6 / 48
Zmiana projektu Zmiana interfejsu graficznego z 2D na 3D. Zmiana wymaga Ponowna implementacja metody rysowanie() Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 6 / 48
Zmiana projektu Zmiana interfejsu graficznego z 2D na 3D. Zmiana wymaga Ponowna implementacja metody rysowanie() Konieczne Skompilowanie, przetestowanie i wdrożenie Aplikacji geometrycznej, czy przypadkiem coś się nie zmieniło. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 6 / 48
Zmiana projektu ProstokątGeometryczny +pole(): double Aplikacja graficzna Interfejs graficzny Prostokąt Geomertia obliczeniowa +rysowanie() Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 7 / 48
Obserwacje Zmiana projektu wymusiła zmiany tylko części klasy, tj. części graficznej. Może być druga przyczyna zmiany projektu: zmiana części geometrycznej. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 8 / 48
Definicja Żadna klasa nie może być modyfikowana z więcej niż jednego powodu. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 9 / 48
Inny przykład Klasa Pracownik + ObliczPensję(): decimal + Zapisz() + EdycjaGUI() Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 10 / 48
Plan wykładu 1 2 3 Kilka negatywnych przykładów Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 11 / 48
Tydzień temu na wykładzie: Sztywność Drobna zmiana w specyfikacji powoduje kaskadowe zmiany Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 12 / 48
Cel Chcemy aby: rozbudowa systemu wymagała tylko dodawania kodu, a nie jego modyfikacji. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 13 / 48
(open close principle, OCP) Definicja Składniki oprogramowania (klasy, moduły, procedury etc.) muszą być otwarte na rozbudowę, ale zamknięte na dla modyfikacji. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 14 / 48
Objaśnienia Otwarte na rozszerzenia łatwo rozbudować funkcjonalność modułu Zamknięte na modyfikacje Zmiany nie mogą skutkować modyfikacją kodu źródłowego, ani nawet zmianami na poziomie binariów. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 15 / 48
Przykład niezgodności z zasadą OCP: architektura klient-serwer Dokument DotPrinter Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 16 / 48
Kod źródłowy public class DotPrinter {... } public class Dokument { } public void drukuj(dotprinter prn) {... } Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 17 / 48
Kod źródłowy public class DotPrinter {... } public class Dokument { } public void drukuj(dotprinter prn) {... } Wymiana sprzętu public class LaserPrinter {... } Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 17 / 48
Rozwiązanie nr 1: wzorzec projektowy Strategia Dokument <<interface>> ClientInterface DotPrinter Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 18 / 48
Implementacja public interface Printer {... } public class DotPrinter : Printer {... } public class Dokument { } public void drukuj(printer prn) {... } Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 19 / 48
Rozwiązanie nr 2: wzorzec projektowy Template Method Dokument Drukarka DotPrinter Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 20 / 48
Inny przykład złego projektu: figury geometryczne enum ShapeType { circle, square }; typedef struct Shape *ShapePointer; void DrawAllShapes(ShapePointer list[], int n) { int i; for(i = 0; i < n; i++) { struct Shape *s = list[i]; switch (s->itstype) { case square: DrawSquare( (struct Square*)s); break; case circle: DrawCircle( (struct Circle*)s); break; } } }
Ocena takiego systemu Dodanie nowej figury, np. trójkąta: wyszukanie wszystkich warunków sprawdzających typ figury; konieczność ponownej kompilacji modułu i wszystkich modułów zależnych; konieczność ponownej instalacji bibliotek. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 22 / 48
Wady Sztywność Dodanie nowego elementu do wyliczenia wymusza serię działań: kompilacja, testowanie, instalacja. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 23 / 48
Wady Sztywność Dodanie nowego elementu do wyliczenia wymusza serię działań: kompilacja, testowanie, instalacja. Wrażliwość Rozszerzenie implementacji wymaga starannego przejrzenia kodu w poszukiwaniu switch--case i if--else badających typ figury. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 23 / 48
Dobre rozwiązanie public interface Shape { void Draw(); } public class Square : Shape { public void Draw() {... } } public class Circle : Shape { public void Draw() {... } } Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 24 / 48
Zgodność z zasadą Otwartość Łatwość zmian w zakresie dodawania nowych figur. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 25 / 48
Zgodność z zasadą Otwartość Łatwość zmian w zakresie dodawania nowych figur. Zamkniętość Nie ma konieczności modyfikacji istniejącego kodu przy rozbudowie. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 25 / 48
A jakie są wady Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 26 / 48
A jakie są wady Trzeba przewidzieć kierunek zmian projektu. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 26 / 48
Kiedy ten projekt zawiedzie Shape shapes[]; foreach (Shape sh in shapes) sh.draw(); Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 27 / 48
Kiedy ten projekt zawiedzie Shape shapes[]; foreach (Shape sh in shapes) sh.draw(); Dodatkowe założenie Chcemy, aby najpierw były kwadraty, a potem okręgi. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 27 / 48
Kiedy ten projekt zawiedzie Shape shapes[]; foreach (Shape sh in shapes) sh.draw(); Dodatkowe założenie Chcemy, aby najpierw były kwadraty, a potem okręgi. Smutny wniosek Nie da się zaprojektować tak system, aby był otwarty na dowolne zmiany. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 27 / 48
Próba poprawienia public interface Shape : IComparable { void Draw(); }... shapes.sort(); foreach (Shape sh in shapes) sh.draw(); Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 28 / 48
Przypomnienie public interface IComparable { int CompareTo(Object obj) } Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 29 / 48
Próba implemetacji public class circle : Shape { public int CompareTo(object o) { if (o is Square) return -1; else return 0; } } Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 30 / 48
Ocena implementacji Czy ta implementacja spełnia warunek ocp? Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 31 / 48
Przewidywanie kierunku zmian szybkie wydania; Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 32 / 48
Przewidywanie kierunku zmian szybkie wydania; szybkia prezentacja; Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 32 / 48
Przewidywanie kierunku zmian szybkie wydania; szybkia prezentacja; szybkie uzyskanie informacji zwrotnej od klienta. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 32 / 48
Plan wykładu Kilka negatywnych przykładów 1 2 3 Kilka negatywnych przykładów Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 33 / 48
Oryginalne sformułowanie Kilka negatywnych przykładów Oczekujemy czegoś na kształt właściwości podstawienia: jeżeli dla każdego obiektu o 1 typu S istnieje taki obiekt o 2 typu T, że zachowanie wszystkich programów P zdefiniowanych na bazie typu T nie zmieni się, jeśli obiekt o 1 zastąpimy obiektem o 2, typ S jest podtypem typu T. Barbara Liskov, 1988 Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 34 / 48
Kilka negatywnych przykładów Pierwszy przykład: figury geometryczne public enum ShapeType { square, circle }; public class Shape { private ShapeType type; public Shape(ShapetType t) { this.type = t; } } public static void DrawShape(Shape s) { if (s.type == ShapeType.square) (s as Square).Draw(); else if (s.type == ShapeType.circle) (s as Circle).Draw(); } Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 35 / 48
Implementacja klas Kilka negatywnych przykładów public class Circle : Shape {... public Circle() : base(shapetype.circle) {} public void Draw() {... } } public class Square : Shape { public Square() : base(shapetype.square) {} public void Draw() {... } } Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 36 / 48
Użycie Kilka negatywnych przykładów Chcemy uniknąć polimorficznych wywołan bo kosztowne... Shape[] obrazek = new Shape[100];... foreach(shape sh in Shape) Shape.DrawShape(sh); Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 37 / 48
Użycie Kilka negatywnych przykładów Chcemy uniknąć polimorficznych wywołan bo kosztowne... Shape[] obrazek = new Shape[100];... foreach(shape sh in Shape) Shape.DrawShape(sh); Ocena implementacji Narusza zasadę otwarte zamknięte Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 37 / 48
Drugi błąd Kilka negatywnych przykładów Shape s = new Circle(ShapeType.circle); s.draw(); Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 38 / 48
Drugi błąd Kilka negatywnych przykładów Shape s = new Circle(ShapeType.circle); s.draw(); Shape s = new Shape(ShapeType.circle); s.draw(); Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 38 / 48
Ciekawszy przykład Kilka negatywnych przykładów public class Rectangle { double width; double height; public double Width { get { return width; } set { width = value; } } public double Height { get { return height; } set { height = value; } } } Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 39 / 48
Ciąg dalszy Kilka negatywnych przykładów Rozszerzamy implementację o Kwadraty. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 40 / 48
Jak rozwinąć projekt Kilka negatywnych przykładów Rectangle double: Height double: Width kwadrat jest prostokątem; Square Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 41 / 48
Jak rozwinąć projekt Kilka negatywnych przykładów Rectangle double: Height double: Width kwadrat jest prostokątem; Square ale w klasie Square nie potrzebujemy tylu atrybutów! Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 41 / 48
A może tak Kilka negatywnych przykładów Square double: Height double: Width W kwadracie wysokość i szerokość są równe, w prostokącie musimy usunąć ten warunek; Rectangle Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 42 / 48
A może tak Kilka negatywnych przykładów Square double: Height double: Width Rectangle W kwadracie wysokość i szerokość są równe, w prostokącie musimy usunąć ten warunek; musimy zmienić implementację klasy Square (czego programiści zwinni unikają). Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 42 / 48
Poprawiamy projekt Kilka negatywnych przykładów public class Square : Rectangle { public new double Width { set { base.width = value; base.height = value; } } public new double Height { set { base.width = value; base.height = value; } Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 43 / 48
Czy to działa? Kilka negatywnych przykładów void obszar(rectangle r) { r.width = 4.0; r.height = 5.0; if (r.area()!= 20.0) throw new Exception("Nieprawidłowa metoda Area!"); } Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 44 / 48
Czy to działa? Kilka negatywnych przykładów void obszar(rectangle r) { r.width = 4.0; r.height = 5.0; if (r.area()!= 20.0) throw new Exception("Nieprawidłowa metoda Area!"); } obszar(new Square()); Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 44 / 48
Kilka negatywnych przykładów Morał Nie można używać obiektów klasy Square zamiast Rectangle. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 45 / 48
Kilka negatywnych przykładów Morał Nie można używać obiektów klasy Square zamiast Rectangle. Szukanie winnego klasa Rectangle? klasa Square? funkcja obszar? Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 45 / 48
Analiza przypadków Kilka negatywnych przykładów Funkcja obszar Wygląda na to, że programista użył prawidłowo interfejsu klasy Rectangle. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 46 / 48
Analiza przypadków Kilka negatywnych przykładów Funkcja obszar Wygląda na to, że programista użył prawidłowo interfejsu klasy Rectangle. Klasa Rectangle? Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 46 / 48
Analiza przypadków Kilka negatywnych przykładów Funkcja obszar Wygląda na to, że programista użył prawidłowo interfejsu klasy Rectangle. Klasa Rectangle? Klasa Square Programista zmodyfikował działanie metod. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 46 / 48
Kilka negatywnych przykładów Może klasa Square nie musi być podklasą Rectangle? Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 47 / 48
Zasada podstawiania Liskov Kilka negatywnych przykładów Definicja Musi istnieć możliwość zastępowania typów bazowych ich podtypami. Marcin Młotkowski Metodyki zwinne wytwarzania oprogramowania 48 / 48