Ачааллж байна...
Түвшин 3-ын сүүлийн бүлэгт тавтай морил. Өмнөх хоёр бүлэгт бид нэг class-ийн дотоод хэрэгслүүдийг эзэмшсэн: classmethod, staticmethod, class attribute, dataclass, operator-ууд, container протокол, __call__, __iter__. Энэ бүлэг өөр түвшний асуулт тавина: олон class, олон функц хоорондоо хэрхэн зохион байгуулагдах ёстой вэ? Нэг объект нөгөөгөө хэрхэн сонгох вэ, хэрхэн үүсгэх вэ, өөрчлөлтийн тухай хэрхэн мэдэгдэх вэ?
Програм хангамжийн түүхэнд эдгээр асуултууд дахин дахин гарч ирсэн бөгөөд хөгжүүлэгчид тэдгээрт шалгарсан шийдлүүдийг олж, нэр өгсөн. Ийм нэртэй, олон удаа шалгагдсан шийдлийг design pattern буюу "загварчлалын хэв маяг" гэж нэрлэдэг. Энэ хичээлд код бага байна. Бид pattern гэж юу болох, хаанаас гаралтай, яагаад тэдгээрийг мэдэх нь хэрэгтэй, мөн хамгийн чухал нь яагаад тэдгээрт болгоомжтой хандах ёстойг ярилцана.
Энэ хичээлийн хоёр гол мессеж бий. Нэгдүгээрт, та pattern-уудыг аль хэдийн хэрэглэж байсан. Энэ сургалтын хэд хэдэн хичээл нэргүйгээр тэдгээрийг заасан бөгөөд одоо тэдгээрт нэр өгнө. Хоёрдугаарт, Python-д ихэнх pattern бусад хэлнээс хамаагүй хөнгөн, заримдаа бараг үл харагдана. Сонгодог pattern-уудыг Python-д Java эсвэл C++-ийн хэлбэрээр нь хуулах нь илүүдэл, ойлгомжгүй кодын түгээмэл шалтгаан. Энэ бүлгийн гурван pattern-ыг бид Python маягаар, тухайн асуудалд яг хэрэгтэй хэмжээгээр судална.
Хоолны жорыг төсөөлөөд үзээрэй. Бууз хийх арга нэг хүний санаанаас нэг өдөр гараагүй. Олон үеийн хүмүүс гурил, мах, сонгино, давсны хэмжээ, хайрах хугацааг туршиж, алдаж, засаж, эцэст нь "ингэвэл сайн болдог" гэсэн дадлыг бий болгосон. Тэр дадлыг "бууз" гэж нэрлэдэг. Нэр нь чухал: та гал тогоонд "бууз хийе" гэвэл хамт ажилладаг хүн тань бүх алхмыг тайлбарлахгүйгээр ойлгоно.
Гэвч жор хууль биш. Туршлагатай тогооч жорыг нөхцөлдөө тохируулна: махны төрлөөр, гэр бүлийн амтаар, бэлэн байгаа хэрэглээгээр. Мөн тогооч хоол бүрт жор хайдаггүй: аяга цай чанахад жор хэрэггүй. Хэрэв хэн нэгэн цай чанахын тулд таван хуудас процедур дагавал тэр ухаалаг биш, харин инээдтэй харагдана.
Design pattern бол програмчлалын жор: олон дахин шалгарсан зохион байгуулалтын жор. Тэр нэртэй тул хөгжүүлэгчид богино үгээр санаагаа солилцоно: "энд Strategy хэрэглэе" гэвэл бүх баг ойлгоно. Тэр шалгагдсан тул түүнд итгэж болно. Гэвч тэр хууль биш, хэлэндээ тохируулагдах ёстой, бас асуудал бүрт хэрэггүй.
Pattern бүр дөрвөн хэсэгтэй гэж үздэг уламжлал бий. Нэгдүгээрт, нэр: хөгжүүлэгчдийн хамтын үгсийн сан. Хоёрдугаарт, асуудал: pattern ямар нөхцөлд хэрэгтэй, ямар зөрчилдөөнийг шийддэг. Гуравдугаарт, шийдэл: объектууд, функцууд хэрхэн зохион байгуулагдах, хоорондоо хэрхэн харилцах ерөнхий хэлбэр. Тодорхой код биш, харин код бичих чиглэл. Дөрөвдүгээрт, үр дагавар: pattern юу өгдөг, юуны төлөө төлдөг. Сүүлийн хэсгийг ихэвчлэн мартдаг ч хамгийн чухал нь. Pattern бүр кодонд нэмэлт бүтэц, нэмэлт давхарга авчирдаг бөгөөд тэр зардал асуудлын үнэ цэнээс хэтэрвэл pattern хор хөнөөлтэй болно.
Pattern-ийн санаа програмчлалаас биш, архитектураас гаралтай. 1977 онд архитектор Кристофер Александр “A Pattern Language” номдоо барилга, хотын төлөвлөлтөд дахин дахин гарч ирдэг асуудлууд ба тэдгээрийн шийдлүүдийг нэрлэж, системчилсэн. Програм хангамжийн хөгжүүлэгчид энэ санааг авч, 1994 онд дөрвөн зохиогч, Эрих Гамма, Ричард Хелм, Ральф Жонсон, Жон Влиссидес, “Design Patterns: Elements of Reusable Object-Oriented Software” номоо хэвлүүлсэн. Тэр номд 23 pattern бий бөгөөд зохиогчдыг хамтад нь “Gang of Four” буюу "Дөрвийн бүлэг" гэж нэрлэх болсон. Номын жишээнүүд C++ ба Smalltalk хэл дээр бичигдсэн бөгөөд энэ баримт Python-ий хувьд маш чухал, учрыг нь удахгүй харна.
Энэ сургалтын хэд хэдэн хичээл pattern-уудыг нэргүйгээр заасан. Одоо тэдгээрт нэр өгье.
Iterator. Gang of Four-ын pattern-уудын нэг: "цуглуулгын дотоод бүтцийг ил гаргахгүйгээр түүний элементүүдийг нэг нэгээр нь авах арга". Энэ бол Бүлэг 1-ийн бүх сэдэв: хайрцаг ба түгээгч, __iter__ ба __next__. C++-д энэ pattern-ыг хэрэгжүүлэхэд тусгай class-ууд, интерфэйсүүд бичих шаардлагатай байсан. Python-д тэр хэлний дотор суусан: for loop өөрөө iterator pattern-ыг хэрэглэдэг, generator түүнийг гурван мөрөнд бичих боломж олгодог. Хичээл 8.4-ийн Playlist-ийн __iter__ бол Iterator pattern-ын бүрэн хэрэгжилт.
Decorator. Gang of Four-ын бас нэг pattern: "объектыг ороож, түүний зан чанарт нэмэлт үүрэг нэмэх, анхны объектыг өөрчлөхгүйгээр". Бүлэг 5 бүхэлдээ энэ санаа. Шударгаар тэмдэглэе: Python-ий @ decorator ба Gang of Four-ын Decorator pattern нэг санаанаас гаралтай ч яг ижил зүйл биш. Pattern объектуудыг ороодог бол Python-ий decorator ихэвчлэн функцүүдийг тодорхойлох мөчид ороодог. Гэвч гол санаа, "ороох замаар нэмэлт зан чанар өгөх", ижил. Python түүнд тусгай бичлэг, @ тэмдгийг өгсөн нь pattern хэлэнд хэр гүн шингэснийг харуулж байна.
Factory. Хичээл 7.1-ийн classmethod туслах хаалганууд, from_string, from_dict, from_file, бол объект үүсгэх ажлыг тусгай газарт төвлөрүүлдэг Factory pattern-ын нэг хэлбэр. Хичээл 9.3-т бид үүнийг илүү өргөн хүрээнд судална.
Strategy. Хичээл 3.2-ын key= argument. sorted() эрэмбэлэх алгоритмаа өөрчлөхгүйгээр харьцуулах шалгуурыг гаднаас хүлээн авдаг: уртаар, оноогоор, монгол цагаан толгойгоор. Ажлын нэг хэсгийг сольж болдог хэсэг болгон гадагш гаргах нь Strategy pattern. Дараагийн хичээлийн сэдэв.
Composite. Хичээл 8.3-ын AllOf: шалгагчдын бүлэг өөрөө шалгагч, түүнийг ганц шалгагч шиг дуудаж, өөр AllOf-д хийж болно. "Хэсэг ба бүхлийг ижил байдлаар харьцах" нь Composite pattern.
Тэгэхээр энэ бүлэг танд цоо шинэ ертөнц нээхгүй. Тэр танд аль хэдийн хэрэглэж байгаа зүйлдээ нэр өгнө. Энэ нь бага зүйл мэт санагдаж магадгүй, гэвч нэр хүчтэй. Хамтран ажиллагчдаа "энд жижиг Strategy хэрэгтэй" гэх нь гурван догол мөр тайлбараас хурдан. Нэр нь мөн шийдлийн үр дагаврын тухай хуримтлагдсан мэдлэгт хаалга нээнэ: Strategy-ийн тухай олон арван жилийн туршид юу бичигдсэнийг та одоо хайж олох боломжтой.
Gang of Four-ын ном C++ ба Smalltalk-д бичигдсэн гэдгийг дахин санаарай. Тэр үеийн C++-д функц утга байж чадахгүй, closure байхгүй, хэлбэр нь ижил ч төрөл нь өөр объектуудыг нэг газарт хэрэглэхийн тулд тэдгээрийг нийтлэг эцэг class-аас удамшуулах шаардлагатай байв. Pattern-уудын ихэнх нь эдгээр хязгаарлалтыг тойрох арга байсан. Хэрэв таны хэлэнд тэр хязгаарлалт байхгүй бол тойрох арга ч хэрэггүй.
1996 онд Питер Норвиг Lisp ба Dylan зэрэг динамик хэлүүдэд Gang of Four-ын 23 pattern-аас 16 нь хялбар болдог эсвэл бүр үл харагддаг гэдгийг харуулсан алдартай илтгэл тавьсан. Python ч ижил шинж чанаруудтай. Энэ сургалтын хэд хэдэн бүлэг яг тэр шинж чанаруудыг заасан:
Функц бол утга (Бүлэг 3). Хуучин хэлүүдэд "сольж болдог үйлдэл" хэрэгтэй бол түүнийг ганц method-той class болгож, интерфэйс тодорхойлох хэрэгтэй байсан. Python-д функцийг шууд дамжуулна. Strategy, Command зэрэг pattern-ууд энгийн функц болж хувирдаг.
Closure ба __call__ (Бүлэг 4, Хичээл 8.3). Тохиргоотой үйлдэл хэрэгтэй бол factory class биш, function factory эсвэл callable объект.
Duck typing ба протоколууд (Бүлэг 8). Хуучин хэлүүдэд объект "хэрэглэгдэж болох" эсэхийг түүний эцэг class шийддэг байсан. Python-д түүний чадвар шийддэг: __call__ байвал дуудагдана, __iter__ байвал давтагдана. Удамшлын урт шатлалууд хэрэггүй.
Модуль бол ганц (Course 2-оос). Gang of Four-ын Singleton pattern “class-ийн цорын ганц объект” баталгаажуулдаг бөгөөд үүнд нарийн код шаардагддаг. Python-д модуль анх импортлогдох үедээ нэг удаа ажилладаг, дараагийн импортууд ижил модуль объектыг буцаадаг. Тэгэхээр модуль өөрөө аль хэдийн ганц. Тохиргоо, холболтын пул зэрэг "ганц байх ёстой" зүйлийг модулийн түвшинд тодорхойлоход хангалттай.
Хэлний бэлэн бичлэгүүд. Iterator-т for ба yield, Decorator-т @, нөөц удирдлагад with. Pattern хэлний синтакс болсон.
Энэ бүхний дүгнэлт нь: pattern-ийн санаа Python-д бүрэн хүчинтэй, харин түүний хэлбэр хамаагүй хөнгөн. Хэрэв та Java-ийн сурах бичигнээс pattern хуулж, Python-д class-ийн шатлал, интерфэйс, factory-ийн factory бичиж байгаа бол зогсоод асуу: “Python-д энэ хязгаарлалт үнэхээр байна уу?”
Pattern-ууд олон жилийн туршид хөгжүүлэгчдийн дунд нэр хүндийн тэмдэг болсон. Ярилцлага дээр "та ямар pattern-ууд мэддэг вэ" гэж асуудаг, CV-д pattern-уудын нэрийг жагсаадаг. Энэ нь нэг хор хөнөөлтэй дадал үүсгэсэн: асуудлаас биш, pattern-аас эхлэх. "Энэ төсөлд Observer хэрэглэе" гэж эхлээд, дараа нь түүнийг хаана хийхээ хайх. Үр дүн нь ердөө нэг тоо нэмдэг функцийг гурван class, хоёр интерфэйс, нэг factory-гоор ороосон код. Хөгжүүлэгчид үүнийг хошигнон “patternitis” буюу “pattern-ийн өвчин” гэж нэрлэдэг. Java-ийн алдартай сангуудын нэгэнд бодитоор оршдог AbstractSingletonProxyFactoryBean гэсэн class-ийн нэр энэ өвчний бэлгэдэл болсон: дөрвөн pattern-ийн нэр нэг үгэнд.
Энэ бүлгийн зарчим энгийн: pattern асуудалд үйлчилдэг, CV-д биш. Эрүүл дараалал иймэрхүү: эхлээд хамгийн энгийн, ажилладаг код бич. Дараа нь бодит асуудал гарч ирэхэд, жишээлбэл "энэ if-elif гинж арван салаатай болж, өдөр бүр шинэ салаа нэмэгдэж байна", тэр асуудалд аль pattern тохирохыг таниад, кодыг тэр рүү дахин бүтэцлэ. Pattern-ийг урьдчилан нэмэх биш, хэрэгцээ гарахад гарч ирэх ёстой. Хэрэв pattern хэрэглэсний дараа код уншихад хэцүү болсон бол тэр pattern тухайн асуудалд тохирохгүй байж магадгүй.
Gang of Four-ын 23 pattern-ыг бүгдийг судлах нь энэ сургалтын зорилго биш. Бид гурвыг сонгосон: Python-д хамгийн түгээмэл, бодит кодонд хамгийн их тааралддаг, мөн тус бүр өмнөх бүлгүүдийн өөр өөр хэрэгслийг ашигладаг.
Strategy (Хичээл 9.2): сольж болдог зан чанар. Хүргэлтийн төлбөрийн тооцооны олон аргыг сонгох. Бүлэг 3-ын "функц бол утга" санааны ачаар Python-д бараг үнэгүй.
Factory (Хичээл 9.3): объект үүсгэх ажлыг нэг хаалганы цаана нуух. Хичээл 7.1-ийн classmethod-оос эхлээд тохиргооны файлаас аль class-ийг үүсгэхийг шийддэг loader хүртэл.
Observer (Хичээл 9.4, Түвшин 3-ын бүтээл): нэг объектод болсон үйл явдлын тухай олон сонсогчдод мэдэгдэх. Хичээл 8.3-ын __call__ ба Бүлэг 8-ын протоколууд тэнд нэгдэнэ.
Хичээл бүр pattern-ийг эхлээд “Python-гүй” хэлбэрээр, класс ба интерфэйсийн хүнд хувилбараар товч харуулж, дараа нь Python маягийн хөнгөн хувилбарыг бүтээнэ. Ингэснээр та бусдын кодонд аль хэлбэрийг харсан ч таних болно.
Хүсвэл өөрийн Course 2 эсвэл Course 3-ын төслөөс энэ хичээлд нэрлэсэн pattern-уудыг хайж үзээрэй. key= эсвэл callback функц дамжуулсан газар байна уу? Энэ бол Strategy. classmethod туслах хаалга, эсвэл "төрлөөс хамааран аль class үүсгэхийг сонгодог" функц байна уу? Энэ бол Factory. Сонирхвол Course 3-ын FastAPI кодоо харж, @app.get ба Depends зэрэг хэрэгслүүдэд ямар pattern-ууд нуугдаж байж болохыг бодож болно. Мөн эсрэг чиглэлд хайх нь сонирхолтой: зөвхөн нэг method-той class, эсвэл юу ч хийдэггүй давхарга олдох уу? Тэдгээрийг энгийн функц болгож болох уу? Эдгээр бүгд санал бөгөөд дараагийн хичээлүүд үүнийг хийсэн гэж үзэхгүй.
Энэ хичээлд код бага тул алдааны мессеж ч байхгүй. Харин pattern-уудтай ажиллахад түгээмэл гардаг сэтгэлгээний алдаанууд бий.
Асуудлаас биш, pattern-аас эхлэх. "Энэ төсөлд ямар pattern хэрэглэх вэ" гэж эхлэх нь асуудалд тохирохгүй бүтэц авчирдаг. Зөв асуулт нь "ямар асуудал тулгарч байна вэ, түүнд шалгарсан шийдэл бий юу?"
Java ба C++-ийн хэлбэрийг хуулах. Ганц method-той class, үргэлж ижил нэг объект буцаадаг getInstance() method, бүх attribute-д get_name() ба set_name() method бичих нь Python-д илүүдэл. Ганц method-той class ихэвчлэн функц байх ёстой. Ганц объект ихэвчлэн модулийн түвшний хувьсагч байх ёстой. getter ба setter-ийн оронд энгийн attribute хэрэглэж, шалгалт хэрэгтэй болоход Хичээл 5.4-ийн property руу шилжинэ: гаднах код огт өөрчлөгдөхгүй.
Урьдчилан ерөнхийлөх. "Хэзээ нэгэн цагт өөр төрлийн хүргэлт нэмэгдэж магадгүй" гэж ганц хүргэлтийн аргад зориулж Strategy бүтэц барих. Ихэнхдээ тэр "хэзээ нэгэн цаг" ирдэггүй, эсвэл ирэхдээ таны таамагласнаас өөр хэлбэртэй ирдэг. Хоёр дахь, гурав дахь тохиолдол гарч ирэхэд л ерөнхийлөх нь илүү найдвартай.
Нэрийг шийдэл гэж андуурах. "Энд Factory хэрэглэсэн" гэх нь кодыг сайн болгодоггүй. Pattern-ийн нэр бол харилцааны хэрэгсэл, чанарын баталгаа биш. Код сайн эсэхийг тестүүд, уншигдах байдал, өөрчлөхөд хялбар байдал шийднэ.
Design pattern бол програм хангамжид дахин дахин гарч ирдэг асуудлуудад өгсөн, нэртэй, шалгагдсан шийдэл: олон дахин шалгарсан зохион байгуулалтын жор. Pattern бүр нэр, асуудал, шийдэл, үр дагавар гэсэн дөрвөн хэсэгтэй, мөн сүүлийнх нь хамгийн чухал, учир нь pattern бүр бүтцийн зардалтай. Санаа нь Кристофер Александрын архитектурын бүтээлээс гаралтай бөгөөд 1994 оны Gang of Four-ын ном 23 pattern-ыг C++ ба Smalltalk-ийн жишээгээр нэрлэсэн. Та энэ сургалтад Iterator, Decorator, Factory, Strategy, Composite-ийг нэргүйгээр аль хэдийн хэрэглэсэн. Python-д ихэнх pattern хөнгөн эсвэл үл харагдана, учир нь функц утга, closure ба __call__ бий, duck typing удамшлын шатлалыг шаарддаггүй, модуль өөрөө ганц, Iterator, Decorator, нөөцийн удирдлага хэлний бичлэгт шингэсэн. Pattern-ийг асуудлаас эхэлж, хэрэгцээ гарахад хэрэглэнэ: асуудалд үйлчилдэг, CV-д биш.
Дараагийн хичээлд эхний pattern-аа бүрэн судална: Strategy. Онлайн дэлгүүрт хүргэлтийн төлбөр тооцох хэд хэдэн арга бий гэж бодъё: стандарт, түргэн, дэлгүүрээс авах, үнэгүй хүргэлтийн урамшуулал. Эдгээрийг if-elif гинжээр бичих нь эхэндээ энгийн боловч арга нэмэгдэх бүрт нэг функц томорсоор байна. Strategy тэр гинжийг сольж болдог хэсгүүд болгон задалдаг. Бид эхлээд түүний сонгодог class хэлбэрийг товч харж, дараа нь Python-д зүгээр л функцүүдийн dict болж хувирахыг харна.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.