1 Inżynieria oprogramowania OOP i zasady SOLID WWW: http://www.fizyka.umk.pl/~jacek/dydaktyka/inzynieria/index.html Jacek Matulewski Instytut Fizyki, UMK WWW: http://www.fizyka.umk.pl/~jacek E-mail: [email protected] semestr letni 2016
2 Plan wykładu Słownik IEEE 610.12-1990: Inżynieria oprogramowania to systematyczne, ilościowe i sformalizowane (dyscyplina) tworzenie, rozwój i utrzymywanie oprogramowania
3 Plan wykładu Słownik IEEE 610.12-1990: Inżynieria oprogramowania to systematyczne, ilościowe i sformalizowane (dyscyplina) tworzenie, rozwój i utrzymywanie oprogramowania
4 Plan wykładu Słownik IEEE 610.12-1990: Inżynieria oprogramowania to systematyczne, ilościowe i sformalizowane (dyscyplina) tworzenie, rozwój i utrzymywanie oprogramowania
5 Plan wykładu Słownik IEEE 610.12-1990: Inżynieria oprogramowania to systematyczne, ilościowe i sformalizowane (dyscyplina) tworzenie, rozwój i utrzymywanie oprogramowania 1.Programowanie obiektowe, SOLID, testy 2.Metodologia wytwarzania oprogramowania (SCRUM) 3.UML 4.Wzorce projektowe gangu czworga 5.Wzorce architektoniczne
6 Warunki zaliczenia 1.Warunek konieczny: Odrobienie zadań domowych (może być w parach). 2.Kolokwium (ocena) 3.Ocenę można dodatkowo podnieść biorąc udział w konkursach (zwykle nagroda to +1/2 do oceny). Zadania: http://www.fizyka.umk.pl/~jacek/dydaktyka/inzynieria/2016L/zadania.txt Konkursy – w prezentacji
7 Programowanie obiektowe Zagadnienia na powtórzeniu: klasy, pola, metody, własności, zakres dostępności (prywatny, publiczny, chroniony) dziedziczenie, klasy abstrakcyjne (figury), nadpisywanie metod przykład: równanie kwadratowe (double, Complex) testy jednostkowe, refactoring, TDD biblioteki DLL
8 Interfejs (ang. interface) C++: interfejs = klasa abstrakcyjna (ale tylko metody czysto wirtualne) class IFigura { public: virtual double ObliczPole() = 0; virtual double ObliczObwod() = 0; }; class Prostokat : public IFigura { private: double a, b; public: Prostokat(double a, double b) { this->a = a; this->b = b; } double ObliczPole() { return a * b; } double ObliczObwod() { return 2 * a + 2 * b; } };
9 Interfejs (ang. interface) C++: interfejs = klasa abstrakcyjna (ale tylko metody czysto wirtualne) Prostokat prostokat(2, 3); std::cout
10 Interfejs (ang. interface) C#: można nadal używać klas abstrakcyjnych w roli interfejsów public abstract class Figura { public abstract double ObliczPole(); public abstract double ObliczObwód(); } class Prostokąt : Figura { private double a, b; public Prostokąt(double a, double b) { this.a = a; this.b = b; } public override double ObliczPole() { return a * b; } public override double ObliczObwód() { return 2 * a + 2 * b; } };
11 Interfejs (ang. interface) C#: można nadal używać klas abstrakcyjnych w roli interfejsów Prostokąt prostokąt = new Prostokąt(2, 3); Console.WriteLine("Pole: " + prostokąt.ObliczPole()); Console.WriteLine("Obwod: " + prostokąt.ObliczObwód()); Console.WriteLine(); Figura figura = new Prostokąt(2, 3); Console.WriteLine("Pole: " + figura.ObliczPole()); Console.WriteLine("Obwod: " + figura.ObliczObwód()); Console.WriteLine();
12 Interfejs (ang. interface) C#: specjalna konstrukcja pozwalająca na definiowanie interfejsów (słowo kluczowe interface) public interface IFigura { double ObliczPole(); double ObliczObwód(); } class Prostokąt : IFigura { private double a, b; public Prostokąt(double a, double b) { this.a = a; this.b = b; } public override double ObliczPole() { return a * b; } public override double ObliczObwód() { return 2 * a + 2 * b; } };
13 Interfejs (ang. interface) C#: specjalna konstrukcja pozwalająca na definiowanie interfejsów (słowo kluczowe interface) Prostokąt prostokąt = new Prostokąt(2, 3); Console.WriteLine("Pole: " + prostokąt.ObliczPole()); Console.WriteLine("Obwod: " + prostokąt.ObliczObwód()); Console.WriteLine(); IFigura figura = new Prostokąt(2, 3); Console.WriteLine("Pole: " + figura.ObliczPole()); Console.WriteLine("Obwod: " + figura.ObliczObwód()); Console.WriteLine();
14 Interfejs (ang. interface) C#: specjalna konstrukcja pozwalająca na definiowanie interfejsów (słowo kluczowe interface) public interface IFigura { double Pole { get; } //własności double Obwód { get; } } class Prostokąt : IFigura { private double a, b; public Prostokąt(double a, double b) { this.a = a; this.b = b; } public double Pole { get { return a * b; } } public double Obwód { get { return 2 * a + 2 * b; } } };
15 Interfejs (ang. interface) C#: specjalna konstrukcja pozwalająca na definiowanie interfejsów (słowo kluczowe interface) Prostokąt prostokąt = new Prostokąt(2, 3); Console.WriteLine("Pole: " + prostokąt.Pole); Console.WriteLine("Obwod: " + prostokąt.Obwód); Console.WriteLine(); IFigura figura = new Prostokąt(2, 3); Console.WriteLine("Pole: " + figura.Pole); Console.WriteLine("Obwod: " + figura.Obwód); Console.WriteLine();
16 Interfejs (ang. interface) Interfejs vs klasa abstrakcyjna Interfejs można zastąpić klasą abstrakcyjną, w której są tylko metody abstrakcyjne Interfejs vs delegacja (typ wskaźnika do metody) Delegacja to typ metody = wymaganie metody lub klasy względem używającego ją kodu Interfejs określa sygnatury metod, w tym i ich nazwy = kontrakt klasy dostarczającej Rola interfejsów: Ograniczenie (rozluźnienie) związków między klasami. Ustalenie kontraktów między nimi. Jednocześnie prowadzi jednak do uwolnienia ich rozwoju, o ile przestrzegamy kontraktu.
17 Użycie interfejsów - zasady SOLID Spopularyzował: Robert C. Martin (uncle Bob) (jeden z sygnatariuszy Agile Manifesto) Cel: przygotowanie kodu, który jest łatwy do utrzymania (konserwacji), rozbudowy i prawd. zmian wymagań
18 Zasady SOLID S. Zasada jednej odpowiedzialności Single responsibility principle (SRP) O. Zasada otwarte-zamknięte Open/close principle (OCP) L. Zasada podstawienia Liskov Liskov substitution principle (LSP) I. Zasada segregacji interfejsów Interface segregation principle (ISP) D. Zasada odwrócenia zależności Dependency inversion principle (DIP)
19 Zasady SOLID Zasada jednej odpowiedzialności (klasy, metody, DLL) Single responsibility principle Boska klasa, którą należy rozdzielić na wiele klas odpowiedzialnych tylko za pojedyncze funkcjonalności Inne sformułowanie: KISS Keep it simple, Stupid! Zmiana jednej funkcjonalności pociąga za sobą konieczność zmiany wielu innych (wszystko splątane)
20 Zasady SOLID Zasada jednej odpowiedzialności (klasy, metody, DLL) Single responsibility principle Przykład dla klas implementujących figury (np. Prostokąt ): Odpowiedzialności: -przechowywanie rozmiaru do obliczeń P i O ( double ), -przechowywanie położenia do rysowania ( int ), -obliczenia pola i obwodu, -rysowanie z System.Drawing.Graphics. Zmiana sposobu rysowania (np. WinForms -> WPF) oznacza konieczność zmiany tej klasy i klas zależnych
21 Zasady SOLID Zasada jednej odpowiedzialności (klasy, metody, DLL) Single responsibility principle public interface IFigura //tu wymieszana odpowiedzialność modelu i widoku { double Pole { get; } double Obwód { get; } void Rysuj(Graphics g, Color kolor, double skala = 1, bool wypełnij = false); } public class Prostokąt : IFigura { private int x, y; private double a, b; public Prostokąt(int x, int y, double a, double b) {... } public double Pole { get { return a * b; } } public double Obwód { get { return 2 * a + 2 * b; } } public void Rysuj(Graphics g, Color kolor, double skala = 1,...) {... } }
22 Zasady SOLID Zasada jednej odpowiedzialności (klasy, metody, DLL) Single responsibility principle Zmiana: -przygotowanie klasy odpowiedzialnej za obliczenia, -przeniesienie kodu odpowiedzialnego za rysowanie do osobnej klasy lub do statycznego „pomocnika” Efekt: rozdzielenie warstw modelu i widoku Zmiana sposobu rysowania dotknie tylko klasy widoku (stosowanie zasady SRP nie musi prowadzić do tworzenia warstw, ale warstwy są efektem konsekwentnego stosowania SRP)
23 Zasady SOLID Zasada jednej odpowiedzialności (klasy, metody, DLL) Single responsibility principle public interface IFigura { double Pole { get; } double Obwód { get; } void Rysuj(Graphics g, Color kolor, double skala = 1, bool wypełnij = false); } public class Prostokąt : IFigura { private int x, y; private double a, b; public Prostokąt(int x, int y, double a, double b) {... } public double Pole { get { return a * b; } } public double Obwód { get { return 2 * a + 2 * b; } } public void Rysuj(Graphics g, Color kolor, double skala = 1,...) {... } }
24 Zasady SOLID Zasada jednej odpowiedzialności (klasy, metody, DLL) Single responsibility principle public static class PomocnikRysowania //helper { public static void Rysuj(this Model.Prostokąt prostokąt, int x, int y, Graphics g, Color kolor, double skala = 1, bool wypełnij = false) { int w = (int)(skala * prostokąt.A); int h = (int)(skala * prostokąt.B); if (!wypełnij) g.DrawRectangle(new Pen(kolor), x, y, w, h); else g.FillRectangle(new SolidBrush(kolor), x, y, w, h); } public static void Rysuj(this Model.IFigura figura, int x, int y, Graphics g, Color kolor, double skala = 1, bool wypełnij = false) {... }
25 Zasady SOLID Zasada otwarte-zamknięte Open/close principle Klasy są otwarte na rozszerzenia, ale zamknięte na modyfikacje To dotyczy też metod/funkcji oraz modułów, czy bibliotek. Idea kodu produkcyjnego, który nie może być zmieniany (poza naprawianiem błędów) „Do dodania możliwości założenia kapelusza nie powinna być potrzebna operacja mózgu!”
26 Zasady SOLID Zasada otwarte-zamknięte Open/close principle Rozwiązania tego pozornego paradoksu: -wprowadzenie abstrakcji umożliwiających „podmianę” kodu na inny bez modyfikacji zasadniczej klasy (interfejsy, „wstrzykiwanie” kodu), -dodanie lub zmiana funkcjonalności w klasie potomnej. Zasada OCP wymaga projektowania kodu w sposób ułatwiający jego późniejszą rozbudowę
27 Zasady SOLID Zasada otwarte-zamknięte Open/close principle Przykład: -klasy reprezentujące prostokąt i okrąg -Statyczny pomocnik obliczający pola poszczególnych figur (za obliczanie pól w pełni odpowiedzialny jest pomocnik) Problem (jak zwykle w przypadku SOLID): Jak napisać kod w taki sposób, żeby był łatwy do rozbudowy?
28 Zasady SOLID Zasada otwarte-zamknięte Open/close principle public class Prostokąt { public double A { get; private set; } public double B { get; private set; } public Prostokąt(double a, double b) { A = a; B = b; } } public class Okrąg { public double R{ get; private set; } public Okrąg(double r) { R = r; } }
29 Zasady SOLID Zasada otwarte-zamknięte Open/close principle public static class SumatorPólFigur { public static double SumujPolaFigur(object[] figury) { double sumaPól = 0; foreach(object figura in figury) { if(figura is Prostokąt) { Prostokąt prostokąt = figura as Prostokąt; sumaPól += prostokąt.A * prostokąt.B; }... } return sumaPól; } Klient: rozszerzyć zbiór figur o nowe Dodajemy kolejny if i polecenia zależ. Ukryta zależność od klas Prostokąt, …
30 Zasady SOLID Zasada otwarte-zamknięte Open/close principle public interface IFigura { double Pole { get; } } public class Prostokąt : IFigura { public double A { get; private set; } public double B { get; private set; } public Prostokąt(double a, double b) { A = a; B = b; } public double Pole { get { return a * b; } } } Klasy figur przejmują odpowiedzialność za obliczanie figur Interfejs = abstrakcja
31 Zasady SOLID Zasada otwarte-zamknięte Open/close principle public static class SumatorPólFigur { public static double SumujPolaFigur(IFigura[] figury) { double sumaPól = 0; foreach(IFigura figura in figury) { sumaPól += figura.Pole; } return sumaPól; } Klient: rozszerzyć zbiór figur o nowe Dodajemy nową klasę impl. IFigura Bez instrukcji warunkowych
32 Zasady SOLID Zasada podstawiania Liskov Liskov subsitution principle Funkcje które używają wskaźników lub referencji do klas bazowych, muszą być w stanie używać również obiektów klas dziedziczących po klasach bazowych, bez dokładnej znajomości tych obiektów. Idea standaryzacji: baterie AA = R6, AAA = R03, … Interfejsy jako specyfikacje standardów
33 Zasady SOLID Zasada podstawiania Liskov Liskov subsitution principle Z zasady LSP wynika, że klasa potomna powinna rozszerzać funkcjonalność klasy bazowej, a nie ją zawężać lub całkowicie zmieniać. Sposób użycia klasy potomnej powinien być taki sam, jak klasy bazowej. Pełna wymienność klas potomnych, ale również klasa bazowa powinna móc zastąpić klasę potomną
34 Zasady SOLID Zasada podstawiania Liskov Liskov subsitution principle public class Prostokąt { protected double a = 0, b = 0; public virtual double A { get { return a; } set { a = value; } } public virtual double B { get { return b; } set { b = value; } } public double Pole { get { return a * b; } } } public class Kwadrat : Prostokąt //ta klasa nie rozszerza klasy bazowej { public override double A { get { return a; } set { a = value; b = value; } } public override double B { get { return b; } set { a = value; b = value; } } }
35 Zasady SOLID Zasada podstawiania Liskov Liskov subsitution principle public static double ZmieńBokiIObliczPole(Prostokąt prostokąt, double a, b) { prostokąt.A = a; prostokąt.B = b; return prostokąt.Pole; } Wynik wywołania: Pole (prostokąt): 6, pole (kwadrat): 9 Spodziewamy się, że instancja klasy potomnej może być przesłana z równym skutkiem, jak instancja klasy bazowej.
36 Zasady SOLID Zasada podstawiania Liskov Liskov subsitution principle public interface IFigura { double Pole { get; } } public class Prostokąt : IFigura { protected double a = 0, b = 0; public virtual double A { get { return a; } set { a = value; } } public virtual double B { get { return b; } set { b = value; } } public double Pole { get { return a * b; } } } public class Kwadrat : IFigura //Klasa Kwadrat nie dziedziczy po Prostokąt { public override double A { get { return a; } set { a = value; b = value; } } public override double B { get { return b; } set { a = value; b = value; } } public double Pole { get { return a * a; } } }
37 Zasady SOLID Zasada segregacji interfejsów Interface segregation principle Redukcja zależności między klasami: związki między klasami powinny być ograniczone do minimum, czyli klient klasy powinien mieć dostęp tylko do tych składowych, których rzeczywiście potrzebuje Klasa: Prostokąt Klasa-klient: KalkulatorPola, KalkulatorObwodu
38 Zasady SOLID Zasada segregacji interfejsów Interface segregation principle Po co? Co się stanie w przypadku zmiany klasy Prostokąt lub klas-klientów? Jak ograniczyć zmiany w całym kodzie? Kontrakty między klasami = interface’y Klasa: Prostokąt Klasa-klient: KalkulatorPola, KalkulatorObwodu
39 Zasady SOLID Zasada segregacji interfejsów Interface segregation principle public interface IFigura { double Pole { get; } double Obwód { get; } } public class Prostokąt : IFigura { protected double a = 0, b = 0; public Prostokąt(double a, double b) { this.a = a; this.b = b; } public double Pole { get { return a * b; } } public double Obwód { get { return 2 * a + 2 * b; } } }
40 Zasady SOLID Zasada segregacji interfejsów Interface segregation principle //klasa SumatorPólFigur public static double ObliczSumęPól(IEnumerable figury) { double sumaPól = 0; foreach (IFigura figura in figury) sumaPól += figura.Pole; return sumaPól; } //klasa SumatorObwodówFigur public static double ObliczSumęObwodów(IEnumerable figury) { double sumaObwodów = 0; foreach (IFigura figura in figury) sumaObwodów += figura.Obwód; return sumaObwodów; }
41 Zasady SOLID Zasada segregacji interfejsów Interface segregation principle Osobne interfejsy na poszczególne funkcjonalności (kontrakty z klientami ograniczają zakres zmian): public interface IFiguraZPolem { double Pole { get; } } public interface IFiguraZObwodem { double Obwód { get; } } public class Prostokąt : IFiguraZPolem, IFiguraZObwodem { protected double a = 0, b = 0; public Prostokąt(double a, double b) { this.a = a; this.b = b; } public double Pole { get { return a * b; } } public double Obwód { get { return 2 * a + 2 * b; } } }
42 Zasady SOLID Zasada segregacji interfejsów Interface segregation principle //klasa SumatorPólFigur public static double ObliczSumęPól(IEnumerable figury) { double sumaPól = 0; foreach (IFiguraZPolem figura in figury) sumaPól += figura.Pole; return sumaPól; } //klasa SumatorObwodówFigur public static double ObliczSumęObwodów(IEnumerable figury) { double sumaObwodów = 0; foreach (IFiguraZObwodem figura in figury) sumaObwodów += figura.Obwód; return sumaObwodów; }
43 Zasady SOLID Zasada odwrócenia zależności Dependency inversion principle Oczywista oczywistość: Niższe warstwy architektoniczne nie mogą zależeć od wyższych! DIP: Zależność wyższych warstw od niższych też powinna być ograniczona jedynie do kontaktów (interfejsów) W przypadku zmian w module takie osłabienie wiązania ogranicza zakres koniecznych zmian w innych modułach. Zapewnia wymienność modułów z niższej warstwy.
44 Zasady SOLID Zasada odwrócenia zależności Dependency inversion principle //niższa warstwa public class Prostokąt { private double a, b; public Prostokąt(double a, double b) { this.a = a; this.b = b; } public double Pole { get { return a * b; } } } //wyższa warstwa (klasa silnie zależna od klasy Prostokąt) public class Opakowanie { private Prostokąt prostokąt = null; public Opakowanie(double a, double b) { prostokąt = new Prostokąt(a, b); } public double ObliczPole() { return prostokąt.Pole; } }
45 Zasady SOLID Zasada odwrócenia zależności Dependency inversion principle Silna zależność klasy Opakowanie od klasy Prostokąt: nie można wymienić klasy podrzędnej na inną w razie zmian w którejkolwiek z klasy, druga musi także być modyfikowana Zasada DIP: Moduły z wyższej warstwy nie powinny zależeć od modułów z niższej warstwy. Oba powinny zależeć od abstrakcji (np. interfejsu) Abstrakcja nie powinna zależeć od szczegółów (np. a, b), to szczegóły powinny zależeć od abstrakcji (nie ma ich w interf.)
46 Zasady SOLID Zasada odwrócenia zależności Dependency inversion principle public interface IFiguraZPolem { double Pole { get; } } //abstrakcja //niższa warstwa (zależy od abstrakcji) public class Prostokąt : IFigura { private double a, b; public Prostokąt(double a, double b) { this.a = a; this.b = b; } public double Pole { get { return a * b; } } } //wyższa warstwa (metoda zależy tylko od abstrakcji) public class Opakowanie { private IFiguraZPolem figura = null; public Opakowanie(IFiguraZPolem figura) {this.figura = figura; } public double ObliczPole() { return figura.Pole; } }
47 Zasady SOLID Zasada odwrócenia zależności Dependency inversion principle W przypadku zmian w module takie osłabienie wiązania ogranicza zakres koniecznych zmian w innych modułach. Zapewnia wymienność modułów z niższej warstwy. Rozwiązanie, które zastosowaliśmy to Dependency Injection (wstrzyknięcie zależności). Można wstrzyknąć obiekt przez konstruktor, własność lub do jednej konkretnej metody, która używa zależności. Uwaga! W ogólności DI ≠ IoC ≠ DIP (DI ϵ IoC ϵ DIP)
48 Zasady SOLID Zasady SOLID usuwanie zależności między klasami ogólność (wymienność klas) i podatność na zmiany Dependency Inversion Principle (DIP) – osłabianie (decoupling) zależności klas z wyższych warstw od klas z niższych Design by Contract (programowanie poprzez kontakt) – metodyka tworzenie systemów, w których kładzie się nacisk na zasady SOLID Inversion of Control (IoC, odwrócenie sterowania) – przenoszenie części odpowiedzialności poza klasę np. klient tworzący klasę Opakowanie ma kontrolę nad sposobem obliczania figury (może przekazać taką implementację interfejsu IFiguraZPolem jaką zechce, dowolnie obliczającą pole figury). Dependency Injection (wstrzykiw. zależności) – jedna z realizacji IoC
49 Zasady GRASP General Responsibility Assignment Software Principles zasady mówiące o podziale odpowiedzialności w systemie komp. Zasady zebrane przez Craiga Larmana w książce Wzorce GoF można traktować jako konsekwencję stosowania zasad GRASP Przykład skróconego opisu: http://nandrew.blogspot.com/2007/09/zasady-grasp-cz-i.html http://nandrew.blogspot.com/2007/09/zasady-projektowania-obiektowego-grasp.html Ekspert (Information Expert) Pytanie: Której klasie przypisać odpowiedzialność/zadanie? Odpowiedź: Tej, która ma niezbędne informacje (aby uniknąć przekazywania danych i związanych z tym powiązań między klasami)
50 Zasady GRASP General Responsibility Assignment Software Principles zasady mówiące o podziale odpowiedzialności w systemie komp. Twórca (Creator) Pytanie: Która klasa powinna tworzyć obiekt jakiejś klasy Klasa ? Odpowiedź: Ta klasa, która: -przechowuje kolekcje klas Klasa, -jest mocno powiązana z klasą Klasa, -ma dane niezbędne do inicjacji instancji Klasa. Im więcej przesłanek, tym lepsza decyzja.
51 Zasady GRASP General Responsibility Assignment Software Principles zasady mówiące o podziale odpowiedzialności w systemie komp. Kontroler (Controller) Pytanie: Jaki obiekt (z warstwy modelu) powinien obsługiwać żądania użytkownika (np. przesyłane z warstwy widoku / GUI)? Odpowiedź: klasa kontrolera powinna: -opisywać działanie systemu jako całości (spinać go), -reprezentować urządzenie, na którym działa system, -reprezentować przypadek użycia ( Generalnie: powinna to być klasa, która organizuje pracę systemu.
52 Zasady GRASP General Responsibility Assignment Software Principles zasady mówiące o podziale odpowiedzialności w systemie komp. Niska liczba powiązań (Low Coupling) Wysoka spójność (High Cohesion) Pytanie: Jak zmniejszyć powiązania między klasami systemu? Jak nie przeciążać klasę obowiązkami? Odpowiedź: Każda klasa powinna mieć tylko jedno zadanie (jedną odpowiedzialność), ale na tyle obszerne, żeby nie wymagało współpracy z innymi klasami (unikanie powiązań między klasami). Porównaj z: Single Responsibility Principle i boskie klasy
53 Zasady GRASP General Responsibility Assignment Software Principles zasady mówiące o podziale odpowiedzialności w systemie komp. Pośrednictwo (Indirection) Pytanie: Jak ograniczyć powiązania między dwoma pakietami klas? Odpowiedź: Wstawić między te pakiety dodatkową klasę, która przejmie odpowiedzialność za komunikację i tłumaczenie danych, wywołania metod przekazywanych między obydwoma pakietami To sprzyja zasadom Niskiej liczby powiązań i Wysokiej spójności Przykład: Warstwa dostępowa (DAL) między bazą danych i BLL Efekt: BLL nie zależy od sposobu zapisywania danych
54 Zasady GRASP General Responsibility Assignment Software Principles zasady mówiące o podziale odpowiedzialności w systemie komp. Polimorfizm (Polymorphism) Przejmowanie odpowiedzialności w przypadku alternatywnych możliwości -> alternatywne klasy zgodne z tym samym interfejsem Zapewniona zmienność (Protected Variations) Jak zaprojektować system z łatwo wymienialnymi modułami? Klasy podejrzewane o duże zmiany w przyszłości (niestabilne) zapośredniczyć interfejsami – łatwo będzie je wymienić na inne Czysty wymysł (Pure Fabrication) Wyjście awaryjne, gdy pozostałe zasady dają sprzeczne zalecenia Klasa (np. singleton) realizująca jedynie usługi na rzecz innych klas