Oprogramowanie można podzielić na typu według wielu rożnych
kluczy, jednym z nich może być: czy buduje produkt który będę utrzymywał przez lata, czy piszę kod dla kogoś - i przyszłość rozwiązania to nie będzie już moje zmartwienie. Przy tak postawionym pytaniu zasadnicza różnica dotyczy jakości, nie tylko ostatecznego produktu (działa czy nie działa) ale jakości samego rozwiązania. Nie tego co zostało dostarczone ale jak zostało zrobione. Bo właśnie to 'jak' wpływa później na koszt nanoszenia poprawek, dodawania kolejnych elementów, poprawiania ewentualnych błędów. Na wykładzie chciałbym przybliżyć te zasady jak, jak programować aby później z trwoga nie wracać do fragmentów sprzed lat. Jakich trzymać się zasad aby kod był czytelny, klarowny, intencja jasna a całość była dobrze zaprojektowana
19. Kiedy software jest dobry?
Oprogramowanie musi działać
Musi być na czas
Musi być rozbudowywalne
Modyfikowalne
Musi mieć odpowiednią jakość
Punkt wyjścia
20. Kiedy software jest dobry?
Oprogramowanie musi działać
Musi być na czas
Musi być rozbudowywalne
Modyfikowalne
Musi mieć odpowiednią jakość
To zależy???
Punkt wyjścia
21. Więc czym jest jakość
Dla kogoś Dla siebie
Dla kogo pracuje?
22. Więc czym jest jakość
Dla kogoś Dla siebie
Dla kogo pracuje?
23. „Jakość (jak piękno) jest sądem
wartościującym, wyrażonym przez
użytkownika. Jeśli nie ma takiego
użytkownika – nie ma takiego sądu”
Platon
Więc czym jest jakość
24. Więc czym jest jakość
Dla kogoś Dla siebie
Dla kogo pracuje?
25. „Jakość to sposób myślenia,
który powoduje, że stosuje się i
bez przerwy poszukuje
najlepszych rozwiązań”
William Edwards Deming
Więc czym jest jakość
27. If design is a hypothesis
how can you prove it?
Czy mój software jest dobrej jakości?
28. Czy mój software jest dobrej jakości?
Oprogramowanie musi działać
Musi być na czas
Musi być rozbudowywalne
Modyfikowalne
Musi mieć odpowiednią jakość
31. Konkretyzacja pewnej idei
Wprowadzenie uporządkowanego
sposoby pracy
Ujęcie złożonych czynności w zestaw
powtarzalnych kroków
Framework to pewna obietnica
32. No więc mamy to zadanie
I ten ciekawy framework
…
Wyobraźmy sobie nowy projekt…
40. • Kod jest podstawowym medium
komunikacji w projekcie
• Zły kod to jak solenie herbaty koledze
z zespołu albo plucie do kanapki – a
przecież nie jesteśmy złośliwi
Wartości
41. • Jako zespół jesteśmy jednością
– Jak ja pójdę na skróty, to kolega
będzie się męczył
– I jako całość i tak będziemy
nieefektywni
Wartości
42.
43. • Programy są częściej czytane niż
pisane
• Więcej czasu poświęcamy na
modyfikację istniejącego kodu niż na
tworzenie nowego
Implementation Patterns
44.
45. • Komunikacja – kod źródłowy powinno
się czytać jak książkę
• Prostota – wprowadzaj złożoność
tylko wtedy, kiedy jest to konieczne
• Elastyczność – elastyczność to
dodatkowa złożoność, więc
wprowadzaj ją tylko tam gdzie to
konieczne
Implementation patterns
46. • Lokalne konsekwencje – zmiana w
jednym miejscu nie powoduje zmian w
innych
• Minimalne powtórzenia – DRY
Implementation patterns
47. • Dane i logika razem – ponieważ dane
i logika z reguły zmieniają się w tym
samym czasie
• Symetria – utrzymuj podobny poziom
abstrakcji w obrębie metody / klasy
Implementation patterns
48. „Czysty kod jest prosty i bezpośredni.
Czysty kod czyta się jak dobrze
napisaną prozę. Czysty kod nigdy nie
zaciemnia zamiarów projektanta; jest
pełen trafnych abstrakcji i prostych
ścieżek sterowania.”
Grady Booch – to jeden z tych panów od UMLa
51. • Po prostu głęboko wierzymy że dobry
kod nam pomoże
• Choć nie wiemy jak, intuicyjnie
staramy się go stosować
• Z pokorą przyjmujemy karcący wzrok
mnicha
Nie wiemy że nie wiemy
52. • Uczymy się… bez wnikania w kontekst
Nazywaj zmienne w taki a taki sposób
Stosuj komentarze w takich a nie innych
przypadkach
Dziel funkcje na części zgodnie z takimi
a takimi zasadami
• Z czasem zobaczymy że z czystym
kodem lepiej się pracuje… tak po
ludzku
53. Nasz mózg lepiej reaguje na czysty kod
• Utrzymujemy koncentrację
• Nie gubimy wątków, swobodniej
podążamy tokiem myślenia
• Cognitive load – możemy pomieścić
poszczególne kawałki kodu w głowie
więc potrafimy się miedzy nimi
swobodnie przemieszczać
54. • Nazwy
• Funkcje
• Komentarz
• Formowanie kodu
• Obiekty i struktury danych
• Obsługa błędów
Poziom I
55. • Nazwy
• Funkcje
• Komentarz
• Formatowanie kodu
• Obiekty i struktury danych
• Obsługa błędów
Poziom I
56. • Nazwy zmiennych, metod, klas
powinny być wystarczająco opisowe
aby zrozumieć jaką wartość
przetrzymuje zmienna i jaką czynność
wykonuje metoda.
Zmienne
57. • Nazwy nie powinny wymagać
dodawania komentarza
• Nazwy zmiennych nie mogą
wprowadzać w błąd!
• Nazwy metod nie mogą ukrywać
funkcjonalności!
Nazewnictwo
58. int d1; //dni od rozpoczęcia
int d2; //dni do zakończenia
int d3; //dni wolnych
int daysSinceStart;
int daysTillEnd;
int daysOf;
public List<int[]> getThem() {
List<int[]> list1 = new ArrayList<int[]>();
for (int[] x : theList)
if (x[0] == 4)
list1.add(x);
return list1;
} public List<int[]> getDates() {
List<int[]> dateList = new ArrayList<int[]>();
for (int[] week : theWeeksArray)
if (week[0] == BEGIN_DATE)
dateList.add(x);
return dateList;
}
59. for (int i=0; i<10; i++){
k += ((l[i]*1.5) / 3 );
}
float milleageRate;
const int NUMER_OF_EMPLOYEE = 3;
float sum = 0;
for ( int i=0; i<numberOfTrips; i++ ){
float totalCompensation = tripLength[i] * milleageRate;
float deduction = totalCompensation / NUMER_OF_EMPLOYEE;
sum += deduction;
}
60. • Korzystaj z nazw, które można
wymówić
Ułatwiają zapamiętywanie
Umożliwiają swobodną dyskusję o kodzie
Nazwewnictwo
61. public class CsmrDt {
public void crtshpcrt() {/*...*/};
public void remcrt() {/*...*/};
private final int ssntm = 10;
/*...*/
}
public class CustomerDataset {
public void createShoppingCart() {/*...*/};
public void removeCart() {/*...*/};
private final int sessionTimeout = 10;
/*...*/
}
62. • Używaj rzeczowników lub wyrażeń
rzeczownikowych do nazywania klas
np.: Customer, Account, Page
• Nie nazywaj klas za pomocą
czasowników
• Używaj nazw konkretnych a nie
“wszystkoznaczących”
ManagerFactoryProcessor
Klasy
63. • Używaj czasowników lub wyrażeń
czasownikowych do nazywania metod np.:
addUser, deleteCustomer, update
• Twórz nazwy metod tak, aby wyrażały to co
robią
Metody
64. • Nazwy powinny sugerować co zwracają
• Nazwy muszą mówić o całym zakresie
funkcjonalności
Metody
String findLastNameOfCustomerWithId(long customerId){...}
Map<Long, Customer> customers;
Customer getCustomer(Long id){
Customer customer = customers.get(id);
if(customer == null){
customer = createNewCustomer(id);
}
return customer;
}
65. • Bądź konsekwentny i używaj tych samych
słów do określania podobnych czynności,
np.: add, get, DeviceManager,
ProtocolController
Metody
66. • Nazwy
• Funkcje
• Komentarz
• Formatowanie kodu
• Obiekty i struktury danych
• Obsługa błędów
Poziom I
67. • Zasada pierwsza:
funkcje powinny być małe
• Zasada druga:
funkcje powinny być jeszcze mniejsze
Funkcje
71. Funkcje
• Korzystaj z nazw dokładnie opisujących
przeznaczenie funkcji
• Nie obawiaj się konstruowania długich nazw
• Stosuj spójne nazwy - wykorzystuj te same frazy,
rzeczowniki czy czasowniki dla funkcji
wykonujących analogiczne operacje
• addCustomer, putItemToBasket
• UserSettings, AdminConfiguration
• Nie ukrywaj funkcjonalności za złymi nazwami
funkcji
72. Funkcje
• Pisz funkcje zawierające jak najmniejszą liczbę
argumentów
• Funkcją idealną jest funkcja bezargumentowa
• Dopuszczalne są funkcje
jedno- i dwuargumentowe
• Unikaj funkcji już z trzema argumentami
• Funkcje posiadające więcej niż trzy argumenty
stosuj tylko w uzasadnionych sytuacjach
74. Funkcje
• Stosuj wyjątki zamiast zwracania
kodów błędów
• Korzystanie z kodów błędów tworzy
głęboko zagnieżdżone struktury
• Wyjątki pozwalają na oddzielenie
przetwarzania błędów od prawidłowej
ścieżki wykonywania kodu
75. if (changeDate(date) == OK) {
if (dateFilled(date) == OK) {
if (dateFromFuture(date) == OK) {
System.out.println("Data została zmieniona.");
} else {
System.out.println("Data niepoprawna.");
}
} else {
System.out.println("Brak podanej daty.");
}
} else {
System.out.println("Błąd podczas zmiany daty.");
return ERROR;
}
try {
changeDate(date);
dateFilled(date);
dateFromFuture(date);
}
catch (Exception e) {
System.out.println(e.getMessage());
}
76. Funkcje
DRY – Don’t repeat yourself
• Duplikacja zmniejsza czytelność
• Zwiększa koszty utrzymania, refactoringu i
poprawiania błędów
• Prowadzi do rozbieżności funkcjonalnej
modułów wykonujących to samo
• Zmniejsza reusability kodu
77. Funkcje
Jeden poziom abstrakcji na funkcje
• Nie realizuj w funkcji zadań które operują
na innych poziomach abstrakcji
• Pomieszane poziomy abstrakcji
zmniejszają czytelność kodu, utrudniają
zrozumienie logiki i prowadzą do
zwiększenia duplikacji kodu
78.
79.
80. • Nazwy
• Funkcje
• Komentarz
• Formatowanie kodu
• Obiekty i struktury danych
• Obsługa błędów
Poziom I
83. Komentarze
Są złe bo
• Często się powtarzają
• Są mylące
• Nadmiarowe
• Klamry zamykające
• Zakomentowany kod
• Informacje nielokalne
• Nie są utrzymywane
85. //Sprawdzenie czy klient ma możliwość korzystania ze zniżki
if (customer.isStudent() ||
(customer.age < 18) || (customer.age > 65))
if (customer.isEligibleForDiscount())
87. Komentarze
• Nieprawdą jest, że wszystkie funkcje
powinny posiadać Javadoc, a każda
zmienna komentarz
• Ten typ komentarzy powoduje
zaciemnianie kodu i dezorganizację
88. public abstract class ContainerBase implements
Container, Lifecycle, Pipeline,
MBeanRegistration, Serializable {
/**
* The processor delay for this component.
*/
protected int backgroundProcessorDelay = -1;
/**
* The lifecycle event support for this component.
*/
protected LifecycleSupport lifecycle = new LifecycleSupport(this);
/**
* The container event listeners for this Container.
*/
protected ArrayList listeners = new ArrayList();
/**
* The Loader implementation with which this Container is associated.
*/
protected Loader loader = null;
/**
* The Logger implementation with which this Container is associated.
*/
protected Log logger = null;
/**
* Associated logger name.
*/
protected String logName = null;
/**
* The Manager implementation with which this Container is associated.
*/
protected Manager manager = null;
89. „If you decide to write a comment, then
spend the time necessary to make sure it is
the best comment you can write”
Robert C. Martin
Komentarze
90. • Nazwy
• Funkcje
• Komentarz
• Formatowanie kodu
• Obiekty i struktury danych
• Obsługa błędów
Poziom I
92. Formatowanie
• Odpowiednie formatowanie kodu
ułatwia czytanie klasy.
• Redukuje czas potrzebny na szukanie
składowych, konstruktorów itp.
• Nie musimy przeglądać całej klasy aby
sprawdzić czy ma konstruktor bo
wiemy dokładnie gdzie go szukać
93.
94. • Nazwy
• Funkcje
• Komentarz
• Formatowanie kodu
• Obiekty i struktury danych
• Obsługa błędów
Poziom I
95. • Ukrywaj szczegóły techniczne i stosuj
terminy abstrakcyjne
Abstrakcja danych
public static interface Vehicle {
double getFuelTankCapacityInGallons();
double getGallonsOfGasoline();
}
public static interface Vehicle {
double getPercentFuelRemaining();
}
96. • Prawo Demeter – zasada minimalnej
wiedzy
• Moduł powinien nie wiedzieć nic o
wnętrzu obiektów, którymi manipuluje
Prawo Demeter
97. • Prawo Demeter głosi, że metoda f
klasy C powinna wywoływać tylko
metody z:
• Klasy C,
• Obiektu utworzonego przez f,
• Obiektu przekazanego jako argument f,
• Obiektu umieszczonego w zmiennej
instancyjnej klasy C.
Prawo Demeter
98. • Możesz bawić się ze sobą
• Możesz bawić się własnymi
zabawkami (ale nie możesz ich
rozbierać)
• Możesz bawić się zabawkami które
dostałeś
• Możesz bawić się zabawkami które
zrobiłeś samodzielnie
Prawo Demeter
99. final String outputDir = context.getOptions().getScratchDir().getAbsolutePath();
Options options = context.getOptions();
File scratchDir = options.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();
100. // before refactoring
class Customer {
String getMailingAddressLine1();
String getMailingAddressLine2();
String getShippingAddressLine1();
String getShippingAddressLine2();
}
class Employee {
String getHomeAddressLine1();
String getHomeAddressLine2();
}
public void method() {
customer.getMailingAddressLine1();
employee.getHomeAddressLine1();
}
101. // before refactoring
class Customer {
String getMailingAddressLine1();
String getMailingAddressLine2();
String getShippingAddressLine1();
String getShippingAddressLine2();
}
class Employee {
String getHomeAddressLine1();
String getHomeAddressLine2();
}
public void method() {
customer.getMailingAddressLine1();
employee.getHomeAddressLine1();
}
// after refactoring, before Demetering
class Address {
String getLine1();
String getLine2();
}
Class Customer {
Address getMailingAddress();
Address getShippingAddress();
}
class Employee {
Address getHomeAddress();
}
public void method() {
customer.getMailingAddress().getLine1();
employee.getHomeAddress().getLine1();
}
102. // before refactoring
class Customer {
String getMailingAddressLine1();
String getMailingAddressLine2();
String getShippingAddressLine1();
String getShippingAddressLine2();
}
class Employee {
String getHomeAddressLine1();
String getHomeAddressLine2();
}
public void method() {
customer.getMailingAddressLine1();
employee.getHomeAddressLine1();
}
// after refactoring, before Demetering
class Address {
String getLine1();
String getLine2();
}
Class Customer {
Address getMailingAddress();
Address getShippingAddress();
}
class Employee {
Address getHomeAddress();
}
public void method() {
customer.getMailingAddress().getLine1();
employee.getHomeAddress().getLine1();
}
//after Demeterizing
class Address { // same as above
String getLine1();
String getLine2();
}
class Customer {
Address getMailingAddress();
Address getShippingAddress();
String getMailingAddressLine1();
String getMailingAddressLine2();
String getShippingAddressLine1();
String getShippingAddressLine2();
}
class Employee {
Address getHomeAddress();
String getHomeAddressLine1();
String getHomeAddressLine2();
}
public void method() {
customer.getMailingAddressLine1();
employee.getHomeAddressLine1();
}
103. • Ukrywaj szczegóły wnętrza obiektów
• Nie łam hermetyzacji bez powodu,
łatwiejsze pisanie testów nie jest
powodem
Prawo Demeter
BufferedOutputStream bos =
context.createScratchFileStream(classFileName);
104. • Za idealną formę struktur danych
przyjmuje się obiekty transferu danych
(DTO)
• Są przydatne do komunikowania się z
bazą danych, analizowania
komunikatów z gniazd sieciowych itp.
Data Transfer Objects
105. • DTO są to mogą być klasy
pozbawione funkcji, ale ze zmiennymi
publicznymi
• Częściej spotykaną formą jest postać
typu beans
• Posiada ona zmienne prywatne, do
których dostęp uzyskuje się za
pomocą getterów i setterów
Data Transfer Objects
106. public class Client {
private String name;
private String lastName;
private long clientId;
public Client(long clientId, String name, String lastName) {
this.clientId = clientId;
this.name = name;
this.lastName = lastName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLastName() {
return lastName;
}
public void setlastName(String lastName) {
this.lastName = lastName;
}
public long getClientId() {
return clientId;
}
}
107. Budowanie oprogramowania z
wykorzystaniem struktur danych
wpływa w przyszłości na:
Budowanie oprogramowania z
wykorzystaniem obiektów wpływa na:
łatwość i elastyczność dodawania
nowych funkcji – dodanie nowej
funkcji nie wymaga zmiany żadnej ze
struktur
łatwość i elastyczność w dodawaniu
nowych obiektów – nie wymaga
zmiany żadnych funkcji ani innych
obiektów
trudność dodawania nowych struktur
– musimy zmienić wszystkie funkcje
trudność dodawania nowych funkcji –
musimy zmienić wszystkie obiekty
Obiekty vs struktury danych
108. • Nazwy
• Funkcje
• Komentarz
• Formatowanie kodu
• Obiekty i struktury danych
• Obsługa błędów
Poziom I
109. • Mówiliśmy o kodach błędu i wyjątkach
• O blokach try-catch-finally
110. • Grupuj wyjątki ze względu na kontekst i
zachowuj symetrię w kodzie
• Osłaniaj wyjątki:
• przechwytuj i przekształcaj zgłaszane wyjątki
• uniezależniaj się od wyjatków wprowadzonych przez
API, używaj wyjątków specyficznych dla własnej
aplikacji
Klasy wyjątków
114. • Wykorzystuj zgłaszanie wyjątków lub
zwracanie obiektu specjalnego
przypadku
Zwracanie null
List<Item> items = getItems();
if (items != null) {
for (Item i : items) {
totalCost += i.getCost();
}
}
List<Item> items = getItems();
for(Item i : items) {
totalCost += i.getCost();
}
115. public interface Animal {
public void makeSound();
}
public class Dog implements Animal {
public void makeSound() {
System.out.println("woof!");
}
}
public class NullAnimal implements Animal {
public void makeSound() {
}
}
116. • Przekazywanie null jest gorsze od
jego zwracania
Przekazywanie null
public class MetricsCalculator {
public double rectanglePerimeterCalculate(
double x, double y) {
return 2 * (y + x);
}
/* ... */
}
117. • Defensive programming
Przekazywanie null
public class MetricsCalculator {
public double rectanglePerimeterCalculate(
double x, double y) {
if (x == null || y == null) {
throw InvalidArgumentException("Niewłaściwy
argument.");
}
return 2 * (y + x);
}
} public class MetricsCalculator {
public double rectanglePerimeterCalculate(
double x, double y) {
assert x != null : "x nie może być null";
assert y != null : "y nie może być null";
return 2 * (y + x);
}
}
120. • SOLID
• Zasady programowania obiektowego
• Zasady projektowania obiektowego
Poziom II
121. SOLIDny programista
• The Single Responsibility Principle – klasa
powinna mieć tylko jeden powód do zmiany
• The Open Closed Principle – klasę można
łatwo rozszerzać, nie modyfikując jej
• The Liskov Substitution Principle – klasy
pochodne muszą być przeźroczystymi
zamiennikami klasy nadrzędnej
122. • The Interface Segregation Principle – dla
różnych klientów twórz osobne interfejsy
• The Dependency Inversion Principle –
bądź zależny od abstrakcji a nie od
konkretnych implementacji
SOLIDny programista
123. • Należy znać hierarchię dziedziczenia, aby
zrozumieć metodę
• Kod metody polimorficznej jest rozrzucony
po kilku klasach
• Nie zawsze jest oczywisty stan obiektu, z
którego dziedziczymy
• Dodatkowe elementy (pola, metody) są
mocno związane (coupling) z klasą
nadrzędną
Kompozycja i dziedziczenie
124. • Niezależność klas zawieranych
• Można je używać w wielu kontekstach
• Niskie związanie (coupling)
• Łatwiej testować
Kompozycja i dziedziczenie
126. • Odpowiedzialność – tylko jedna
obiekty maja własną osobowość,
unikaj schizofrenicznych obiektów
• Enkapsulacja – to co się zmienia
enkaspuluj
• Preferencja kompozycji ponad
dziedziczenie
127. • Dokładanie ponad modyfikacje
– Gdy dodajemy nową funkcjonalność raczej dokładamy
nowe byty niż modyfikujemy istniejące.
• Lokalne zmiany
– Zmiana ma konsekwencje lokalne, a nie globalne.
Zasięg rażenia zmian jest jak najmniejszy.
• Nieinwazyjność zmian
– Dodanie nowych rzeczy (odpowiedzialności,
funkcjonalności, zachowań) do istniejących bytów jest
przezroczyste ich dla klientów.
128. public class Sql {
public Sql(String table, Column[] columns)
public String create()
public String insert(Object[] fields)
public String selectAll()
public String fieldByKey(
String keyColumn, String keyValue)
private String ColumnList(Column[] columns)
private String valuesList(
Object[] fields, final Column[] columns)
}
129. abstract public class Sql {
public Sql(String table, Column[] columns)
abstract public String generate();
}
public class CreateSql extends Sql {
public CreateSql(String table, Column[] columns)
@Override public String generate()
}
public class SelectSql extends Sql {
public SelectSql(String table, Column[] columns)
@Override public String generate()
}
public class InsertSql extends Sql {
public InsertSql(String table, Column[] columns)
@Override public String generate()
private String valuesList(Object[] fields, final Column[] columns)
}
public class FindKeyBySql extends Sql {
public FindKeyBySql(String table, Column[] columns, String keyColumn, String keyValue)
@Override public String generate()
}
public class ColumnList {
public ColumnList(Column[] columns)
public String generate()
}
130. Odpowiedzialność. Enkapsulacja. Kompozycja
• To co leży u podstaw możemy
stosować na każdym poziomie
• Projektując klasy
• Projektując moduły, komponenty
• Projektując systemy
131. • Każdy wzorzec opisujemy w trzech
kontekstach:
Odpowiedzialność
Enkapsulacja / hermetyzacja
Kompozycja
Wzorce projektowe
132. • Rozdziela i przesłania implementację
zachowań, czynności.
• Hermetyzujemy zmienny sposób
tworzenia obiektów
• Nowe funkcjonalności uzyskujemy
poprzez dodawanie komend, poprzez
kompozycję.
Wzorce projektowe: komenda
137. • Każdy framework opisujemy w trzech
zdaniach:
Odpowiedzialność
Enkapsulacja
Preferowanie kompozycji
Frameworki
138. • Pozwala na obiektowy dostęp do bazy
danych
• Enkapsulujemy typ bazy, sposób
dostępu do danych
• Preferencja kompozycji (?)
ORM
139. • Nie ważne czy na poziomie klasy,
modułu, frameworka – stosujemy te
same bazowe pojęcia
• Dedukujemy, indukujemy –
posługujemy się doświadczeniem
• Rozumiemy że nie ma uniwersalnych
rozwiązań – ważny jest kontekst.
156. Affordance
a quality of an object, which
allows an individual to perform
an action. For example, a knob
affords twisting, and perhaps
pushing, while a cord affords
pulling
157. public class Sql {
public Sql(String table, Column[] columns)
public String create()
public String insert(Object[] fields)
public String selectAll()
public String fieldByKey(
String keyColumn, String keyValue)
private String ColumnList(Column[] columns)
private String valuesList(
Object[] fields, final Column[] columns)
}
158. abstract public class Sql {
public Sql(String table, Column[] columns)
abstract public String generate();
}
public class CreateSql extends Sql {
public CreateSql(String table, Column[] columns)
@Override public String generate()
}
public class SelectSql extends Sql {
public SelectSql(String table, Column[] columns)
@Override public String generate()
}
public class InsertSql extends Sql {
public InsertSql(String table, Column[] columns)
@Override public String generate()
private String valuesList(Object[] fields, final Column[] columns)
}
public class FindKeyBySql extends Sql {
public FindKeyBySql(String table, Column[] columns, String keyColumn, String keyValue)
@Override public String generate()
}
public class ColumnList {
public ColumnList(Column[] columns)
public String generate()
}
177. • Nie ma kodu perfekcyjnego
• Ale jest wystarczająco dobry
modyfikowalny
refaktoryzowalny
„disposible” over „maintainable”
• Kod który mogę w każdej chwili
wyrzucić i wymienić
Tworzymy dobry kod