Объектке багытталган программалоонун эң маанилүү принциптеринин бири ички жана тышкы интерфейстерди бөлүү болуп саналат.
Бул "салам дүйнөгө" караганда татаал нерсени иштеп чыгууда милдеттүү практика.
Бул принципти түшүнүү үчүн программалоону бир азга унутуп, реалдуу дүйнөнү карайлы.
Биз колдонгон аппараттар, адатта, абдан татаал. Бирок ички жана тышкы интерфейстерди бөлүү бизге аларды эч кандай көйгөйсүз колдонууга мүмкүндүк берет.
Чыныгы жашоодон мисал
Мисалы, кофе кайнаткыч. Сыртынан караганда жөнөкөй: баскыч, экран, бир нече тешик... Анан, албетте, натыйжада сонун кофе! :)
Бирок ичинде ... (оңдоо боюнча колдонмодогу сүрөт)
Көптөгөн деталдар. Бирок биз аны эч нерсе билбей туруп колдоно алабыз.
Кофе кайнаткычтар абдан ишенимдүү, туурабы? Биз аларды жылдар бою колдоно алабыз, эгер бир нерсе туура эмес болуп калса, биз аларды оңдоого алабыз.
Кофе кайнатуучунун ишенимдүүлүгүнүн жана жөнөкөйлүгүнүн сыры бардык майда-чүйдөсүнө чейин жакшы чечилип, ичинде жашырылган .
Кофе кайнаткычтын коргоочу капкагын алып салсак, анда аны колдонуу бир топ кыйын (кайда басуу керек?) жана коркунучтуураак (электр шокуна алып келиши мүмкүн).
Көрүнүп тургандай, программалоодо объекттер кофе кайнаткычтарга окшош.
Ал эми ички майда-чүйдөсүнө чейин жашыруу үчүн, биз коргоочу жабууну эмес, тил жана конвенциянын атайын синтаксисин колдонобуз.
Ички жана тышкы интерфейстер
Объектке багытталган программалоодо касиеттер жана методдор 2 топко бөлүнөт:
- Ички интерфейс - класстын башка методдорунан жеткиликтүү болгон, бирок класстан тышкаркы эмес методдор жана касиеттер.
- Тышкы интерфейс - класстан тышкары жеткиликтүү болгон ыкмалар жана касиеттер.
Кофе кайнаткыч менен окшоштукту уланта турган болсок, анын ичинде эмне катылган: казан түтүгү, жылытуу элементи ж.б. ички интерфейс болуп саналат.
Объекттин иштеши үчүн ички интерфейс колдонулат, анын бөлүктөрү бири-бирин колдонушат. Мисалы, жылытуу элементине казан түтүгү орнотулган.
Бирок сыртынан кофе кайнаткыч коргоочу кабык менен капталган, ошондуктан эч ким кыйын жерлерге жете албайт. Чоо-жайы жашыруун жана жеткиликтүү эмес. Биз алардын функцияларын тышкы интерфейс аркылуу колдоно алабыз.
Ошентип, объектти колдонуу үчүн анын тышкы интерфейсин билүү керек. Биз анын ички иштээрин такыр билбей калышыбыз мүмкүн, бул эң сонун.
Бул жалпы киришүү болчу.
JavaScript'те объекттин талааларынын эки түрү (касиеттери жана ыкмалары) бар:
- Коомдук: каалаган жерден жеткиликтүү. Алар алдыңкы бөлүктөрдү түзөт. Ушул убакка чейин биз жалаң гана коомдук касиеттерди жана ыкмаларды колдонуп келдик.
- Жеке: класстын ичинде гана жеткиликтүү. Алар ички интерфейс үчүн.
Көптөгөн башка тилдерде да класстын ичинде же бала класстар үчүн гана жеткиликтүү болгон "корголгон" талаалар бар (башкача айтканда, купуя катары, бирок класстарды мурастоо үчүн кирүү уруксаты бар) жана ички интерфейстер үчүн да пайдалуу. Кандайдыр бир мааниде, алар жеке класстарга караганда кеңири таралган, анткени биз адатта тукум кууган класстардын ички талааларга кирүү мүмкүнчүлүгүн каалайбыз.
Корголгон талаалар JavaScriptде тил деңгээлинде ишке ашырылбайт, бирок иш жүзүндө алар абдан ыңгайлуу, ошондуктан алар эмуляцияланат.
Эми ушул сапаттардын бардык түрлөрү менен JavaScript кофе кайнаткыч жасайлы. Кофе кайнаткычтын көптөгөн бөлүктөрү бар, биз аларды мисалдын жөнөкөйлүгү үчүн үлгү кылбайбыз (бирок биз мүмкүн болсо да).
"waterAmount" корголгон мүлк
Келгиле, адегенде кофе кайнаткычты сүрөттөө үчүн жөнөкөй класс түзөлү:
@A@class CoffeeMachine {
waterAmount = 0; // количество воды внутри
constructor(power) {
this.power = power;
alert( `Создана кофеварка, мощность: ${power}` );
}
}
// создаём кофеварку
let coffeeMachine = new CoffeeMachine(100);
// добавляем воды
coffeeMachine.waterAmount = 200;@A@
Азыр мүлк waterAmount
жана power
коомдук. Биз аларды оңой эле алып, сырттан каалаган баага коё алабыз.
Келгиле, мүлктү waterAmount
корголгон деп өзгөртөлү, биз аны көбүрөөк көзөмөлдөйбүз. Мисалы, биз аны нөлдөн төмөн коюуну эч ким каалабайбыз.
Корголгон касиеттер адатта префикс менен башталат _
.
Бул тил синтаксиси эмес: программисттер арасында мындай касиеттерге жана методдорго сырттан таасир этпеши керек деген белгилүү конвенция бар. Көпчүлүк программисттер бул конвенцияны карманышат.
Ошентип, биздин мүлк деп аталат _waterAmount
:
@A@class CoffeeMachine {
_waterAmount = 0;
set waterAmount(value) {
if (value < 0) throw new Error("Отрицательное количество воды");
this._waterAmount = value;
}
get waterAmount() {
return this._waterAmount;
}
constructor(power) {
this._power = power;
}
}
// создаём новую кофеварку
let coffeeMachine = new CoffeeMachine(100);
// устанавливаем количество воды
coffeeMachine.waterAmount = -10; // Error: Отрицательное количество воды@A@
Азыр кирүү көзөмөлгө алынгандыктан нөлдөн төмөн сууну көрсөтүү мүмкүн болгон жок.
Окуу үчүн гана касиет "күч"
Келгиле, мүлктү power
окуу үчүн гана кылалы. Кээде сиз объект түзүлгөндө гана касиеттин коюлушун каалайсыз жана андан кийин эч качан өзгөрбөйт.
Кофе кайнатуучуга дал ушул нерсе керек: кубаттуулугу эч качан өзгөрбөйт.
Бул үчүн, биз жөн гана жөндөөчү эмес, алуучу түзүшүбүз керек:
@A@class CoffeeMachine {
// ...
constructor(power) {
this._power = power;
}
get power() {
return this._power;
}
}
// создаём кофеварку
let coffeeMachine = new CoffeeMachine(100);
alert(`Мощность: ${coffeeMachine.power}W`); // Мощность: 100W
coffeeMachine.power = 25; // Error (no setter)@A@
Бул жерде биз getter/setter синтаксисин колдондук.
Бирок көпчүлүк учурларда функцияларды колдонуу get.../set...
артык:
@A@class CoffeeMachine {
_waterAmount = 0;
setWaterAmount(value) {
if (value < 0) throw new Error("Отрицательное количество воды");
this._waterAmount = value;
}
getWaterAmount() {
return this._waterAmount;
}
}
@
new CoffeeMachine().setWaterAmount(100);@A@
Бул бир аз узунураак көрүнөт, бирок функциялары ийкемдүү. Алар бир нече аргументти (азыр бизге кереги жок болсо да) ала алышат. Демек, келечек үчүн, эгерде биз бир нерсени рефакциялашыбыз керек болсо, функциялар коопсуз тандоо.
Башка жагынан алганда, алуу/коюу синтаксиси кыскараак, сизге жараша.
Эгерде бизден мурас алсак , жаңы класстын ыкмаларына жетүүбүзгө class MegaMachine extends CoffeeMachine
эч нерсе тоскоол болбойт .this._waterAmount
this._power
Ошентип, корголуучу талаалар, албетте, мурас болуп саналат. Жеке талаалардан айырмаланып, биз төмөндө көрөбүз.
Жеке менчик "#waterLimit"
JavaScript тилинде стандартка дээрлик кошулган инновация бар: ал жеке касиеттерге жана ыкмаларга колдоо көрсөтөт.
Жеке касиеттери жана ыкмалары менен башталышы керек #
. Алар класстын ичинде гана жеткиликтүү.
Мисалы, төмөндөгү класстын жеке менчиги жана суунун көлөмүн текшерүүнүн #waterLimit
жеке ыкмасы бар:#checkWater
@A@class CoffeeMachine {
#waterLimit = 200;
#checkWater(value) {
if (value < 0) throw new Error("Отрицательный уровень воды");
if (value > this.#waterLimit) throw new Error("Слишком много воды");
}
}
let coffeeMachine = new CoffeeMachine();
// снаружи нет доступа к приватным методам класса
coffeeMachine.#checkWater(); // Error
coffeeMachine.#waterLimit = 1000; // Error@A@
Тил деңгээлинде #
бул талаа купуя экенин билдирген өзгөчө белги. Биз ага сырттан же тукум кууган класстардан кире албайбыз.
Жеке талаалар коомдук талааларга карама-каршы келбейт. Биз бир эле учурда эки талаага ээ боло алабыз - жеке #waterAmount
жана мамлекеттик waterAmount
.
waterAmount
Мисалы, төмөнкү үчүн аксессуар жасайлы #waterAmount
:
@A@class CoffeeMachine {
#waterAmount = 0;
get waterAmount() {
return this.#waterAmount;
}
set waterAmount(value) {
if (value < 0) throw new Error("Отрицательный уровень воды");
this.#waterAmount = value;
}
}
let machine = new CoffeeMachine();
machine.waterAmount = 100;
alert(machine.#waterAmount); // Error@A@
Корголгон талаалардан айырмаланып, жеке талаалардын функционалдуулугу тилдин өзү тарабынан камсыз кылынат. Бул Жакшы.
Бирок, эгерде биз мураска алсак CoffeeMachine
, анда биз түз кире албайбыз #waterAmount
. Биз getter/setterге таянууга аргасыз болобуз waterAmount
:
@A@class MegaCoffeeMachine extends CoffeeMachine {
method() {
alert( this.#waterAmount ); // Error: can only access from CoffeeMachine
}
}@A@
Көп учурларда, бул чектөө өтө катуу болуп саналат. Биз кеңейтип жаткандыктан CoffeeMachine
, бизде ички ыкмаларга жана касиеттерге жетүү үчүн мыйзамдуу себеп болушу мүмкүн. Ошондуктан, корголгон касиеттер тилдин синтаксиси тарабынан колдоого алынбаса да, көбүрөөк колдонулат.
Жеке талаалар өзгөчө.
Эсибизде тургандай, биз адатта бул [аты] аркылуу объекттин талааларына кире алабыз:
@A@class User {
...
sayHi() {
let fieldName = "name";
alert(`Hello, ${this[fieldName]}`);
}
}@A@
Жеке менчик менен, бул мүмкүн эмес: this['#name']
ал иштебейт. Бул синтаксис чектөө купуялык себептерден улам.
Бардыгы
OOP терминдеринде, ички интерфейсти тышкы интерфейстен бөлүү инкапсуляция деп аталат .
Бул төмөнкү артыкчылыктарды камсыз кылат:
Колдонуучулардын бутуна ок атпаш үчүн коргоо
Кофе кайнаткычты колдонуп иштеп чыгуучу топ бар экенин элестетиңиз. Ал Better Coffeemakers тарабынан жасалган жана жакшы иштейт, бирок коргоочу капкагы алынып салынган. Ички интерфейс сырттан жеткиликтүү болуп калды.
Бардык иштеп чыгуучулар маданияттуу - алар кофе кайнаткычты өз максатына ылайык колдонушат. Бирок алардын бири Жон өзүн эң акылдуу деп чечип, кофе кайнаткычтын ичине бир аз өзгөртүү киргизген. Андан кийин кофе кайнаткыч эки күндөн кийин иштен чыкты.
Бул, албетте, Жакандын күнөөсү эмес, тескерисинче, коргоочу жабууну чечип, Жонго өзүнүн манипуляцияларын жасоого уруксат берген адам.
Программалоодо да ушундай. Эгер класстын колдонуучусу сырттан өзгөртүүгө тийиш эмес нерселерди өзгөртсө, анын кесепеттерин алдын ала айтуу мүмкүн эмес.
Техникалык тейлөө
Программалоодогу кырдаал чыныгы кофе кайнаткычка караганда татаалыраак, анткени биз аны бир эле жолу сатып албайбыз. Код дайыма иштелип чыгып, өркүндөтүлүп турат.
Эгерде биз ички интерфейсти так бөлүп алсак, анда класстын иштеп чыгуучусу өзүнүн ички касиеттерин жана ыкмаларын колдонуучуларга билдирбестен эркин өзгөртө алат ...
Эгер сиз ушундай класстын иштеп чыгуучусу болсоңуз, анда купуя ыкмаларды аман-эсен атын өзгөртүүгө, алардын параметрлерин өзгөртүүгө жана ал тургай жок кылууга болот, анткени эч кандай тышкы код алардан көз каранды эмес.
Жаңы версияда сиз бардыгын толугу менен кайра жаза аласыз, бирок эгер фронтон мурунку бойдон калса, колдонуучуга жаңыртуу оңой болот.
Татаалдуулукту жашыруу
Адамдар жөнөкөй нерселерди колдонгонду жакшы көрүшөт. Жок дегенде сыртта. Ичинде эмне бар башка маселе.
Программисттер да четте калышпайт.
Ишке ашыруунун деталдары жашырылган жана жөнөкөй, жакшы документтештирилген алдыңкы бөлүккө ээ болуу ар дайым жагымдуу.
Ички интерфейсти жашыруу үчүн биз корголгон же купуя касиеттерди колдонобуз:
- Корголгон талаалар менен префикс коюлган
_
. Бул белгилүү конвенция жана тил деңгээлинде колдоого алынбайт._
Программисттер өз классынын ичинен жана андан алынган класстардан гана башталган талаага кирүүлөрү керек . - Жеке талааларга префикс коюлган
#
. JavaScript класстын ичиндеги мындай талааларга гана кире аларыбызды камсыздайт.
Учурда, жеке талаалар браузерлерде жакшы колдоого алынбайт, бирок политолтурууну колдонсо болот.