1. Костадин Голев
@kotseto
github.com/kotse
WRITING SOLID CODE
преразказ с елементи на разсъждение
2. SOLID CODE ДИЗАЙН ПРИНЦИПИ
• Първоначално наречени “първите
пет принципа” от чичо Боб
• SOLID e акроним, съставен от
първата буква на всеки принцип
• Целят да ни помогнат да пишем код,
който е по-лесен за четене,
поддръжка и надграждане
4. КАК ГИ ИЗПОЛЗВАМЕ?
• Не са правила или закони
• Идеи, които ни помагат в взимане на решения
• Служат като средство да комуникираме дизайна на
кода, който пишем
• Ако имаме усещането, че един код е добър или лош,
често можем да намерим принцип, който да обясни
това
6. SINGLE RESPONSIBILITY PRINCIPLE
Всеки клас/функция/променлива трябва да прави
едно нещо и да го прави добре
По-просто - само една причина да се промени
7.
8. class TaxiService {
public void orderTaxi(String phoneNumber, String address) {
if (phone.length() < 7
|| phone.length() > 12
|| phone.contains(BAD_CHARACTERS)) {
throw new Exception("Phone number not valid!");
}
Taxi taxi = taxiPool.getTaxi(address);
smsClient.sendMessage(phoneNumber, “Taxi on the way!”);
}
}
9. class TaxiService {
boolean validatePhoneNumber() {
if (phone.length() < 7
|| phone.length() > 12
|| phone.contains(BAD_CHARACTERS)) {
return false;
}
return true;
}
public void orderTaxi(String phoneNumber, String address) {
if (!validatePhoneNumber(phoneNumber)) {
throw new Exception("Phone number not valid!");
}
Taxi taxi = taxiPool.getTaxi(address);
smsClient.sendMessage(phoneNumber, “Taxi on the way!”);
}
}
10. class SMSService {
boolean validatePhoneNumber() {
if (phone.length() < 7
|| phone.length() > 12
|| phone.contains(BAD_CHARACTERS)) {
return false;
}
return true;
}
void sendSms(String phoneNumber, String message) {
smsClient.sendMessage(phoneNumber, “Taxi on the way”);
}
}
11. class TaxiService {
SMSService smsService;
public void orderTaxi(String phoneNumber, String address) {
if (smsService.validatePhoneNumber(phoneNumber)) {
throw new Exception("Phone number not valid!");
}
Taxi taxi = taxiPool.getTaxi(address);
smsService.sendSMS(phoneNumber, “Taxi on the way!”);
}
}
12. SUMMARY
• Може би най-труден за прилагане от петте
принципа
• По-четим и лесен за преизползване код
• Код в един метод се асоциира с името на метода
• Името на всеки метод се асоциира с името на
класа
19. SUMMARY
• Вместо да променяме код, който вече работи,
надграждаме го
• Използваме наследяване за целта
• Нарушаване на принципа води до много трудна
поддръжка на кода, тъй като една малка промяна
в един клас води до множество промени в други
21. LISKOV SUBSTITUTION PRINCIPLE
Обектите в програмата могат да бъдат заменени
от наследниците им без промяна в тяхното
поведение
22.
23. public interface Duck {
public void papa();
}
public class RealDuck implements Duck {
public void papa() {
//papa code goes here!
}
}
public class ElectricDuck implements Duck {
public void papa() {
if (turnedOn) {
//papa code goes here!
}
}
}
24. Duck realDuck = new RealDuck();
duck.papa(); //works!
Duck electricDuck = new ElectricDuck();
duck.papa(); //does not work!
if (duck instanceof ElectricDuck) {
((ElectricDuck)duck).turnOn();
}
duck.papa(); //now works!
25. class ElectricDuck implements Duck {
public void turnOn() {
turnedOn = true
}
public void papa() {
if (!turnedOn) {
turnOn();
}
//papa code goes here!
}
}
Duck duck = new AnyKindOfDuck();
duck.papa(); //just works with all Ducks now!
26. SUMMARY
• Ако бъде нарушен, даден обект нарушава
обичайното си “поведение”
• На практика представлява допълнение към
Open-Closed принципа, като ни "повелява", че
не можем едновременно да надградим един
модул и да променим неговото поведение
30. public interface Worker {
public void work();
public void getPaid();
}
public class RegularWorker implements Worker {
public void work() {}
public void getPaid() {}
}
public class Manager {
Worker worker;
public void setWorker(Worker worker) {
this.worker = worker;
}
public manage () {
worker.work();
}
}
31. public class Robot implements Worker {
public void work() {}
public void getPaid() {} ???
}
32. interface IWork {
public void work();
}
interface IGetPaid {
public void getPaid();
}
class Worker implements IWork, IGetPaid {
@Override
public void work() {
}
@Override
public void getPaid() {
}
}
class Robot implements IWork {
@Override
public void work() {
}
}
34. SUMMARY
• Да се пише код, който да е лесен за разбиране е
не по-малко важно от това кода да работи
• Интерфейсите ни помагат да опишем как трябва
да работи кода, който пишем
• Множество малки интерфейси е по-добре от това
да имаме един голям т.нар "замърсен" интерфейс
36. DEPENDENCY INVERSION PRINCIPLE
Зависимостите в кода е добре да се основават на
абстракции, а не конкретни неща
Модулите на по-високо ниво в кода не трябва да
зависят от тези на по-ниско
37.
38. public class Steed5 {
Diesel130HPEngine engine;
public Steed5() {
engine = new Diesel130HPEngine();
}
public void start() {
engine.ignition();
}
}
Steed5 car = new Steed5();
car.start();
39. public class Steed5V8 {
V8Engine engine;
public Steed5V8() {
engine = new V8Engine();
}
public void start() {
engine.ignition();
}
}
40. public interface Engine {
public void ignition();
}
public class V8Engine implements Engine {
public void ignition () {}
}
Engine engine = new V8Engine();
Steed5 car = new Steed5(engine);
car.start();
41. public class Steed5 {
Engine engine;
public Steed5(Engine engine) {
this.engine = engine;
}
public void start() {
engine.ignition();
}
}
42. SUMMARY
• При нарушаване на принципа поддръжката на
кода се затруднява значително
• Ако един модул се променя, той не трябва да
променя модулите на по-високо ниво от
неговото