Костадин Голев 
@kotseto 
github.com/kotse 
WRITING SOLID CODE 
преразказ с елементи на разсъждение
SOLID CODE ДИЗАЙН ПРИНЦИПИ 
• Първоначално наречени “първите 
пет принципа” от чичо Боб 
• SOLID e акроним, съставен от 
първата буква на всеки принцип 
• Целят да ни помогнат да пишем код, 
който е по-лесен за четене, 
поддръжка и надграждане
КОИ СА SOLID ПРИНЦИПИТЕ? 
• (S)ingle Responsibility Principle 
• (O)pen-Closed Principle 
• (L)iskov Substitution Principle 
• (I)nterface segregation Principle 
• (D)ependency inversion Principle
КАК ГИ ИЗПОЛЗВАМЕ? 
• Не са правила или закони 
• Идеи, които ни помагат в взимане на решения 
• Служат като средство да комуникираме дизайна на 
кода, който пишем 
• Ако имаме усещането, че един код е добър или лош, 
често можем да намерим принцип, който да обясни 
това
КРУШКАТА И ВЕНТИЛАТОРА
SINGLE RESPONSIBILITY PRINCIPLE 
Всеки клас/функция/променлива трябва да прави 
едно нещо и да го прави добре 
По-просто - само една причина да се промени
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!”); 
} 
}
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!”); 
} 
}
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”); 
} 
}
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!”); 
} 
}
SUMMARY 
• Може би най-труден за прилагане от петте 
принципа 
• По-четим и лесен за преизползване код 
• Код в един метод се асоциира с името на метода 
• Името на всеки метод се асоциира с името на 
класа
СТАРИЯ ТЕЛЕВИЗОР
OPEN-CLOSED PRINCIPLE 
Кодът, който пишем трябва да е отворен за 
разширение и затворен за модификация
class PaymentService { 
int calculatePayment (int amount, Customer customer) { 
double discount = 0; 
switch (customer.type()) { 
case SILVER : discount = 0.1; break; 
case GOLD : discount = 0.2; break; 
... 
... 
} 
amountToPay = amount - amount*discount; 
return amountToPay; 
} 
}
class Customer { 
double getDiscount() { 
return 0; 
} 
} 
class SilverCustomer extends Customer { 
double getDiscount() { 
return 0.1; 
} 
} 
class GoldCustomer extends Customer { 
double getDiscount() { 
return 0.2; 
} 
}
class PaymentService { 
int calculatePayment (int amount, Customer customer) { 
double discount = customer.getDiscount(); 
amountToPay = amount - amount*discount; 
return amountToPay; 
} 
}
SUMMARY 
• Вместо да променяме код, който вече работи, 
надграждаме го 
• Използваме наследяване за целта 
• Нарушаване на принципа води до много трудна 
поддръжка на кода, тъй като една малка промяна 
в един клас води до множество промени в други
САЛАТЕНИЯ БАР
LISKOV SUBSTITUTION PRINCIPLE 
Обектите в програмата могат да бъдат заменени 
от наследниците им без промяна в тяхното 
поведение
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! 
} 
} 
}
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!
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!
SUMMARY 
• Ако бъде нарушен, даден обект нарушава 
обичайното си “поведение” 
• На практика представлява допълнение към 
Open-Closed принципа, като ни "повелява", че 
не можем едновременно да надградим един 
модул и да променим неговото поведение
КНИГАТА С РЕЦЕПТИ
INTERFACE SEGREGATION PRINCIPLE 
Не принуждавайте кода си за зависи от неща, от 
които няма нужда
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(); 
} 
}
public class Robot implements Worker { 
public void work() {} 
public void getPaid() {} ??? 
}
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() { 
} 
}
class Manager { 
IWork worker; 
void setWorker(IWork worker) { 
this.worker = worker; 
} 
void manage() { 
worker.work(); 
} 
}
SUMMARY 
• Да се пише код, който да е лесен за разбиране е 
не по-малко важно от това кода да работи 
• Интерфейсите ни помагат да опишем как трябва 
да работи кода, който пишем 
• Множество малки интерфейси е по-добре от това 
да имаме един голям т.нар "замърсен" интерфейс
ФАБРИКАТА В ЛОВЕЧ
DEPENDENCY INVERSION PRINCIPLE 
Зависимостите в кода е добре да се основават на 
абстракции, а не конкретни неща 
Модулите на по-високо ниво в кода не трябва да 
зависят от тези на по-ниско
public class Steed5 { 
Diesel130HPEngine engine; 
public Steed5() { 
engine = new Diesel130HPEngine(); 
} 
public void start() { 
engine.ignition(); 
} 
} 
Steed5 car = new Steed5(); 
car.start();
public class Steed5V8 { 
V8Engine engine; 
public Steed5V8() { 
engine = new V8Engine(); 
} 
public void start() { 
engine.ignition(); 
} 
}
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();
public class Steed5 { 
Engine engine; 
public Steed5(Engine engine) { 
this.engine = engine; 
} 
public void start() { 
engine.ignition(); 
} 
}
SUMMARY 
• При нарушаване на принципа поддръжката на 
кода се затруднява значително 
• Ако един модул се променя, той не трябва да 
променя модулите на по-високо ниво от 
неговото
Благодаря за вниманието!

Presentation on SOLID design principles

  • 1.
    Костадин Голев @kotseto github.com/kotse WRITING SOLID CODE преразказ с елементи на разсъждение
  • 2.
    SOLID CODE ДИЗАЙНПРИНЦИПИ • Първоначално наречени “първите пет принципа” от чичо Боб • SOLID e акроним, съставен от първата буква на всеки принцип • Целят да ни помогнат да пишем код, който е по-лесен за четене, поддръжка и надграждане
  • 3.
    КОИ СА SOLIDПРИНЦИПИТЕ? • (S)ingle Responsibility Principle • (O)pen-Closed Principle • (L)iskov Substitution Principle • (I)nterface segregation Principle • (D)ependency inversion Principle
  • 4.
    КАК ГИ ИЗПОЛЗВАМЕ? • Не са правила или закони • Идеи, които ни помагат в взимане на решения • Служат като средство да комуникираме дизайна на кода, който пишем • Ако имаме усещането, че един код е добър или лош, често можем да намерим принцип, който да обясни това
  • 5.
  • 6.
    SINGLE RESPONSIBILITY PRINCIPLE Всеки клас/функция/променлива трябва да прави едно нещо и да го прави добре По-просто - само една причина да се промени
  • 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 • Можеби най-труден за прилагане от петте принципа • По-четим и лесен за преизползване код • Код в един метод се асоциира с името на метода • Името на всеки метод се асоциира с името на класа
  • 13.
  • 14.
    OPEN-CLOSED PRINCIPLE Кодът,който пишем трябва да е отворен за разширение и затворен за модификация
  • 16.
    class PaymentService { int calculatePayment (int amount, Customer customer) { double discount = 0; switch (customer.type()) { case SILVER : discount = 0.1; break; case GOLD : discount = 0.2; break; ... ... } amountToPay = amount - amount*discount; return amountToPay; } }
  • 17.
    class Customer { double getDiscount() { return 0; } } class SilverCustomer extends Customer { double getDiscount() { return 0.1; } } class GoldCustomer extends Customer { double getDiscount() { return 0.2; } }
  • 18.
    class PaymentService { int calculatePayment (int amount, Customer customer) { double discount = customer.getDiscount(); amountToPay = amount - amount*discount; return amountToPay; } }
  • 19.
    SUMMARY • Вместода променяме код, който вече работи, надграждаме го • Използваме наследяване за целта • Нарушаване на принципа води до много трудна поддръжка на кода, тъй като една малка промяна в един клас води до множество промени в други
  • 20.
  • 21.
    LISKOV SUBSTITUTION PRINCIPLE Обектите в програмата могат да бъдат заменени от наследниците им без промяна в тяхното поведение
  • 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 implementsDuck { 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 принципа, като ни "повелява", че не можем едновременно да надградим един модул и да променим неговото поведение
  • 27.
  • 28.
    INTERFACE SEGREGATION PRINCIPLE Не принуждавайте кода си за зависи от неща, от които няма нужда
  • 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 Robotimplements 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() { } }
  • 33.
    class Manager { IWork worker; void setWorker(IWork worker) { this.worker = worker; } void manage() { worker.work(); } }
  • 34.
    SUMMARY • Дасе пише код, който да е лесен за разбиране е не по-малко важно от това кода да работи • Интерфейсите ни помагат да опишем как трябва да работи кода, който пишем • Множество малки интерфейси е по-добре от това да имаме един голям т.нар "замърсен" интерфейс
  • 35.
  • 36.
    DEPENDENCY INVERSION PRINCIPLE Зависимостите в кода е добре да се основават на абстракции, а не конкретни неща Модулите на по-високо ниво в кода не трябва да зависят от тези на по-ниско
  • 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 • Принарушаване на принципа поддръжката на кода се затруднява значително • Ако един модул се променя, той не трябва да променя модулите на по-високо ниво от неговото
  • 44.