Чланци

Отворено / затворено, према СОЛИД принципу

Софтверски ентитети (класе, модули, функције итд.) Требају бити отворени за проширење, али затворени за уређивање.

Дизајнирање софтвера: модула, класа и функција на такав начин да када је потребна нова функционалност, не бисмо требали модификовати постојећи код, већ написати нови код који ће користити постојећи код. Ово може изгледати чудно, посебно код језика попут Јава, Ц, Ц ++ или Ц #, где се односи не само на сам изворни код, већ и на бинарни. Желимо да креирамо нове функције на начине који не захтевају прерасподелу постојећих бинарних датотека, извршних датотека или ДЛЛ-ова.
ОЦП у ЧВРСТОМ контексту

 

Допунски СРП и ОЦП

Већ смо видели СРП принцип јединствене одговорности који каже да модул треба да има само један разлог за промену. ОЦП и СРП принципи се допуњују. Кодекс дизајниран према СРП принципу такође ће поштовати ОЦП принципе. Када имамо код који има само један разлог за промену, увођење нове функције створиће секундарни разлог за ту промену. Дакле, и СРП и ОЦП би били прекршени. Исто тако, ако имамо код који би требало да се промени само када се његова главна функција промени и требало би да остане непромењен када се дода нова функционалност, поштујући тако ОЦП, он ће углавном поштовати и СРП.
То не значи да СРП увек доводи до ОЦП-а или обрнуто, али у већини случајева ако се један од њих придржава, постизање другог је прилично једноставно.

 

Пример кршења принципа ОЦП

Са чисто техничке тачке гледишта, отворени / затворени принцип је врло једноставан. Једноставна веза две класе, попут оне доле, крши ОЦП принцип.

Класа Усер директно користи класу Логиц. Ако треба да имплементирамо другу класу Логиц на начин који нам омогућава да користимо и тренутну и нову, постојећа класа Логиц мораће бити промењена. Корисник је директно везан за имплементацију логике, не постоји начин да пружимо нову логику без утицаја на тренутну. А када говоримо о статички откуцаним језицима, и класа Усер ће вероватно захтевати промене. Ако говоримо о компајлираним језицима, сигурно ће и извршна датотека корисника и логичка извршна датотека или динамичка библиотека захтевати рекомпилацију и испоруку, пожељно је избегавати када је то могуће.

Позивајући се на претходну шему, можемо закључити да било која класа која директно користи другу класу може довести до кршења принципа отворено / затворено. 
Претпоставимо да желимо да напишемо класу која ће кроз нашу апликацију моћи да пружи напредак „у процентима“ преузете датотеке. Имаћемо две главне класе, Напредак и Датотеку, и претпостављам да бисмо их желели користити на следећи начин:

 

фунцтион тестИтЦанГетТхеПрогрессОфАФилеАсАПерцент () {
     $ датотека = нова датотека ();
     $ датотека-> дужина = 200;
     $ датотека-> послано = 100;
     $ прогресс = нови напредак ($ датотека);
     $ тхис-> ассертЕкуалс (50, $ прогресс-> гетАсПерцент ());
}

У овом коду смо корисници Прогреса. Желимо да добијемо вредност у процентима, без обзира на стварну величину датотеке. Датотеку користимо као извор информација. Датотека има дужину у бајтовима и поље названо послано које представља количину података послатих преузимачу. Не занима нас како ће се ове вредности ажурирати у апликацији. Можемо претпоставити да постоји нека магична логика која то ради за нас, па их у тесту можемо изричито поставити.

 

цласс Филе {
     дужина јавног долара;
     јавни $ послан;
}

 

Класа Филе је само једноставан објект података који садржи два поља. Наравно, требало би да садржи и друге информације и понашања, као што су име датотеке, путања, релативна путања, тренутни директоријум, тип, дозволе итд.

 

цласс Прогресс {

     приватна $ датотека;

     функција __цонструцт (Датотека $ датотека) {
          $ ово-> датотека = $ датотека;
     }

     функција гетАсПерцент () {
          ретурн $ тхис-> филе-> сент * 100 / $ тхис-> филе-> ленгтх;
     }

}

Напредак је једноставно класа која прихвата датотеку у свом конструктору. Ради јасноће, одредили смо тип променљиве у параметрима конструктора. На Прогресу постоји једна корисна метода, гетАсПерцент (), која ће послати вредности и дужину преузети из датотеке и претворити их у проценат. Једноставно и делује.

Чини се да је овај код тачан, али крши принцип Отворено / Затворено.

Али зашто?

И како?

 

Покушајмо да променимо захтеве

Свака апликација која ће се временом развијати требаће нове функције. Нова функција наше апликације могла би да буде омогућавање струјања музике уместо пуког преузимања датотека. Дужина датотеке је представљена у бајтовима, трајање музике у секундама. Желимо да понудимо траку напретка нашим слушаоцима, али можемо ли да поново користимо горе написану наставу?

Не не можемо. Наш напредак је везан за Филе. Може да управља само подацима о датотекама, мада се може применити и на музички садржај. Али да бисмо то урадили морамо да га изменимо, морамо да учинимо да Прогресс зна музику и датотеке. Да је наш дизајн у складу са ОЦП-ом, не бисмо требали да додирујемо Филе или Прогресс. Могли бисмо само поново искористити постојећи напредак и применити га на музици.

 

Иновациони билтен
Не пропустите најважније вести о иновацијама. Пријавите се да их примате путем е-поште.

Могуће решење

Динамички куцани језици имају предност у руковању типовима објеката током извођења. То нам омогућава да уклонимо наговештај типа из конструктора Прогресс и код ће наставити да ради.

цласс Прогресс {

     приватна $ датотека;

     функција __цонструцт ($ датотека) {
         $ ово-> датотека = $ датотека;
     }

    функција гетАсПерцент () {
         ретурн $ тхис-> филе-> сент * 100 / $ тхис-> филе-> ленгтх;
     }

}

Сада у Прогрессу можемо покренути било шта. И под било чим мислим буквално на све:

цласс Мусиц {

дужина јавног долара;
јавни $ послан;

јавни $ уметник;
јавни $ албум;
јавни $ релеасеДате;

функција гетАлбумЦоверФиле () {
врати 'Слике / Омоти /'. $ тхис-> уметник. '/'. $ тхис-> албум. '.пнг';
}
}

А часови музике попут овог горе ће савршено функционисати. То можемо лако тестирати тестом врло сличним Филе-у.
фунцтион тестИтЦанГетТхеПрогрессОфАМусицСтреамАсАПерцент () {
$ мусиц = нова музика ();
$ музика-> дужина = 200;
$ музика-> послано = 100;

$ прогресс = нови напредак ($ музика);

$ тхис-> ассертЕкуалс (50, $ прогресс-> гетАсПерцент ());
}

Дакле, у основи било који мерљиви садржај може да се користи са класом Прогресс. Можда бисмо га требали ставити у код тако што ћемо променити и име променљиве:

цласс Прогресс {

приватни $ мерљиви садржај;

функција __цонструцт ($ мерљивиЦонтент) {
$ тхис-> мјерљиви садржај = $ мерљиви садржај;
}

функција гетАсПерцент () {
ретурн $ тхис-> мјерљиви садржај-> послан * 100 / $ овај-> мерљиви садржај-> дужина;
}

}

Када смо одредили Филе као наговештај, били смо оптимистични у погледу онога што наша класа може да поднесе. Било је изричито и ако је ишта друго дошло, велика грешка нам је рекао.

Uна класа која надјачава метод основне класе тако да изведена класа не поштује уговор основне класе. 

Не желимо да на крају покушамо да позовемо методе или приступимо пољима на објектима који нису у складу са нашим уговором. Када смо имали наговештај, уговор је њиме прецизиран. Поља и методе класе Филе. Сад кад немамо ништа, можемо послати било шта, чак и низ и то би резултирало лошом грешком.

Иако је крајњи резултат исти у оба случаја, што значи да се код ломи, први је произвео лепу поруку. Ово је, међутим, веома мрачно. Не постоји начин да се зна шта је променљива – стринг у нашем случају – и која својства су тражена, а нису пронађена. Тешко је отклонити грешке и решити проблем. Програмер мора да отвори класу Прогресс, да је прочита и разуме. Уговор, у овом случају, када не наведете експлицитно типски наговештај, јесте defiзавршено понашањем Прогреса. То је подразумевани уговор познат само Прогресу. У нашем примеру јесте defiзавршено приступом два поља, послато и дужина, у методи гетАсПерцент(). У стварном животу подразумевани уговор може бити веома сложен и тешко га је открити само тражењем неколико секунди на часу.

Ово решење се препоручује само ако се ниједан од следећих савета не може лако применити или ако би нанели озбиљне архитектонске промене које не оправдавају напор.

Ercole Palmeri

Иновациони билтен
Не пропустите најважније вести о иновацијама. Пријавите се да их примате путем е-поште.

Недавни чланци

Британски антимонополски регулатор подигао је БигТецх аларм због ГенАИ

УК ЦМА је издао упозорење о понашању Биг Тецх-а на тржишту вештачке интелигенције. Тамо…

КСНУМКС април КСНУМКС

Цаса Греен: енергетска револуција за одрживу будућност у Италији

Уредба „Цасе Греен“, коју је формулисала Европска унија за побољшање енергетске ефикасности зграда, завршила је свој законодавни процес са…

КСНУМКС април КСНУМКС

Е-трговина у Италији на +27% према новом извештају Цасалеггио Ассоциати

Представљен годишњи извештај Цасалеггио Ассоциати-а о е-трговини у Италији. Извештај под насловом „АИ-Цоммерце: границе е-трговине са вештачком интелигенцијом“.…

КСНУМКС април КСНУМКС

Сјајна идеја: Бандалук представља Аирпуре®, завесу која прочишћава ваздух

Резултат сталних технолошких иновација и посвећености животној средини и добробити људи. Бандалук представља Аирпуре®, шатор…

КСНУМКС април КСНУМКС

Прочитајте Иновације на свом језику

Иновациони билтен
Не пропустите најважније вести о иновацијама. Пријавите се да их примате путем е-поште.

Пратите нас