1986 онд, би амьдралд минь хамгийн их нөлөө үзүүлсэн хүнтэй хамт ажилладаг байлаа. Нэрийг нь Билл гэдэг, манай компаний борлуулагч нарын нэг. Гэхдээ зүгээр ч нэг борлуулагч биш №1 борлуулагч нь байсан юм. 190см өндөр, баатар шиг биетэй, гал улаан үс, ногоон нүдтэй, үргэлж албаны хувцасладаг хүн байлаа. Хичнээн ажил ихтэй, оройн цагаар сууж ажиллаж байсан ч, ядаж нэг удаа "зангиагаа сулласан" байхыг нь хараагүй юм.
Хаа ч очсон тэр газрынхаа эзэн нь юм шиг л том том алхлан явж ордог, замд нь байгаа ямар ч хүн зай тавьж өгдөг, ярьсан яриаг нь бүгд анхааралтай сонсдог нэгэн байлаа. Бас тэр захиралтай ч, надтай ч, хамтран ажиллагсадтайгаа ч, тэр ч бүү хэл цайны газрын зөөгчтэй ч яг ижилхэн үргэлж инээмсэглэсэн найрсаг харьцдаг байсан юм. Миний л амьдралдаа харсан хүмүүсээс хамгийн гоё хандлага, харьцаатай хүн байсан юм. Түүний мэндийг мэдэж “сайн байна уу?” гэж асуух болгонд "гайхалтай сайн байна, үүнээс дээр байсангүй." гэж хариулдаг байлаа. Бүр чин сэтгэлээсээ шүү...
Нэг өдөр тэр хүүхдийнхээ талаар маш муу мэдээ аваад хэдэн өдөр ажилдаа ирсэнгүй. Хэд хоногийн дараа, зангиагаа хоолойндоо тултал татаж, нүүр дүүрэн инээмсэглэлтэй, гар цүнхээ барьсаар том том алхлан орж ирэв. Тухайн үед бүгд л түүнтэй найз байхыг хүсдэг байсан ч, ойр ажилладгийн хувьд би өөрийгөө илүү дотно найз нь гэж боддог байлаа. Тийм ч учраас тэр өглөө би түүний өрөөнд ороод хаалгыг хаангаа, "За, сайн уу хө? Яажшуухан байна? Миний хийж чадах зүйл байна уу? Юу ч байсан хамаагүй зүгээр л хэлэхэд болно шүү." гэлээ. Өөдөөс минь шүдээ ярзайлган, "Гайхалтай сайхан байна, үүнээс дээр байсангүй." гээд хоосорсон нэг удаагийн кофены аягаа хогийн сав руу шидэв. Би хэсэг зогсож байгаад, "Яагаад? Яаж та ийм цоглог байж чадаж байна аа? Яаж одоо байгаа чинь өмнөхөөсөө илүү сайхан байдаг юм?" гэлээ.
Нүүрэндэх инээмсэглэл нь тэр дороо арилж, ширээн дээрээ тохойлдон урагш бөхийж надруу хуруугаараа чичилж байгаад... (Тэгэхэд тэр маш их ууртай харагдаж байлаа, би түүнийг уурлаж байхыг нь огт хараагүй байсан болоод тухайн үед эргэж хараад зугтчихмаар л байлаа)
"За чи намайг сонс, сайн сонс. Чи миний талаар болон миний амьдралын талаар үнэндээ юу ч мэдэхгүй. Чи зүгээр л мэддэг гэж л бодож байгаа. Би чамд нэг юм хэлье, чи харин сайн санаж яваарай. Чиний зан, хандлага чинь үнэндээ авах юм алга. Хамт ажиллагсад чинь яаж тэсч байгааг ойлгохгүй байна. Би лав тэгж тэсч чадахгүй. Чиний хандлага бол чиний бүх юм. Чиний амьдралыг чиглүүлж бүтээдэг зүйл чинь юм. Одоо болж буй бүх зүйл муу байна бүтэхгүй байна гэж бодож байна уу? Чамд л гэж хэлэхэд, бүүр ч муу байж болох л байсан. бүүр долоон дор ч байж болно. Чи одоо энд наад хямдхан цамц зангиатайгаа зогсчихоод, хурал дээр олигтой байсангүй гэж гомдоллож байна уу? Чи гараад бохирын нүх ухаж үзчихээд дараа нь өнөөдрийн энэ хурлаа дүгнээд үзвэл юу гэхүү? Эсвэл чи ажлаасаа халагдаад бохирын нүхэнд амьдарч, хогийн савнаас хоол идснийхээ дараа юу гэж харах бол? Чи үнэндээ юу ч мэдэхгүй байна. Би чамд энийг одоо л, ганцхан удаа л хэлчихье, дахиж энэ талаар ярихгүй, тиймээс сайн сонс, залуу минь. Хэн нэгний 'ямархуу байна' гэсэн асуулт нь ГАНЦХАН л хариулттай. Тэр нь 'ҮҮНЭЭС ДЭЭР БАЙСАНГҮЙ'. Энийг үнэн гэж итгээд л амьдрах хэрэгтэй. Яагаад гэвэл, чамд хичнээн онцгүй байсан ч, байдал тэрнээс чинь долоон дор байж болох л байсан. Тиймээс наад зүүднээсээ сэрээд, хандлагаа өөрчил, ЯГ ОДОО!"
Ингэж хэлээд буцаад сандлаа налаад юу ч болоогүй юм чинь инээмсэглэж эхлэв, би ч зог тусан байсан газраа чимээгүй зогсож байлаа. Хамаг бие маань хөлөрч цамц маань норж байсныг санадаг юм. "Дахиад нэг кофе уумаар санагдчихлаа, чи надтай цуг явах уу? Би даая." гэхээр нь, дагаад явлаа. Тэр юу ч болоогүй юм шиг л байлаа. Харин миний хувьд бүх зүйл өөрчлөгдсөн байсан юм.
Би ч тэр ажилдаа түүний хүссэн шиг байж чадахгүй байгаагаа мэдээд, ажлаасаа гараад өөр ажилд орохоор болов. Өмнөх ажилдаа бараг 10 жил ажиллачихсан байсан болохоор их л тэвдэж байсан юмдаг. Шинэ компанид орох нь их том өөрчлөлт байлаа. Намайг тэр ажил руу анх ороход ресепшн нь инээмсэглэн надаас хэр байгааг минь асуув, Би ч нүүр дүүрэн инээмсэглэн "Үүнээс илүү сайхан байсангүй" гэлээ. Хариуд нь "За тэгвэл сайн байна, та үргэлж ийм байж чадвал энд яг тохирох юм байна" гэв.
Ингээд ажил ч эхэлж, би компани доторх хамгийн сэргэлэн, цоглог хүн нь болов. Би наргианы эзэн, онигооны бай нь байлаа. Гэхдээ тухайн үедээ мэдээгүй ч би дампуурлын ирмэг дээр байгаа компанид орцон байлаа. Компаний байдал муудах тусам миний харьцаа улам л анзаарагдаж байлаа. Хэдий тэдний технологийн талаар юу ч мэддэггүй байсан ч, би их хурдан зэрэг ахидаг байсан юм. Хэсэг хүн цомхотгов, дараа нь бүүр ихийг цомхотгов, дахиад л бүүр ихийг... Гэсэн ч би хамгийн шинэ хүн нь байсан мөртлөө л бүх цомхотголоос мултарч чадаж байлаа. Сүүлдээ гайхаад менежерээсээ яагаад би үлдээд байгааг асуув. Менежер маань,
"Юу л даа, хурал дээр хэнийг цомхотгох талаар ярилцах үед чиний нэр үргэлж хамгийн түрүүнд гарч ирдэг байсан юм. Чи шинэ, бас ямар ч туршлагагүй учраас чамайг явуулах нь зөв сонголт нь байсан л даа. Гэхдээ яг санал хураахад бүгд л чамайг үлдээх хүсэлтэй байлаа. ЧИНИЙ ХАРЬЦАА ХАНДЛАГА ЧИНЬ ҮНЭХЭЭР ЭЕРЭГ БАЙСАН БОЛОХООР БҮГД ЧАМАЙГ ҮЛДЭЭХ ХҮСЭЛТЭЙ БАЙСАН. Ерөнхий захирал хүртэл, гутарсан таван эксперттэй байснаас ганц цоглог шинековтой байсан нь дээр гэж байсан. Ер нь хүний хандлага л хамгийн чухал нь байгаа юм л даа. Чи санаа зоволтгүй дээ, намайг явахад хүртэл чи энд байсаар л байна..." гэж билээ.
Байдал дордох тусам би ахиж, 18 сарын дотор хэлтсийнхээ ахлах нь болов. Эцэст нь компани авралгүй болж, хуучин менежер маань надад өөр ажил санал болгож би ч саналыг нь авч ажлаасаа гарав. Энэ бүхний эцэст, би ажилдаа амжилттай байхыг хүсч буй ажилтан бүрийн дагах 3 зүйлсийг бодож олсон юм. Энийгээ орсон компани болгоныхоо ханан дээр наадаг байлаа, олон ч хүн энийг хуулж авч байсан юм. Тэд маань:
- Удирдлагын чинь хувьд: Чиний хамгийн их анхаарах зүйл бол яг дээд талынхаа удирдах албан тушаалтныг л сайхан харагдуулах
- Компаний чинь хувьд: Чиний хамгийн их анхаарах зүйл бол компаний олох орлогыг ихэсгэх, №2 нь ашгийг нь сайжруулах
- Өөрийн чинь хувьд: Чиний хамгийн их анхаарах зүйл бол үргэлж, ямар ч эргэлзээгүйгээр, эерэг хандлагатай байх, өөртөө итгэх, амжилттай харагдах. Энэ л чиний карьер болон амьдралыг өөрчлөнө. Өөр ямар ч зүйл, ямар ч боловсрол, ямар ч туршлага мэдлэг чамд үүнээс илүү хэрэг болохгүй. “Эерэг хандлагатай дундаж ажилтан гуньсан 5 экспертээс илүү”. Ямар ч үнээр хамаагүй үргэлж эерэг хандлагатай байснаар л чи амжилтанд хүрэх болно.
Энэ олон жилийн хугацаанд, дээрх 3 дүрмийг баримталж чадсан газар болгондоо би амжилт гаргаж, чадаагүй газар болгондоо ялагдаж шаналж явжээ. Хандлага бол чиний амжилтыг тодорхойлох №1 хүчил зүйл шүү. Байдал яаж ч муу байсан гэсэн, тэрнээс илүү муу байж болох л байсан гэдгийг санаж үргэлж эерэг хандлагатай зөв харьцаатай байгаарай.
http://qr.ae/RiJsMU
HashMap
HashMap нь өөр өөрийн гэсэн давтагдахгүй дугаар бүхий "сагс"-нуудын цуглуулгатай бөгөөд сагс бүр нь
LinkedList-д түлхүүр-утгаа хадгалдаг. Шинээр түлхүүр-утга орж ирэх үед, түлхүүрийнх нь hashCode-г аваад тэр
дугаартай сагсанд очно, хэрэв сагс хоосон биш бол, түлхүүртэй "тэнцэх" элемент хүртэл явна. Хэрэв тийм
элемент олдвол, утгаа дараад бичнэ. Хэрэв олдохгүй бол, листэнд нэмж өгнө. Түлхүүр ашиглан HashMap-с утга
авах нь ч мөн адил, эхлээд түлхүүрийн hashCode бүхий дугаартай сагсан дээр очоод, equals методын true буцаах
елементийг олтлоо linkedList-р хэсээд олбол утгыг нь буцаагаад, олохгүй бол null буцаана.
Яг эндээс hashCode болон equals-н "уялдаа" нь харагдах ёстой юм. Тэнцүү биш обьектууд ижил hashCode-той
байхыг давхцал гэх юм бол, хэдий чинээ их давхцалтай байна, сагсан дахь элементүүд төдий чинээ их байна.
Сагсан дахь элементүүд их байх тусам "хэсэх" шаардлага ихсэнэ. Тэр чинээгээрээ get болон put методууд
удаашрана. Харин давхцал үгүй бол, HashMap нь O(1) гэсэн хурдаар олно.
References
Бүтэц нь гэвэл их энгийн. Class-н constructor нь заавал private байна. Ингэснээрээ, тухайн class-н instance-г
гаднаас үүсгэх боломжгүй болох юм. Мөн instance үүсгэх болон үүссэн instance-г нь авах зорилго, public
хандалттай (хүсвэл өөр байж болно.) static method байна. Энэ method-н үүрэг нь, тухайн class-с ямар нэг
instance үүссэн эсэхийг шалгаад үүсээгүй бол үүсгээд түүнийгээ буцаах юм. Яагаад static байх шаардлагатай вэ
гэхээр, static байж байж, ямар нэг instance-р дамжихгүйгээр(угаасаа байхгүй шд) шууд энэ method-г дуудах
боломжтой болох юм. Энэ method-н нэрийг ихэвчлэн getInstance() гэж нэрлэдэг. Нэгэнт үүсгэсэн instance-аа нэг
газар хадгалаад, дараагийн дуудалтан дээрээ буцаах хэрэгтэй учраас, private static variable ашиглана.
Implementations:
1. Singleton class-н хамгийн энгийн implementation доорх хэлбэртэй байж болно.
public class Singleton {
private static Singleton final theInstance = new Singleton();
public static Singleton getInstance() {
return theInstance;
}
private Singleton() {
System.out.println("constructor");
}
}
Тухайн class load хийгдэх мөчид, шинэ instance үүсээд theInstance variable-д утгаа өгчихнө. Дараа нь
шаардлага гараад getInstance method дуудагдах үед зүгээр л тэр variable-нхаа утгыг буцаачихна.
2. Lazy-loading. Class-н шинэ instance үүсгэх процесс нь их хүнд (үнэтэй) бол, мөн class програм ажиллах
бүрд ашиглагдахгүй байх магадлалтай тохиолдолд, дээрх арга тийм зөв сонголт болохгүй байх. Харин зөвхөн
шаардлагатай үед л шинэ instance үүсгэдэг доорх арга илүү зөв болох талтай. Хамгийн эхний getInstance
дуудагдах үед, шинээр instance үүснэ. Тэрний дараагаас зүгээр л variable-нхаа утгыг буцаана.
public class Singleton {
private static Singleton final theInstance = null;
public static Singleton getInstance() {
if (theInstance == null) {
theInstance = new Singleton();
}
return theInstance;
}
private Singleton() {
System.out.println("constructor");
}
}
Thread-Safe. Гэвч дээрх аргыг олон thread зэрэг хандах орчинд ашиглавал, нэгээс олон instance үүсчих
боломжтой. Тэгэхээр getInstance() method-г synchronized болгосноор асуудлыг шийдэж болно.
public static synchonized Singleton getInstance() {
Гэвч, хамгийн эхний удаад л instance үүсгээд, бусад бүх дуудалтанд зүгээр бэлэн утга буцаадаг энэ method-г
synchronized болгосноороо, performance талаасаа их асуудалтай болж ирнэ. Зүгээр нэг утга буцаахын төлөө
lock/unlock/wait хийгдэнэ гэсэн үг.
Double-Checked-Locking. Дээрх асуудлыг шийдэхийн тулд, бид эхлээд variable-нхаа утгыг шалгаад, хэрэв
instance үүссэн бол шууд буцаадаг, үүсээгүй бол synchronized block руу ордогоор хийе.
public class Singleton {
private static Singleton final theInstance = null;
public static Singleton getInstance() {
if (theInstance == null) {
synchronized(Singleton.class) {
if (theInstance == null) {
theInstance = new Singleton();
}
}
}
return theInstance;
}
private Singleton() {
System.out.println("constructor");
}
}
Ингэснээрээ, эхнийхээс бусад бүх дуудалт ямар нэг саадгүйгээр хурдан хэрэгжинэ. Харин эхний удаад, эхлээд
null эсэхийг нь шалгаад, дараа нь synchronized block руу ороод, дахиад null эсэхийг шалгаад, тэгээд
instance-аа үүсгэнэ. Яагаад яг адилхан шалгалтыг 2 удаа хийгээд байгаад гайхаж магадгүй. Яагаад гэвэл, жишээ
нь t1 thread эхний шалгуурыг даваад, synchronized block руу орохын өмнөхөн нь t2 thread бас тэр шалгуурыг
даваад орох ирэх боломжтой. Энэ үед хэрэв дотор талын шалгуур байхгүй байсан бол, 2 thread ээлжээр
synchronized block руу орж instance-аа үүсгэх байсан юм. Ингээд бидний Singleton худлаа болох байлаа. Давхар
шалгуур тавьж өгснөөрөө 2дахь thread synchronized block руу орсон ч, өмнө нь орсон thread нь үүсгэсэн байна
уу, гэдгийг шалгаж байгаа юм.
Java-н хувьд Double-Checked-Locking арга нь "эвдэрхий" гэж үзэгддэг байна. Яагаад гэвэл,
1. Thread A нь instance үүсээгүй байна гэж бодоод lock хийгээд instance-аа үүсгэж эхэллээ гэж бодъё
2. Java-д ялангуяа, тухайн constructor ажиллаж эхлэх үед л, санах ойд авсан (allocate) хаягаа шууд
хувьсагчдаа (theInstance) оноочихдог байна. Thread A-г instance-аа бүрэн үүсгэж дуусаагүй байх үед
шүү дээ. Өөрөөр хэлбэл, манай static хувьсагч дутуу үүссэн обьектыг зааж байна гэсэн үг.
3. Яг энэ үед Thread B getInstance() method руу орж ирээд хувьсагчийнхаа утгыг хоосон байна уу гэж
шалгана. Мэдээж тэнд дутуу ч гэсэн нэг хаяг оноочихсон байгаа учир, түүнийгээ аваад буцна. Тэгээд
"дутуу үүссэн" instance-г ашиглах гэж оролдоод алдаа заана.
Үүнээс үүдэн Java 5.0-н memory model-д өөрчлөлт орж, volatile түлхүүр үгийн ажиллах зарчим ч шинэчлэгдэж.
Volatile-г нэмж ашиглан дээрх аргыг алдаагүй болгосон байна.
3. On-Demand-Holder. Нэгэнт double-checked-locking нь эвдэрхий гэж үзэгдсэн учир, мөн synchronized,
volatile гэх мэт их "үнэтэй" аргуудыг ашиглаж буй учир, илүү хөнгөн аргыг бодож олцгоожээ. Энэ нь бидний
эхний аргыг inner class ашиглан хийх юм.
public class Singleton {
private static class SingletonHolder {
private static final Singleton theInstance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.theInstance;
}
private Singleton() {
System.out.println("constructor");
}
}
Singleton class нь load хийгдэж байх үед, SingletonHolder class load хийгдэхгүй. Хэзээ load хийгдэх вэ
гэхээр, getInstance() method дуудагдах үед л хийгдэнэ. Тэр бол ямар ч Singleton-н instance үүсэхгүй гэсэн
үг. Энэ нь lazy-loading гэдгийг нь батална. Харин getInstance() method дуудагдахад, JVM SingletonHolder-г
load хийх хэрэгтэйгээ мэдээд (мэдээж өмнө нь хийгээгүй бол шүү дээ) load хийж эхэлнэ. Java-н ямар нэг
class-г load хийх үйлдэл нь өөрөө serial буюу давхар дуудагдах асуудалгүй учир бид SingletonHolder-н load
хийгдэж, доторх Singleton-н reference-дээ шинэ intance үүсгэжн оноож өгөх процесс нь аюулгүй гэдэгт бүрэн
итгэж болно. Энэ нь thread-safe гэдгийг нь харуулна. Нэгэнт class load хийгдэж, бүрэн хамгаалалтын доор шинэ
instance-аа үүсгэсэн болохоор, getInstance() method-н хийх ажил нь ердөө л тэр reference-н утгыг буцаах юм.
4. Enum. "Effective Java" номонд Joshua Bloch-н танилцуулсан арга. Нэгэнт Java-н enum нь lazy байдлаар,
classload хийгдэх үед ажилладаг болохоор, мөн тухайн enum доторх тооны л instance-ууд үүсдэг учир, ганцхан
элементтэй enum үүсгэх нь Singleton-г хэрэгжүүлэх сонгодог арга болж байгаа юм.
public enum Singleton {
INSTANCE;
public void execute (String arg) {
// perform operation here
}
}
Энд ямар ч getInstance гэх мэт static method, variable хэрэггүй, дуудах, ашиглахад ч амар.
Singleton pattern-ы талаар ерөнхийд нь гэвэл нэг иймэрхүү, мэдээж үлдээсэн, дутуу мэдээлэл зөндөө байгаа, мөн
ухаад л байвал ухаад л байхаар юм билээ.
Бусад pattern-ы адилаар энэ pattern-г ашиглах газар гэж бий, ашиглахгүй газар гэж бий. Буруу газраа ашиглах нь
огт ашиглаагүйгээсээ илүүтэй хортой байж болох. Доорх линкүүдэд жишээ нь өгүүлсэн байгаа.
References: