Návrhové principy
Proč?
- pomáhá předcházet špatnému design
Co působí špatný desing?
-
ztuhlost - jakákoli změna je obtížná nebo je potřeba na mnoha místech
-
křehkost - provedená úprava působí problémy , často na nesouvisejících místech
- znovu(ne)použitelnost - znovupoužití určité části je složitější než ji napsat znovu
S - Single responsibility principle
S.O.L.I.D
O - Open/Close principle
L - Liskov substitution principle
I - Interface segregation principle
D - Dependency inversion principle
Single responsibility principle
- Každá třída/fce by měla mít právě jednu odpovědnost/důvod ke změně.
- HINT: Pokud zodpovědnost třídy/fce nelze popsat jednoduchou větou bez "a", pravděpodobně porušuje SRP.
- Snižuje složitost systému, zvyšuje jeho pochopitelnost a čitelnost
Porušení SRP
class Book {
private Name name;
private Author author;
private Content content;
// ... getters
// ... settters
public void print() {
// book printing code
}
public void read() {
// book reading code
}
}
Oprava podle SRP
class Book {
private Name name;
private Author author;
private Content content;
// ... getters
// ... settters
}
class Printer {
public void print(Book book) {
// book printing code
}
}
class Reader {
public void read(Book book) {
// book reading code
}
}
Open/Close principle
- Každá třída/fce by měla být otevřená pro rozšíření a uzavřená pro změny
-
Zajistíme především pomocí abstrakce a polymorfismu
- !!Vždy budou existovat změny vyžadující zásah do existujícího kódu, ale čím méně tím lépe
Porušení OCP
class Payment {
public void pay(Method method, Money amount) {
if (method.isCash()) {
//do something
} else if (method.isTransfer()) {
//do something else
} else {
throw new IllegalArgumentException("Unknown payment option.");
}
}
}
Oprava podle OCP
interface Method {
void confirmPaymentReceived(Money amount);
}
class TransferMethod implements Method { /* ...confirmPaymentReceived... */ }
class CashMethod implements Method { /* ...confirmPaymentReceived... */ }
class Payment {
public void pay(Method method, Money amount) {
method.confirmPaymentReceived(amount);
printReceipt(amount);
dispatchGoods();
}
}
Liskov substitution principle
- Všude kde je vyžadována bázová třída, musí být možné použít její podtřídy bez jejich znalostí
- Definuje za jakých podmínek je možné vytvářet potomky bázových tříd abychom se nedostali do problémů.
Podmínky LSP
- Preconditions - musí platit před provedením kódu
- Postconditions - musí platit po provedení kódu
- Invariants - musí platit po celou dobu existence objektu
- Preconditions - nesmí být zesilovány
- Postconditions - nesmí být oslabovány
- Invariants - musí být zachovány
V podtřídách:
Porušení LSP
(nutná znalost)
class Shape {}
class Rectangle extends shape {}
class Circle extends shape {}
class Drawer {
void draw(Shape shape) {
if (shape instanceof Rectangle) {
//draw
} else if (shape instanceof Circle) {
//draw
}
}
}
Oprava LSP
(nutná znalost)
class Shape {
abstract void draw();
}
class Rectangle extends shape {
@Override
void draw() {
//draw
}
}
class Circle extends shape {
@Override
void draw() {
//draw
}
}
class Drawer {
void draw(Shape shape) {
shape.draw();
}
}
Porušení LSP
(nezaměnitelnost)
class Rectangle {
int width;
int height;
int getArea() {
return width*height;
}
//set & get
}
class Square extends Rectangle {
void setWidth(int width) {
this.width = width;
this.height = height;
}
void setHeight(int height) {
setWidth(height);
}
}
class Test {
void test() {
Rectangle rectangle =
SomeFactory.getRectangle();
rectangle.setWidth(5);
rectangle.setHeight(10);
rectangle.getArea();
//4 Rectangle get 50
//4 Squere get 100
}
}
Oprava LSP
(nezaměnitelnost)
- V setterech nastavuje vždy jen hodnotu které se setter týká. Přidáme kontrolu že čtverec má width==height
- Dědíme z obecnější třídy 'Shape' a fce getArea je abstraktní. Každý potomek si implemetuje fci sám bez přetěžování bázové třídy
Interface segregation principle
- Každé rozhraní by mělo být co nejmenší
- Žádná třída by neměl být nucena používat rozhraní, která nepoužívá
Porušení ISP
interface Lifecycle {
void start();
void stop();
}
Oprava ISP
interface Startable {
void start();
}
interface Stoppable{
void stop();
}
Dependency inversion principle
- Všechny závisloti by měli vést jedním směrem. Od konkrétní k abstraktní.
- Všechny závisloti by měly být na abstraktní třídy a rozhraní, nikdy na konkrétní implementaci
- Přispívá k redukci závislostí
Porušení DIP
class Worker {
void work(){
//working
}
}
class Manager {
Worker worker;
void manage(){
worker.work();
}
//get & set
}
class SuperWorker {
void work(){
//working much more
}
}
Oprava DIP
interface Worker {
void work();
}
class LazyWorker implements Worker {
void work(){
//working
}
}
class Manager {
Worker worker;
void manage(){
worker.work();
}
//get & set
}
class SuperWorker implements Worker{
void work(){
//working much more
}
}
Law of Demeter
(rozmlouvej s přáteli, ne s cizinci)
- Snaha o co největší ukrytí vnitřní struktury objektů a tím snížení provázanosti tříd
Metoda f třídy C smí volat jen:
- metody třídy C
- metody objektů vytvořených metodou f
- metody objektů předaných jako argumenty f
- metody objektů, které jsou instanční proměnnou C
Příznaky porušení LoD
- Zřetězené gettery/mnoho pomocných proměnných
value = object.getX().getY().getValue();
x = object.getX();
y = x.getY();
value = y.getValue();
Porušení LoD
public class Customer {
private String name;
private Wallet myWallet;
//get & set
}
public class Wallet {
private float value;
public void addMoney(float deposit) {
value += deposit;
}
public void subtractMoney(float debit) {
value -= debit;
}
//get & set
}
payment = 2.00;
Wallet wallet = myCustomer.getWallet();
if (wallet.getTotalMoney() > payment) {
wallet.subtractMoney(payment);
//OK
} else {
//NOK
}
Oprava podle LoD
public class Customer {
private String name;
private Wallet myWallet;
public String getName(){
return name;
}
public bool getPayment(float bill) {
if (myWallet != null && myWallet.getTotalMoney() > bill) {
myWallet.subtractMoney(payment);
return true;
} else {
return false;
}
}
payment = 2.00;
if (myCustomer.getPayment(payment)) {
// OK
} else {
// NOK
}
(Ne)Výhody LoD
+ snižování provázanosti tříd
+ třídá má kontrolu svého vnitřního stavu
+ snažší orientace ve třídách
- třída musí poskytovat metody pro manipulaci se svou vnitřní strukturou
navrhove principi 1 SOLID, Demeter Law
By Jiří Čížek
navrhove principi 1 SOLID, Demeter Law
- 435