Зачем нужны абстракции и интерфейсы
Объектно-ориентированное программирование: на пальцах Что такое классы в объектно-ориентированном программировании Объясняем объекты Классы и функции ООП: атрибуты и методы
Зачем нужны абстракции и интерфейсы

Как в ста­ром анек­до­те: про объектно-ориентированное про­грам­ми­ро­ва­ние мож­но рас­ска­зать про­сто и непра­виль­но либо слож­но и непра­виль­но. Мы попро­бу­ем рас­ска­зать про оче­ред­ной аспект ООП просто. 

Зачем это: ООП — одна из глав­ных кон­цеп­ций совре­мен­ной раз­ра­бот­ки. Она при­ме­ни­ма не к каким-то кон­крет­ным язы­кам, это ско­рее спо­соб мыш­ле­ния в про­грам­ми­ро­ва­нии. Если вы пони­ма­е­те ООП, ваш код на любом язы­ке будет чище, чита­е­мее и эффективнее. 

В этой ста­тье раз­бе­рём два слож­ных поня­тия из объектно-ориентированного про­грам­ми­ро­ва­ния: абстрак­ции и интер­фей­сы. Это ещё одна сту­пень в пони­ма­нии непостижимого. 

Основные идеи из ООП

  • Объектно-ориентированное про­грам­ми­ро­ва­ние постро­е­но вокруг объ­ек­тов. Мож­но пред­ста­вить, что объ­ект — это короб­ка, в кото­рой лежат дан­ные и функции. 
  • Деле­ние на объ­ек­ты нуж­но для того, что­бы мож­но было созда­вать, обслу­жи­вать и пере­де­лы­вать части про­грам­мы, не влияя на дру­гие части и про­грам­му в целом. По-умному это назы­ва­ет­ся «для повы­ше­ния уров­ня абстракции». 
  • Смысл объ­ек­та в том, что он уме­ет делать какие-то свои дела, и осталь­ные объ­ек­ты не долж­ны знать, как он это делает.
  • У объ­ек­та есть поня­тие атри­бу­та и мето­да. Атри­бут — это любые дан­ные, кото­рые хра­нят­ся в «короб­ке» объ­ек­та. Мето­ды — это любые дей­ствия, кото­рые мож­но совер­шать над объ­ек­том или внут­ри его. Атри­бу­ты обыч­но выра­жа­ют­ся пере­мен­ны­ми, а мето­ды — функциями.
  • Объ­ек­ты необ­хо­ди­мы, напри­мер, в ком­пью­тер­ных играх, где каж­дая штуч­ка на игро­вом поле — это объ­ект с какими-то свой­ства­ми. Или в интернет-магазине, где один объ­ект отве­ча­ет за кор­зи­ну, дру­гой — за выклад­ку товара. 
  • Объ­ек­ты мож­но созда­вать по шаб­ло­ну — такие шаб­ло­ны назы­ва­ют­ся классами. 

Абстракция

Пред­ставь­те, что вы попро­си­ли несколь­ких чело­век опи­сать в общих чер­тах, что такое теле­фон и как им поль­зо­вать­ся: пусть это будут бабуш­ка, мама и подру­га. Бабуш­ка вспом­нит про дис­ко­вые теле­фо­ны и труб­ки с витым про­во­дом. Мама рас­ска­жет про радио­те­ле­фо­ны, у кото­рых есть база и есть труб­ка, с кото­рой мож­но ходить по всей квар­ти­ре, а подру­га нач­нёт опи­сы­вать мобильник.

Несмот­ря на то что рас­ска­зы будут силь­но отли­чать­ся меж­ду собой, у них будет несколь­ко общих момен­тов про телефон:

  • у теле­фо­на есть трубка;
  • в труб­ку мы гово­рим, из труб­ки — слушаем;
  • мож­но набрать номер нуж­но­го чело­ве­ка и позво­нить ему;
  • если вам позво­нят по теле­фо­ну, вы это услы­ши­те и при­ме­те звонок.

Полу­ча­ет­ся, что если пред­ста­вить абстракт­ный теле­фон, то полу­чит­ся такое устрой­ство с дина­ми­ком, мик­ро­фо­ном и сред­ством набо­ра номера. 

Это и есть абстрак­ция: когда мы опи­сы­ва­ем толь­ко самые суще­ствен­ные дета­ли, кото­рые важ­ны для зада­чи. В нашем слу­чае зада­ча такая — понять, что такое теле­фон и как им поль­зо­вать­ся. Поэто­му мик­ро­фон и дина­мик для этой зада­чи важен, а спо­соб свя­зи теле­фо­на с сетью — нет. Устрой­ство набо­ра номе­ра важ­но, а то, какая мело­дия игра­ет при вызо­ве — нет.

🔥 Абстрак­ция — это когда мы сосре­до­та­чи­ва­ем­ся толь­ко на суще­ствен­ных для зада­чи дета­лях и игно­ри­ру­ем всё осталь­ное. В ООП абстрак­ция озна­ча­ет, что для каж­до­го объ­ек­та мы зада­ём мини­маль­ное коли­че­ство мето­дов, полей и опи­са­ний, кото­рые поз­во­лят нам решить зада­чу. Чем мень­ше харак­те­ри­стик, тем луч­ше абстрак­ция, но клю­че­вые харак­те­ри­сти­ки уби­рать нельзя.

Что­бы рабо­тать с абстрак­ци­я­ми, исполь­зу­ют интерфейсы.

Интерфейс

Итак, у нас есть некое устрой­ство с труб­кой, мик­ро­фо­ном, дина­ми­ком и сред­ством набо­ра номе­ра. Но если вы вспом­ни­те рас­ска­зы мамы, бабуш­ки и подру­ги, то обна­ру­жи­те вот что:

  • в мик­ро­фон гово­рят, что­бы собе­сед­ник мог вас услышать;
  • что­бы слы­шать само­му, ухо при­кла­ды­ва­ют к динамику;
  • что­бы набрать номер, нуж­но с помо­щью номе­ро­на­би­ра­те­ля вызвать нуж­ную после­до­ва­тель­ность цифр;
  • когда идёт вызов, слыш­ны гуд­ки в динамике.

Всё это — интер­фей­сы. Они поз­во­ля­ют рабо­тать с объ­ек­том, не вни­кая в то, как он устро­ен внут­ри. Если вы уме­е­те рабо­тать с интер­фей­сом номе­ро­на­би­ра­те­ля, то вам всё рав­но, нуж­но ли кру­тить диск, нажи­мать физи­че­ские кноп­ки на радио­труб­ке или давить паль­цем на сен­сор­ный экран. 

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

Интер­фей­сы — это дей­ствия над объ­ек­том, доступ­ные дру­гим объ­ек­там (поэто­му они назы­ва­ют­ся публичными). 

Есть ещё инкап­су­ли­ро­ван­ные, то есть внут­рен­ние мето­ды. Напри­мер, у мик­ро­фо­на есть пуб­лич­ный метод «Слу­шать голос», и есть внут­рен­ний метод «Пре­об­ра­зо­вать голос в элек­три­че­ские сиг­на­лы». С его помо­щью он вза­и­мо­дей­ству­ет с дру­ги­ми частя­ми наше­го абстракт­но­го теле­фо­на. Про инкап­су­ля­цию будет отдель­ный мате­ри­ал, пото­му что тема большая.

Сложная терминология

Стро­го гово­ря, интер­фей­сы — это не дей­ствия, а мето­ды. Сей­час объясним. 

В про­грам­ми­ро­ва­нии есть опе­ра­ции — это про­стей­шие дей­ствия, напри­мер, ско­пи­ро­вать зна­че­ние из одной пере­мен­ной в другую. 

Из про­стых дей­ствий состав­ля­ют­ся функ­ции — это когда несколь­ко опе­ра­ций «скле­и­ва­ют­ся» в нечто еди­ное. Мы даём этой склей­ке назва­ние и полу­ча­ем функ­цию. Напри­мер, может быть функ­ция «про­ве­рить пра­виль­ность элек­трон­но­го адре­са», кото­рая состо­ит из несколь­ких десят­ков про­стых операций. 

На язы­ке ООП функ­ции, при­вя­зан­ные к объ­ек­там, назы­ва­ют­ся мето­да­ми. Про­сто такой тер­мин. По сути это функ­ции, то есть скле­ен­ные вме­сте операции. 

Ито­го: метод — это набор про­стых дей­ствий, кото­рые скле­и­ли в еди­ное целое и засу­ну­ли в объект. 

Для чего это всё

Допу­стим, вы рабо­та­е­те в коман­де над боль­шим про­дук­том. В таких слу­ча­ях удоб­но раз­де­лить одну боль­шую про­грам­му на мно­же­ство мел­ких под­про­грамм и сер­ви­сов, каж­дый из кото­рых реша­ет свою узкую задачу.

Если зара­нее не дого­во­рить­ся о том, как эти ком­по­нен­ты обме­ни­ва­ют­ся дан­ны­ми меж­ду собой, то может слу­чить­ся то, о чём мы уже пре­ду­пре­жда­ли:

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

Что­бы тако­го не было, посту­па­ют так:

  • Один про­грам­мист отве­ча­ет за реги­стра­цию новых пользователей.
  • Он дела­ет объ­ект «Поль­зо­ва­тель» и про­пи­сы­ва­ет ему интер­фейс «Заре­ги­стри­ро­вать».
  • Так­же он гово­рит, что интер­фейс «Заре­ги­стри­ро­вать» будет при­ни­мать на вход три пере­мен­ные: имя, почту и пароль.
  • Теперь про­грам­мист гово­рит осталь­ным: «Ребя­та, если нуж­но заре­ги­стри­ро­вать ново­го поль­зо­ва­те­ля — исполь­зуй­те интер­фейс Пользователь.Зарегистрировать(имя,почта,пароль)».
  • Даже если внут­ри объ­ек­та поме­ня­ет­ся вся логи­ка рабо­ты, то это никак не повли­я­ет на интер­фейс — все так же смо­гут им пользоваться.
  • А всё пото­му, что нико­му нет дела, как тот про­грам­мист реа­ли­зо­вал свою часть, если через интер­фейс всё рабо­та­ет нор­маль­но. Неваж­но, как он шиф­ру­ет пароль, где хра­нит почту, как обра­ба­ты­ва­ет имя и в какую базу дан­ных всё это скла­ды­ва­ет. Если интер­фейс рабо­та­ет, все осталь­ные могут про­дол­жать разработку.

Текст:
Миха­ил Полянин

Редак­тор:
Мак­сим Ильяхов

Худож­ник:
Даня Бер­ков­ский

Кор­рек­тор:
Ири­на Михеева

Вёрст­ка:
Мария Дро­но­ва

Соц­се­ти:
Олег Веш­кур­цев
Даже опыт­ные про­грам­ми­сты не все­гда пони­ма­ют ООП. А вы — пой­мё­те. При­хо­ди­те в Прак­ти­кум, что­бы посте­пен­но стать опыт­ным программистом.