Дизајн на софтвер: модули, класи и функции на таков начин што кога е потребна нова функционалност, не треба да го менуваме постојниот код, туку да напишеме нов код што ќе го користи постојниот код. Ова може да звучи чудно, особено со јазици како Java, C, C ++ или C # каде што се однесува не само на самиот изворен код, туку и на бинарниот. Ние сакаме да создадеме нови одлики на начини што не бараат прераспределба на постојните бинарни, извршни или DLL.
OCP во СОЛИДЕН контекст
Ние веќе го видовме принципот СРП за единствена одговорност, кој вели дека модулот треба да има само една причина да се менува. Принципите на ОЦП и СРП се комплементарни. Кодот дизајниран следејќи го принципот SRP исто така ќе ги почитува принципите на OCP. Кога имаме код што има само една причина да се промени, воведувањето нова опција ќе создаде второстепена причина за таа промена. Така и СРП и ОЦП ќе бидат прекршени. Исто така, ако имаме код кој треба да се менува само кога неговата главна функција се менува и треба да остане непроменет кога ќе се додаде нова функционалност, со тоа почитувајќи го OCP, тој најмногу ќе го почитува и SRP.
Ова не значи дека SRP секогаш доведува до OCP или обратно, но во повеќето случаи ако се почитува еден од нив, постигнувањето на второто е прилично едноставно.
Од чисто техничка гледна точка, принципот Отворено / затворено е многу едноставен. Едноставна врска помеѓу две класи, како онаа подолу, го нарушува принципот OCP.
Корисничката класа директно ја користи класата Logic. Ако треба да имплементираме втора класа Logic на начин што ќе ни овозможи да ги користиме и тековната и новата, постојната класа Logic ќе треба да се смени. Корисникот е директно врзан за имплементација на логиката, не постои начин да обезбедиме нова логика без да влијаеме на сегашната. И кога зборуваме за статички внесени јазици, многу е веројатно дека Корисничката класа ќе бара и модификации. Ако зборуваме за компајлирани јазици, сигурно дека и Извршливиот корисник и Извршната логика или динамичката библиотека ќе бараат повторна компилација и испорака, по можност да се избегнат кога е можно.
Повикувајќи се на претходната шема, можеме да заклучиме дека секоја класа што директно користи друга класа, може да доведе до кршење на принципот Отворено / затворено.
Да претпоставиме дека сакаме да напишеме класа што може да обезбеди напредок "во проценти" на преземената датотека, преку нашата апликација. Haveе имаме две главни класи, Напредок и Датотека, и претпоставувам дека би сакале да ги користиме на следниов начин:
тест на функцијатаItCanGetTheProgressOfAFileAsAPercent () {
$ датотека = нова датотека ();
$ датотека-> должина = 200;
$ датотека-> испратена = 100;
$ напредок = нов напредок (датотека $);
$ this-> assertEquals (50, $ progress-> getAsPercent ());
}
Во овој код ние сме корисници на Прогрес. Ние сакаме да добиеме вредност како процент, без оглед на реалната големина на датотеката. Ние ја користиме Датотеката како извор на информации. Датотеката има должина во бајти и поле наречено испратено што претставува количина на податоци испратени до симнувачот. Не ни е гајле како овие вредности се ажурираат во апликацијата. Можеме да претпоставиме дека постои некоја волшебна логика што го прави ова за нас, така што на тест можеме експлицитно да ги поставиме.
класа Датотека {
јавна должина $;
јавно испратено $;
}
Класата File е само едноставен податочен објект што ги содржи двете полиња. Секако дека треба да содржи и други информации и однесувања, како што се име на датотека, патека, релативна патека, тековен директориум, тип, дозволи и сл.
класа Напредок {
приватна датотека $;
функција __construction (Датотека $ датотека) {
$ ова-> датотека = $ датотека;
}
функција getAsPercent () {
вратете $ ова-> датотека-> испратено * 100 / $ ова-> датотека-> должина;
}
}
Прогресот е едноставно класа што прифаќа датотека во нејзиниот конструктор. За јасност, ние го наведовме типот на променлива во параметрите на конструкторот. Постои единствен корисен метод за Progress, getAsPercent (), кој ќе ги земе испратените вредности и должина од File и ќе ги претвори во процент. Едноставно и работи.
Овој код се чини дека е точен, сепак го крши принципот Отворено / затворено.
Но зошто?
И како?
Секоја апликација ќе има потреба од нови карактеристики за да се развива со текот на времето. Нова опција за нашата апликација може да биде да дозволиме стриминг на музика наместо само преземање датотеки. Должината на датотеката е претставена во бајти, времетраењето на музиката во секунди. Ние сакаме да им понудиме лента за напредок на нашите слушатели, но дали можеме повторно да го користиме часот напишан погоре?
Не не можеме. Нашата прогресија е обврзана датотека. Може да управува само со информации за датотеки, иако може да се примени и на музичка содржина. Но, за да го сториме тоа, мора да го модифицираме, мораме да го натераме Напредокот да ја знае музиката и датотеките. Ако нашиот дизајн беше во согласност со OCP, не треба да допираме Датотека или Прогрес. Можевме само да го искористиме постојниот напредок и да го примениме на музиката.
Динамички внесените јазици имаат предност во управувањето со типовите на предмети при извршувањето. Ова ни овозможува да го отстраниме пишувањето од конструкторот Progress и кодот ќе продолжи да работи.
класа Напредок {
приватна датотека $;
функција __construction ($ датотека) {
$ ова-> датотека = $ датотека;
}
функција getAsPercent () {
вратете $ ова-> датотека-> испратено * 100 / $ ова-> датотека-> должина;
}
}
Сега можеме да лансираме сè на Прогрес. И под ништо, мислам буквално на што било:
класа Музика {
јавна должина $;
јавно испратено $;
јавен $ уметник;
јавен албум $;
јавно $ releaseDate;
функција getAlbumCoverFile () {
вратете ги 'Слики / обвивки /'. $ ова-> уметник. '/'. $ ова-> албум. '.png';
}
}
И часот по Музика како горенаведениот ќе работи совршено. Лесно можеме да го тестираме со тест многу сличен на File.
тест на функцијатаItCanGetTheProgressOfAMusicStreamAsAPercent () {
$ музика = нова музика ();
$ музика-> должина = 200;
$ музика-> испратена = 100;
$ напредок = нов напредок ($ музика);
$ this-> assertEquals (50, $ progress-> getAsPercent ());
}
Значи, во основа секоја мерлива содржина може да се користи со класата Progress. Можеби треба да го ставиме во код со промена на името на променливата исто така:
класа Напредок {
приватна $ мерлива содржина;
функција __конструирај ($ мерлива содржина) {
$ ова-> мерлива содржина = $ мерлива содржина;
}
функција getAsPercent () {
вратете $ ова-> мерлива содржина-> испратена * 100 / $ оваа-> мерлива содржина-> должина;
}
}
Кога ја наведовме File како типка, бевме оптимисти за тоа што може да се справи со нашата класа. Тоа беше експлицитно и ако дојдеше нешто друго, голема грешка ќе ни кажеше.
Una класа што надминува метод на основна класа, така што договорот за основна класа не е почитуван од изведената класа.
Ние не сакаме да се обидеме да повикаме методи или полиња за пристап на објекти кои не се во согласност со нашиот договор. Кога имавме пишување, договорот беше прецизиран од него. Полињата и методите од класата File. Сега, кога немаме ништо, можеме да испратиме што било, дури и низа и тоа ќе резултира со лоша грешка.
Додека крајниот резултат е ист во двата случаи, што значи дека кодот се распаѓа, првиот произведе убава порака. Ова, сепак, е многу темно. Не постои начин да се знае што е променливата - низа во нашиот случај - и кои својства се барани и не се пронајдени. Тешко е да се дебагира и да се поправи проблемот. Програмерот мора да ја отвори класата Progress, да ја прочита и разбере. Договорот, во овој случај, кога не го специфицирате експлицитно написот, е defiуништени од однесувањето на Прогресот. Тоа е имплицитен договор познат само на Прогрес. Во нашиот пример, тоа е defiсе завршува со пристап до двете полиња, испратени и должина, во методот getAsPercent(). Во реалниот живот имплицитниот договор може да биде многу сложен и тешко да се открие со само гледање неколку секунди на час.
Ова решение се препорачува само ако никој од другите совети подолу не може лесно да се спроведе или ако тие нанесат сериозни архитектонски промени што не го гарантираат трудот.
Ercole Palmeri
Google DeepMind воведува подобрена верзија на својот модел за вештачка интелигенција. Новиот подобрен модел обезбедува не само…
Ларавел, познат по својата елегантна синтакса и моќните карактеристики, исто така обезбедува цврста основа за модуларна архитектура. Таму…
Cisco и Splunk им помагаат на клиентите да го забрзаат своето патување до Центарот за безбедносни операции (SOC) на иднината со…
Ransomware доминира во вестите во последните две години. Повеќето луѓе се свесни дека нападите…
Операција на офталмопластика со помош на комерцијалниот прегледувач на Apple Vision Pro беше извршена во поликлиниката Катанија…
Развивањето на фини моторни вештини преку боење ги подготвува децата за посложени вештини како пишување. Да обои…
Поморскиот сектор е вистинска глобална економска сила, која навигираше кон пазар од 150 милијарди ...
Минатиот понеделник, Financial Times објави договор со OpenAI. ФТ го лиценцира своето новинарство од светска класа…