OCP
#программирование

SOLID. Что значит O

Это второй пост из цикла статей про SOLID. Сегодня мы разберем, что означает буква O в этой аббревиатуре. Как обычно, мы рассмотрим принцип на примере и поговорим о том, как он нам помогает, что было бы без него и зачем он вообще нужен.

O значит OCP

Что, в свою очередь, расшифровывается как Open-Closed Principle, он же принцип открытости/закрытости. Чисто теоретическое описание доступно на википедии, а мы сразу переходим к примеру.

OCP на практике

Представьте, что вы возвращаетесь в московский аэропорт «Домодедово» из своего отпуска по Европе. Вы выходите из самолета, проходите пару длинных коридоров и оказываетесь у стоек паспортного контроля.

Перед вами две стойки – для граждан Российской Федерации и для граждан других государств. В аэропорт одновременно приходит множество рейсов, и вполне логично для своих граждан выделить отдельную стойку, чтобы они получили приоритет и максимально быстро вернулись домой. Вы пользуетесь этой возможностью. 

Это – правильная и грамотная реализация OCP. Давайте теперь посмотрим, что произошло бы, если бы проектировщики аэропорта нарушили этот принцип.

Игнорирование OCP

Давайте на минуту представим, что аэропорт по-прежнему хочет предоставить своим гражданам преимущество, но при этом не следует принципу открытости/закрытости.

Итак, перед стойкой паспортного контроля стоит очередь из граждан разных государств, в том числе и РФ. Вдруг выходит сотрудник аэропорта и объявляет: «Внимание! Ближайшие два часа обслуживаются только граждане Российской Федерации!».

Граждане РФ ликуют. Все остальные в аэропорту:

Что произошло?

А произошло вот что: аэропорт решил добавить новую фичу (предоставление приоритета своим гражданам) и при этом изменил реализацию паспортного контроля таким образом, что это оказалось неожиданностью для всех.

Как нужно было сделать? Оставить стойку в покое и никак не менять порядок паспортного контроля на ней. Вместо этого добавить еще одну стойку для граждан РФ. Собственно, как это и реализовано во всех аэропортах.

Пример кода

Давайте теперь посмотрим, как бы это выглядело в коде. Представим, что вы – разработчик класса PassportControl.

Вы отвечаете за паспортный контроль прилетевших граждан. Вы проводите идентификацию пассажира и разного рода проверки. Например, граждане с просроченными паспортами и визами не должны пройти паспортный контроль. Если же все хорошо, вы ставите печать о въезде в страну и пропускаете гражданина.

public class PassportControl {

  public void process(List<Passenger> passengers) {
    passengers.forEach(passenger -> pass(passenger));
  }

  private void pass(Passenger passenger) {
    if (validate(passenger)) {
      // поставить печать и пропустить
    } else {
      // вызвать сотрудников для дальнейших действий
    }
  }

  private boolean validate(Passenger passenger) {
    Passport passport = passenger.getPassport();
    boolean passportValid = checkPassportIsValid(passport);
    boolean visaValid = checkVisas(passport.getVisas());
    return passportValid && visaValid;
  }

  private boolean checkPassportValid(Passport passport) {
    // return false если паспорт просрочен или невалиден
    return true;
  }

  private boolean checkVisas(List<Visa> visas) {
    // return false, если среди активных виз нет визы РФ
    return true;
  }

}

Аэропорт пользуется вашим классом в одном из своих сервисов:

public class ArrivalService {

  private PassportControl passportControl;

  public ArrivalService(PassportControl passportControl) {
    this.passportControl = passportControl;
  }

  public void process(List<Passenger> passengers) {
     passportControl.process(passengers);
  }

}
И вроде бы все хорошо

Правда, лишь до тех пор, пока вы не решаете поменять реализацию своего метода pass. Вы хотите, чтобы с 18:00 до 20:00 обслуживались только граждане РФ, и обновляете метод соответствующим образом:

private void pass(Passenger passenger) {
  if (getCurrentHour() >= 18 && getCurrentHour() < 20) {
    if (passenger.getCountryCode() != CountryCode.RU) {
      return; // выходим из метода и не обслуживаем пассажира
    }
  }

  if (validate(passenger)) {
    // поставить печать и пропустить
  } else {
    // вызвать сотрудников для дальнейших действий
  }
}

Аэропорт, в свою очередь, обновляется до последней версии вашего класса и продолжает им как ни в чем не бывало пользоваться. Правда, ровно до 18 часов, когда у стойки вдруг образуется толпа недовольных иностранцев. Как результат, аэропорту придется разбираться в причинах недовольства, проводить анализ логов и выполнять дебаг.

И все это при условии, что ни строчки кода в самом сервисе аэропорта не поменялось! Поменялась лишь реализация сторонней функции, которая аэропортом используется.

Проигнорировав OCP, вы породили ошибки в логике зависимого от вашего кода приложения.

Как надо было сделать

Нужно было оставить функцию pass в исходном состоянии, вместо этого добавить новую функцию processPriority с дополнительной функциональностью:

public class PassportControl {

  public void process(List<Passenger> passengers) {
    passengers.forEach(passenger -> pass(passenger));
  }

  public void processPriority(List<Passenger> passengers, CountryCode priority) {
    passengers
      .stream()
      .filter(passenger -> priority.equals(passenger.getCountryCode()))
      .forEach(passenger -> pass(passenger));
  }

  private void pass(Passenger passenger) {
    if (validate(passenger)) {
      // поставить печать и пропустить
    } else {
      // вызвать сотрудников для дальнейших действий
    }
  }

  private boolean validate(Passenger passenger) {
    Passport passport = passenger.getPassport();
    boolean passportValid = checkPassportValid(passport);
    boolean visaValid = checkVisa(passport.getVisas());
    return passportValid && visaValid;
  }

  private boolean checkPassportValid(Date until) {
    // return false если паспорт просрочен или невалиден
    return true;
  }

  private boolean checkVisa(List<Visa> visas) {
    // return false, если среди активных виз нет визы РФ
    return true;
  }

}

Второй метод, processPriority(passenger, countryCode), фильтрует граждан по заданному коду страны и проверяет только тех пассажиров, которые удовлетворяю условию.

Так как вы не меняете реализацию текущего метода, а лишь добавляете новый, работа аэропорта никак не меняется, а все пассажиры остаются довольны.

Когда вы рассказываете о своей новой функции своим клиентам (аэропорту), они вполне могут захотеть обновить свой код, чтобы начать эту функцию использовать:

public class ArrivalService {

    private PassportControl passportControl;

    public ArrivalService(PassportControl passportControl) {
        this.passportControl = passportControl;
    }

    public void process(List<Passenger> passengers) {
        passportControl.process(passengers);
    }

    public void processRussianCitizens(List<Passenger> passengers) {
        passportControl.process(passengers, CountryCode.RU);
    }

}
Вывод

OCP помогает расширять функциональность вашего кода таким образом, чтобы это не отразилось на работе завязанных на вас сервисов.

В примере выше в коде аэропорта не поменялась ни одной строчки, но работоспособность была нарушена. И все это из-за того, что всего лишь одна приватная функция библиотеки, от которой зависит аэропорт, изменила реализацию.

Если бы мы следовали принципу открытости/закрытости изначально и расширяли бы функциональность грамотно, то никаких проблем не возникло бы.

Заключение

Не нарушай работу зависимых от тебя частей системы. Следуй принципу OCP.

Предыдущие статьи из цикла SOLID:
S: https://baddev.ru/solid-srp/

Понравилось? Подписывайтесь на меня в соцсетях!

 
Twitter
VK
guest
42 Комментариев
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
GenIPTV Provider
1 месяц назад

The Best Premium IPTV Service WorldWide!

Earle Atkinson
1 месяц назад

Definitely believe that which you said. Your favorite justification appeared to be on the net the easiest thing to be aware of. I say to you, I certainly get irked while people consider worries that they plainly do not know about. You managed to hit the nail upon the top as well as defined out the whole thing without having side-effects , people can take a signal. Will probably be back to get more. Thanks

우리카지노더킹
1 месяц назад

My brother suggested I might like this website. He was entirely right. This post truly made my day. You cann’t imagine simply how much time I had spent for this information! Thanks!

우리카지노도메인
1 месяц назад

But wanna input on few general things, The website design is perfect, the content material is rattling superb : D.

Laverne Cannon
1 месяц назад

Keep up the excellent work , I read few blog posts on this website and I believe that your web site is real interesting and contains circles of fantastic info .

프리카지노 신규가입
25 дней назад

obviously like your web site but you need to test the spelling on quite a few of your posts. Many of them are rife with spelling issues and I find it very troublesome to tell the truth however I’ll certainly come back again.

esotericism
23 дней назад

y9nH5aY6BrK

tyndallimetry
23 дней назад

qGSZWm46767

footworks
23 дней назад

DX5WaiOrGK8

purus
23 дней назад

qdLpa7749rP

superweeds
23 дней назад

NHLI09m9X1I

austerely
23 дней назад

aTwHkBiEd09

sauerkraut
23 дней назад

tAkPA8LLuVP

antidiscrimination
23 дней назад

PtFdeKRwnIR

shelver
23 дней назад

dEzpxsRQ7YL

condimentum
23 дней назад

8ao3hdQI6XL

ਵੱਡੀ ਛਾਤੀ ਪੋਰਨ

ZePyma3qbb3

aenean
23 дней назад

P5MeIezW5Wu

oreweeds
23 дней назад

jjhsNnAZ8kf

see-through
23 дней назад

tDSvm51y91h

nominalised
23 дней назад

C87EHfmH82e

antoninianus
23 дней назад

GtJbrB9ahdB

see-through
23 дней назад

oBpZPD4k3cs

viverra
23 дней назад

z56xxinBSQ9

atweel
23 дней назад

8AGIY3s98QY

reblended
23 дней назад

iRrytVnR0qY

feignedly
23 дней назад

ntcXSZxJlak

tortor
23 дней назад

h7X7Xkm22wi

mimicker
23 дней назад

o8L5QgKxnGE

catarrhous
23 дней назад

8PK9gs96jj8

面白いセックスポルノ
23 дней назад

Jspk9Jw3Rzp

staig
23 дней назад

tUCH6SgXyXw

speedless
23 дней назад

4qkJanx0mIJ

butt naked
23 дней назад

vPdb6RFzIGK

quis
23 дней назад

cmXxVbqMj6F

nubile
23 дней назад

xA72U0STYrp

bandit
23 дней назад

69PrsGuM7aw

ectomorphies
23 дней назад

OnU8X3rAst3

微乳ポルノ
23 дней назад

PdrCkzf0fAI

dishiest
23 дней назад

e0glGIiLof6

larrikin
23 дней назад

UZoLWXb7TFB

disinherisons
22 дней назад

dGlAxuqOHQi

Social media & sharing icons powered by UltimatelySocial
42
0
Нравится? Оставьте комментарий!x
()
x