- Notifications
You must be signed in to change notification settings - Fork12
🛁 Clean Code concepts adapted for JavaScript
License
dev-sabbir/clean-code-javascript
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
আসল রিপোজিটরি: ryanmcdermott/clean-code-javascript
- ভূমিকা
- ভ্যারিয়েবলস
- ফাংশনস
- অবজেক্ট এবং ডাটা স্ট্রাকচার
- ক্লাস
- সলিড(SOLID)
- টেস্টিং
- কনকারেন্সি
- এরর হ্যান্ডলিং
- ফরম্যাটিং
- কমেন্টস
- অনুবাদ
এখানে রবার্ট সি. মার্টিন এরClean Code বইয়ে বর্নিত সফটওয়্যার ইঞ্জিনিয়ারিং এর নীতিগুলোকে জাভাস্ক্রিপ্ট এর জন্য কিছুটা পরিবর্তিত করা হয়েছে। এটা কোন স্টাইল গাইড না। এটা হল জাভাস্ক্রিপ্টের মাধ্যমেসুপাঠ্য, পুনব্যবহারযোগ্য, রিফ্যাক্টরযোগ্য সফটওয়্যার তৈরি করার গাইড।
ব্যাপারটা এমন না যে এখানে বর্নিত সব নিয়ম কঠোরভাবে মেনে চলতে হবে, এমনকি এখানে বর্নিত খুব কম নিয়মের সাথে সবাই একমত। এগুলো নির্দেশিকা ব্যাতিত কিছুই না। তবে এগুলোClean Code বইয়ের লেখকদের বহু বছরের সমষ্টিগত অভিজ্ঞতা থেকে বিধিবদ্ধ করা।
আমাদের সফটওয়ার ইঞ্জিনিয়ারিং শিল্পের বয়স ৫০ বছরের কিছু বেশি এবং আমরা এখনও অনেক কিছু শিখছি। যখন সফটওয়ার আর্কিটেকচার স্থাপত্যকলার মত পুরনো হবে, তখন হয়ত আমরা মেনে চলার জন্য কিছু কঠোর নিয়ম পাব। সেদিনের আগ পর্যন্ত আমরা যে জাভাস্ক্রিপ্ট কোড তৈরি করছি তার মান যাচাই করার জন্য এই নির্দেশিকাটিকে ব্যবহার করতে পারি।
ভূমিকা শেষ করার আগে একটা কথা, এই নিয়ম গুলো জানলেই তুমি আগের থেকে ভালো সফটওয়্যার ডেভেলপার হয়ে যাবে না এবং এগুলো অনেক বছর ধরে মেনে চলা মানে এই না যে তুমি আর ভুল করবে না। মাটির দলা থেকে যেমন বিভিন্ন আকৃতি তৈরি হয়, তেমনি প্রতিটা কোড শুরু হয় প্রথম খসড়া থেকে। আমরা যখন সবশেষে আমাদের সহকর্মীদের সাথে কোড রিভিও করতে বসি তখন আমাদের কোডের অসম্পূর্ণতাগুলোকে চেঁছে ফেলে দিই। প্রথম খসড়াতে ভুল থাকবেই। এজন্য নিজেকে শাস্তি দিও না, বরং তোমার কোড ঝেড়েমুছে ঠিক কর।
খারাপ কোড:
constyyyymmdstr=moment().format("YYYY/MM/DD");
ভালো কোড:
constcurrentDate=moment().format("YYYY/MM/DD");
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
খারাপ কোড:
getUserInfo();getClientData();getCustomerRecord();
ভালো কোড:
getUser();
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
আমরা আমাদের ডেভেলপার জীবনে যত কোড লিখব, পড়তে হবে তার থেকে অনেক বেশি। একারণে সুপাঠ্য এবং সহজে খুঁজে পাওয়া যায় এমন কোড লিখা খুবই গুরুত্তপুর্ন। আমরা যদি আমাদের লিখা প্রোগ্রাম বুঝার জন্য ভ্যারিয়েবলের নাম যথেষ্ট অর্থবহ না করি, আমাদের পাঠকদের কষ্ট বাড়বে বই কমবে না। যেসকল ধ্রুবক এবং ভ্যারিয়েবলের নামকরন করা হয় নি সেগুলো চিহ্নিত করতেbuddy.js এবংESLint এর মত টুলগুলো আমাদের সাহায্য করতে পারে।
খারাপ কোড:
// What the heck is 86400000 for?setTimeout(blastOff,86400000);
ভালো কোড:
// Declare them as capitalized named constants.constMILLISECONDS_IN_A_DAY=86400000;setTimeout(blastOff,MILLISECONDS_IN_A_DAY);
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
খারাপ কোড:
constaddress="One Infinite Loop, Cupertino 95014";constcityZipCodeRegex=/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;saveCityZipCode(address.match(cityZipCodeRegex)[1],address.match(cityZipCodeRegex)[2]);
ভালো কোড:
constaddress="One Infinite Loop, Cupertino 95014";constcityZipCodeRegex=/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;const[,city,zipCode]=address.match(cityZipCodeRegex)||[];saveCityZipCode(city,zipCode);
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
নাই মামার চেয়ে কানা মামা ভালো। ভ্যারিয়েবলের নামকরনের সময় এর খেয়াল রাখতে হবে নাম থেকে যেন এর কাজ বুঝা যায়।
খারাপ কোড:
constlocations=["Austin","New York","San Francisco"];locations.forEach(l=>{doStuff();doSomeOtherStuff();// ...// ...// ...// Wait, what is `l` for again?dispatch(l);});
ভালো কোড:
constlocations=["Austin","New York","San Francisco"];locations.forEach(location=>{doStuff();doSomeOtherStuff();// ...// ...// ...dispatch(location);});
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
ক্লাস/অবজেক্ট এর নাম থেকে কোন তথ্য জানা গেলে সেই তথ্য আবার ভ্যারিয়েবলের নামের মধ্যে রাখার দরকার নেই।
খারাপ কোড:
constCar={carMake:"Honda",carModel:"Accord",carColor:"Blue"};functionpaintCar(car){car.carColor="Red";}
ভালো কোড:
constCar={make:"Honda",model:"Accord",color:"Blue"};functionpaintCar(car){car.color="Red";}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
শর্ট সার্কিটিং থেকে ডিফল্ট আর্গুমেন্ট অনেক বেশি পরিচ্ছন্ন। তবে এটা মাথায় রাখতে হবে যে, যদি আমরা ফাংশনে ডিফল্ট আর্গুমেন্ট ব্যবহার করি তবে অসঙ্গায়িত আর্গুমেন্ট এর ক্ষেত্রেই শুধু মাত্র ডিফল্ট ভ্যালু ব্যবহৃত হবে। অন্যান্য falsy ভ্যালু, যেমনঃ''
,""
,false
,null
,0
, এবংNaN
, এগুলোর পরিবর্তে ডিফল্ট ভ্যালু বসবে না।
খারাপ কোড:
functioncreateMicrobrewery(name){constbreweryName=name||"Hipster Brew Co.";// ...}
ভালো কোড:
functioncreateMicrobrewery(name="Hipster Brew Co."){// ...}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
ফাংশন টেস্টিং সহজ করার জন্য, ফাংশন প্যারামিটার কে সীমাবদ্ধ করা খুবই গুরুত্তপুর্ন । ফাংশন প্যারামিটার ৩ এর অধিক হলে, বিন্যাস সমাবেশের কারণে আমাদের টেস্ট কেসের সংখ্যা বেড়ে যায়।
একদমই অপারগ হলে ৩ টি ফাংশন প্যারামিটার ব্যবহার করা উচিত। তবে আদর্শ হল ২ বা তার কম। এর থেকে বেশি প্যারামিটার হলে তাদেরকে একটা অবজেক্ট এর মধ্যে একত্রীকরণের মাধ্যমে প্যারামিটার এর সংখ্যা কমিয়ে আনতে হবে।
যেহেতু জাভাস্ক্রিপ্ট এ অবজেক্ট তৈরি করতে গেলে ক্লাস বয়লারপ্লেট লাগে না, তাই যদি তোমার অনেকগুলো প্যারামিটার প্রয়োজন হয় তবে তুমি অবজেক্ট ব্যবহার করতে পার।
একটা ফাংশনে কি কি প্যারামিটার আসলে যাচ্ছে তা বুঝানোর জন্য ES2015/ES6 এর destructuring syntax ব্যবহার করতে পার। এটার কিছু সুবিধা আছে,
- যখন কেউ ফাংশন সিগনেচার দেখবে তখনি বুঝে ফেলবে কি কি প্যারামিটার দেয়া হচ্ছে ফাংশনের মধ্যে।
- Destructring করলে, ফাংশনের আর্গুমেন্ট হিসেবে যে প্রিমিটিভ ভ্যালু দেয়া হয়েছে সেগুলো ক্লোন হয়ে যায়। যা কিনা আমাদের কে সাইড ইফেক্ট প্রতিরোধ করতে সহায়তা করে। তবে মাথায় রাখতে হবে, যদি এরে বা অবজেক্ট destructure করা হয়, সেক্ষেত্রে এরা ক্লোন হয় না।
- অব্যবহৃত প্যারামিটার খুঁজে বের করতে লীনটার আমাদের হেল্প করতে পারে। কিন্তু Destructure না করলে হয়ত এটা সম্ভব হত না।
খারাপ কোড:
functioncreateMenu(title,body,buttonText,cancellable){// ...}
ভালো কোড:
functioncreateMenu({ title, body, buttonText, cancellable}){// ...}createMenu({title:"Foo",body:"Bar",buttonText:"Baz",cancellable:true});
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
সফটওয়্যার ইঞ্জিনিয়ারিং এ এখন পর্যন্ত এটাই সব থেকে গুরুত্তপুর্ন নীতি। যেসব ফাংশন যদি একের অধিক কাজ করে, সেসব ফাংশন টেস্ট করা, অন্য যায়গায় পুনব্যবহার করা কঠিন হয়ে যায়। একটি ফাংশন যদি কেবল একটি কাজ করে তবে তোমার কোড সুপাঠ্য এবং সহজে পুনরায় লেখা যাবে। তুমি যদি এই গাইডের আর কিছু না গ্রহণ করে শুধু এই নিয়মটি গ্রহণ কর, তাহলেই তুমি অন্য ডেভেলপের থেকে অনেক এগিয়ে যাবে।
খারাপ কোড:
functionemailClients(clients){clients.forEach(client=>{constclientRecord=database.lookup(client);if(clientRecord.isActive()){email(client);}});}
ভালো কোড:
functionemailActiveClients(clients){clients.filter(isActiveClient).forEach(email);}functionisActiveClient(client){constclientRecord=database.lookup(client);returnclientRecord.isActive();}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
খারাপ কোড:
functionaddToDate(date,month){// ...}constdate=newDate();// It's hard to tell from the function name what is addedaddToDate(date,1);
ভালো কোড:
functionaddMonthToDate(month,date){// ...}constdate=newDate();addMonthToDate(1,date);
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
তোমার ফাংশনে একের অধিক ধাপে অ্যাবস্ট্রাকশন থাকা মানেই তোমার ফাংশন একের অধিক কাজ করছে। একে বিভিন্ন ছোটছোট ভাগে বিভক্ত করলে পুনরায় ব্যবহার করা , টেস্টিং করা সহজ হয়ে যায়।
খারাপ কোড:
functionparseBetterJSAlternative(code){constREGEXES=[// ...];conststatements=code.split(" ");consttokens=[];REGEXES.forEach(REGEX=>{statements.forEach(statement=>{// ...});});constast=[];tokens.forEach(token=>{// lex...});ast.forEach(node=>{// parse...});}
ভালো কোড:
functionparseBetterJSAlternative(code){consttokens=tokenize(code);constsyntaxTree=parse(tokens);syntaxTree.forEach(node=>{// parse...});}functiontokenize(code){constREGEXES=[// ...];conststatements=code.split(" ");consttokens=[];REGEXES.forEach(REGEX=>{statements.forEach(statement=>{tokens.push(/* ... */);});});returntokens;}functionparse(tokens){constsyntaxTree=[];tokens.forEach(token=>{syntaxTree.push(/* ... */);});returnsyntaxTree;}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
তোমার সর্বোচ্চ চেষ্টা করবে যেন ডুপ্লিকেট কোড না থাকে। ডুপ্লিকেট কোড থাকা মানেই, কখনো কোন একটার লজিক পরিবর্তন করা লাগলে তোমার সবগুলো ডুপ্লিকেট কোডে পরিবর্তন করা লাগবে।
চিন্তা কর তুমি একটা রেস্টুরেন্ট এ আছো এবং তোমার কাজ হচ্ছে রেস্টুরেন্ট এর স্টোরে কি কি আছে সেগুলোর খবর রাখা। মানে হল রেস্টুরেন্টে কতটুকু টমাটো, পেঁয়াজ, হলুদ, মশলা আছে সেগুলোর হিসাব রাখা। তুমি যদি অনেকগুলো যায়গায় এদের হিসাব রাখ, কোন একটার হিশাব কমলে বা বাড়লে তোমার সব লিস্টে পরিবর্তন করা লাগবে। কিন্তু তুমি যদি একটা লিস্টে এদের হিসাব রাখতে তাহলে একটা লিস্টে পরিবর্তন করলেই হত।
মাঝে মাঝে এমন হয় যে আমাদের ডুপ্লিকেট কোড লিখতে হয়, কারণ দেখা যায়, ২ টা ফাংশন প্রায় একই কাজ করছে শুধু সামান্য একটু পার্থক্য আছে। এই সামান্য পার্থক্যের জন্য আমাদের ২ টা ফাংশন লেখা লাগে। এক্ষেত্রে একটা সমাধান হল, একটা অ্যাবস্ট্রাকশন তৈরি করা।
এই অ্যাবস্ট্রাকশনটা ঠিকঠাক ভাবে করতে পারা খুবই গুরুত্বপুর্ন। একারণে তোমার উচিতক্লাস অধ্যায়ে বর্নিত SOLID প্রিন্সিপাল মেনে চলা। ভুল অ্যাবস্ট্রাকশন ডুপ্লিকেট কোড থেকেও ক্ষতিকর, অতএব সাধু সাবধান! তুমি যদি সঠিক ভাবে অ্যাবস্ট্রাকশন তৈরি করতে পার তবে করে ফেল। তা নাহলে একটা পরিবর্তনের জন্য একাধিক জায়গায় পরিবর্তন করা লাগবে।
খারাপ কোড:
functionshowDeveloperList(developers){developers.forEach(developer=>{constexpectedSalary=developer.calculateExpectedSalary();constexperience=developer.getExperience();constgithubLink=developer.getGithubLink();constdata={ expectedSalary, experience, githubLink};render(data);});}functionshowManagerList(managers){managers.forEach(manager=>{constexpectedSalary=manager.calculateExpectedSalary();constexperience=manager.getExperience();constportfolio=manager.getMBAProjects();constdata={ expectedSalary, experience, portfolio###Functionnamesshouldsaywhattheydo};render(data);});}
ভালো কোড:
functionshowEmployeeList(employees){employees.forEach(employee=>{constexpectedSalary=employee.calculateExpectedSalary();constexperience=employee.getExperience();constdata={ expectedSalary, experience};switch(employee.type){case"manager":data.portfolio=employee.getMBAProjects();break;case"developer":data.githubLink=employee.getGithubLink();break;}render(data);});}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
খারাপ কোড:
constmenuConfig={title:null,body:"Bar",buttonText:null,cancellable:true};functioncreateMenu(config){config.title=config.title||"Foo";config.body=config.body||"Bar";config.buttonText=config.buttonText||"Baz";config.cancellable=config.cancellable!==undefined ?config.cancellable :true;}createMenu(menuConfig);
ভালো কোড:
constmenuConfig={title:"Order",// User did not include 'body' keybuttonText:"Send",cancellable:true};functioncreateMenu(config){config=Object.assign({title:"Foo",body:"Bar",buttonText:"Baz",cancellable:true},config);// config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}// ...}createMenu(menuConfig);
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
ফ্ল্যাগ ব্যবহার করলে তোমার কোড রিভিওয়ার বুঝতে পারে যে এই ফাংশনটি একাধিক কাজ করছে। যদি ফ্ল্যাগ এর ভ্যালু এর উপর নির্ভর করে তোমার কোডফ্লো বিভিন্ন দিকে যায়, তবে তাদের কে আলাদা ফাংশনে রূপান্তরিত কর। তারপর তোমার ফ্ল্যাগ এর উপর নির্ভর করে বিভিন্ন ফাংশন কে কল কর।
খারাপ কোড:
functioncreateFile(name,temp){if(temp){fs.create(`./temp/${name}`);}else{fs.create(name);}}
ভালো কোড:
functioncreateFile(name){fs.create(name);}functioncreateTempFile(name){createFile(`./temp/${name}`);}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
যখন একটা ফাংশন ভ্যালু আদানপ্রদান ছাড়া অন্য কাজ করবে তখনই সাইড ইফেক্ট তৈরি হয়। ফাইলে রাইট করা, গ্লোবাল ভ্যারিয়েবল আপডেট করা এগুলো হল সাইড ইফেক্ট এর উদাহরণ।
মাঝে মাঝে আমাদের সাইড ইফেক্ট এর প্রয়োজন হয়, যেমনঃ ফাইলে রাইট করা। সেক্ষেত্রে আমরা এই সাইড ইফেক্টটাকে একটা আলাদা সার্ভিস বানিয়ে রাখতে পারি। তারপর যেখানে যেখানে দরকার ওই সার্ভিস টা ব্যবহার করতে পারি।
আসল কথা হল, সাধারণ ভুলগুলো এড়িয়ে চলতে হবে। যেমনঃ অবজেক্ট এর মধ্যে state শেয়ার করা, mutable ডাটা টাইপ ব্যবহার করা, সাইড ইফেক্ট গুলোকে ম্যানেজ না করতে পারা ইত্যাদি। যদি তুমি সাইড ইফেক্ট গুলোকে ঠিকঠাক ম্যানেজ করতে পার তাহলে তুমি বেশিরভাগ প্রোগ্রামারদের থেকে শান্তিতে থাকবে।
খারাপ কোড:
// Global variable referenced by following function.// If we had another function that used this name, now it'd be an array and it could break it.letname="Ryan McDermott";functionsplitIntoFirstAndLastName(){name=name.split(" ");}splitIntoFirstAndLastName();console.log(name);// ['Ryan', 'McDermott'];
ভালো কোড:
functionsplitIntoFirstAndLastName(name){returnname.split(" ");}constname="Ryan McDermott";constnewName=splitIntoFirstAndLastName(name);console.log(name);// 'Ryan McDermott';console.log(newName);// ['Ryan', 'McDermott'];
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
জাভাস্ক্রিপ্টে primitive ডাটা pass-by-value এবং এরে/অবজেক্ট pass-by-reference পদ্ধতিতে ফাংশনে যায়। এরে এবং অবজেক্ট এর ক্ষেত্রে, যদি তোমার ফাংশন শপিং কার্ট এরেতে ডাটা পরিবর্তন করে তবে অন্য যত ফাংশন cart এরে ব্যবহার করে সবার cart এরে পরিবর্তন হয়ে যাবে। এটা হয়ত ভাল মনে হচ্ছে, একটা খারাপ কেস চিন্তা করা যাক,
ধর, একজন ব্যবহারকারী তোমার সাইটের "Purchase" বাটন এ ক্লিক করল, যেটা কিনা "purchase" মেথড কে কল করে এবং cart এরে কে নেটওয়ার্ক কলের মাধ্যমে সার্ভার এ প্রেরণ করে। খারাপ ইন্টারনেট কানেকশন এর কারণে, "purchase" ফাংশনকে বারবার নেটওয়ার্কে কল করতে হবে। এখন যদি দুর্ঘটনাবশত তুমি "Add to cart" বাটনে ক্লিক কর তাহলে নতুন আইটেম addItemToCart ফাংশনের মাধ্যমে cart এরেতে যোগ হয়ে যাবে। এবং নতুন আইটেম সহ তোমার রিকুয়েস্ট সার্ভার এ চলে যাবে।
এটার খুব ভালো একটা সমাধান হল, addToItemCart সব সময় cart কে ক্লোন করে নতুন এরে বানাবে, তারপর সেই এরেতে পরিবর্তন করে নতুন এরে টা রিটার্ন করবে। এতেকরে আসল cart এরে অপরিবর্তিত থাকবে।
এই পদ্ধতিতে ২ টা কিন্তু আছে,
- এমন হতে পারে যে তোমার ইনপুট অব্জেক্টটাই পরিবর্তন করা দরকার, ক্লোন করে কাজ হচ্ছেনা। তবে এটা খুবই কম দেখা যা। বেশিরভাগ জিনিশই সাইড ইফেক্ট ছাড়াই রিফ্যাক্টর করা যায়।
- অনেক বড় একটা অবজেক্ট ক্লোন করা পার্ফরমেন্স এর প্রেক্ষিতে খুবই ব্যয়বহুল। সৌভাগ্যবশত বাস্তবে এমন না, কারণ কিছুঅসাধারণ লাইব্রেরি আছে যাদের কারণে অবজেক্ট ক্লোনিং অনেক দ্রুত এবং কম মেমরি ব্যবহার করে করা যায়।
খারাপ কোড:
constaddItemToCart=(cart,item)=>{cart.push({ item,date:Date.now()});};
ভালো কোড:
constaddItemToCart=(cart,item)=>{return[...cart,{ item,date:Date.now()}];};
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
জাভাস্ক্রিপ্টে গ্লোবাল অবজেক্ট এ কিছু পরিবর্তন করা খুবই খারাপ একটি অভ্যাস। কারণ অনেক লাইব্রেরি গ্লোবাল অবজেক্ট ব্যবহার করে। তোমার পরিবর্তন এর কারণে সেইসব লাইব্রেরির কাজের ব্যঘাত ঘটার সম্ভাবনা থাকে। একটা সম্ভাবনা ধরা যাক,কি হবে যদি তুমি জাভাস্ক্রিপ্টের ডিফল্ট এরে মেথড এ "diff" নামক একটি মেথড যোগ কর, যেটা কিনা ২ টা এরের মধ্যে পার্থক্য দেখায়। এই মেথডটা Array.prototype এ রাখলে, অন্য লাইব্রেরি যদি একই "diff" নামক মেথড দিয়ে একটা এরের প্রথম এবং শেষ আইটেম এর পার্থক্য দেখাতে চায় তাহলে কি হবে? এই জন্য ES2015/ES6 এর ক্লাস ব্যবহার করে Array গ্লোবাল কে এক্সটেন্ড করে ব্যবহার করা বেশি ভালো।
খারাপ কোড:
Array.prototype.diff=functiondiff(comparisonArray){consthash=newSet(comparisonArray);returnthis.filter(elem=>!hash.has(elem));};
ভালো কোড:
classSuperArrayextendsArray{diff(comparisonArray){consthash=newSet(comparisonArray);returnthis.filter(elem=>!hash.has(elem));}}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
জাভাস্ক্রিপ্ট হাস্কেল এর মত ফাংশনাল প্রোগ্রামিং ল্যাঙ্গুয়েজ না। কিন্তু এইটায় একধরনের ফাংশনাল ফ্লেভার আছে। ফাংশনাল ল্যাঙ্গুয়েজ টেস্ট করা তুলনামূলকভাবে সহজ। যখনই পারবে এই স্টাইলে প্রোগ্রামিং করবে,
খারাপ কোড:
constprogrammerOutput=[{name:"Uncle Bobby",linesOfCode:500},{name:"Suzie Q",linesOfCode:1500},{name:"Jimmy Gosling",linesOfCode:150},{name:"Gracie Hopper",linesOfCode:1000}];lettotalOutput=0;for(leti=0;i<programmerOutput.length;i++){totalOutput+=programmerOutput[i].linesOfCode;}
ভালো কোড:
constprogrammerOutput=[{name:"Uncle Bobby",linesOfCode:500},{name:"Suzie Q",linesOfCode:1500},{name:"Jimmy Gosling",linesOfCode:150},{name:"Gracie Hopper",linesOfCode:1000}];consttotalOutput=programmerOutput.reduce((totalLines,output)=>totalLines+output.linesOfCode,0);
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
খারাপ কোড:
if(fsm.state==="fetching"&&isEmpty(listNode)){// ...}
ভালো কোড:
functionshouldShowSpinner(fsm,listNode){returnfsm.state==="fetching"&&isEmpty(listNode);}if(shouldShowSpinner(fsmInstance,listNodeInstance)){// ...}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
খারাপ কোড:
functionisDOMNodeNotPresent(node){// ...}if(!isDOMNodeNotPresent(node)){// ...}
ভালো কোড:
functionisDOMNodePresent(node){// ...}if(isDOMNodePresent(node)){// ...}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
শুরুতে এটা অসম্ভব মনে হতে পারে। বেশিরভাগ মানুষ এটা শুনেই বলবে, "If স্টেটমেন্ট ছাড়া আমি কিভাবে কিছু করবো?" এটার উত্তর হল, আমরা "Polymorphism" ব্যবহার করে এটা করতে পারি। তখন তারা জিজ্ঞেস করবে, "এটা আমি কেন করব?" এটার উত্তর হল, আমরা এতক্ষণ যে ক্লিন কোড এর নিয়মনীতি পরে আসলাম তার মধ্যে একটা, "একটা ফাংশন শুধু একটা কাজ করবে"। যখনই তোমার ফাংশনে if থাকবে তার মানেই হল তোমার ফাংশন একাধিক কাজ করছে।
খারাপ কোড:
classAirplane{// ...getCruisingAltitude(){switch(this.type){case"777":returnthis.getMaxAltitude()-this.getPassengerCount();case"Air Force One":returnthis.getMaxAltitude();case"Cessna":returnthis.getMaxAltitude()-this.getFuelExpenditure();}}}
ভালো কোড:
classAirplane{// ...}classBoeing777extendsAirplane{// ...getCruisingAltitude(){returnthis.getMaxAltitude()-this.getPassengerCount();}}classAirForceOneextendsAirplane{// ...getCruisingAltitude(){returnthis.getMaxAltitude();}}classCessnaextendsAirplane{// ...getCruisingAltitude(){returnthis.getMaxAltitude()-this.getFuelExpenditure();}}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
জাভাস্ক্রিপ্টে ফাংশন যেকোন টাইপের ডাটা আর্গুমেন্ট হিসেবে নিতে পারে। মাঝে মধ্যে এই স্বাধীনতাই আমাদের কাল হয়ে দাড়ায়। ফাংশনের ভিতরে টাইপ চেক করা একটা লোভনীয় কাজ। এটা এড়ানর অনেক উপায় আছে। প্রথম কথা হল একটা স্থিতিশীল API ব্যবহার করা।
খারাপ কোড:
functiontravelToTexas(vehicle){if(vehicleinstanceofBicycle){vehicle.pedal(this.currentLocation,newLocation("texas"));}elseif(vehicleinstanceofCar){vehicle.drive(this.currentLocation,newLocation("texas"));}}
ভালো কোড:
functiontravelToTexas(vehicle){vehicle.move(this.currentLocation,newLocation("texas"));}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
তুমি বেসিক প্রিমিটিভ যেমন স্ট্রিং, ইন্টিজারের ক্ষেত্রে polymorphism ব্যবহার করতে পারবে না। কিন্তু তবুও তোমার হয়ত টাইপচেকিং এর দরকার পরতে পারে। তুমি Typescript ব্যবহার করে দেখতে পার। এটা সাধারণ জাভাস্ক্রিপ্টের খুব ভালো একটা বিকল্প। এটা স্ট্যাটিক টাইপিং সাপোর্ট করে। সাধারণ জাভাস্ক্রিপ্টের প্রব্লেম হল এটায় টাইপচেকিং এর জন্য অনেক কিছু করা লাগে যেটা Typescript এ লাগবে না। আবারও বলছি Typescript সাধারণ জাভাস্ক্রিপ্টের খুব ভালো একটা বিকল্প।
খারাপ কোড:
functioncombine(val1,val2){if((typeofval1==="number"&&typeofval2==="number")||(typeofval1==="string"&&typeofval2==="string")){returnval1+val2;}thrownewError("Must be of type String or Number");}
ভালো কোড:
functioncombine(val1,val2){returnval1+val2;}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
আধুনিক ব্রাউজার পর্দার পেছনে অনেক ধরণের অপটিমাইজেশন করে। অনেক ক্ষেত্রেই তোমার অপটিমাইজেশন সময়ের অপচয়। কারণ ব্রাউজার এটা নিজেই আবারও করবে। কোথায় অপটিমাইজেশন দরকার সেটা চেক করার জন্যখুব ভালো রিসোর্স আছে। আপাতত সগুলো অপ্টিমাইজ করার চেষ্টা কর।
খারাপ কোড:
// On old browsers, each iteration with uncached `list.length` would be costly// because of `list.length` recomputation. In modern browsers, this is optimized.for(leti=0,len=list.length;i<len;i++){// ...}
ভালো কোড:
for(leti=0;i<list.length;i++){// ...}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
অব্যবহৃত কোড ডুপ্লিকেট কোড এর মতই খারাপ। তোমার কোডবেজে এগুলোকে রাখার কোন কারণ নেই। তুমি নিশ্চিন্তে অব্যবহৃত কোড ফেলে দিতে পারো। কারণ এগুলো ভার্শন হিস্টরি তে থাকবে।
খারাপ কোড:
functionoldRequestModule(url){// ...}functionnewRequestModule(url){// ...}constreq=newRequestModule;inventoryTracker("apples",req,"www.inventory-awesome.io");
ভালো কোড:
functionnewRequestModule(url){// ...}constreq=newRequestModule;inventoryTracker("apples",req,"www.inventory-awesome.io");
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
অবজেক্ট প্রপার্টি খোজার চেয়ে গেটার সেটার মেথড ব্যবহার করে অবজেক্ট এর ডাটা এক্সেস করা ভালো। তুমি জিজ্ঞেস করতে পারো, কেন? নিচে একটা লিস্ট দিয়ে দিলাম,
- যখন তুমি অবজেক্ট প্রপার্টি খোজার চেয়ে আরও বেশি কিছু করতে চাও, তখন তোমার সব এক্সেসর পরিবর্তন করা লাগবে না।
- setter মেথড ব্যবহার করলে ভ্যালিডেশন সহজ হয়।
- অন্তর্নিহিত অপ্রয়োজনীয় ডাটা আবদ্ধ থাকে
- getting এবং setting এর সময় লগ করা, এরর হ্যান্ডলিং করা সহজ হয়।
- তুমি সার্ভার থেকে ডাটা লোড করার সময় 'lazy-load' করতে পার।
খারাপ কোড:
functionmakeBankAccount(){// ...return{balance:0// ...};}constaccount=makeBankAccount();account.balance=100;
ভালো কোড:
functionmakeBankAccount(){// this one is privateletbalance=0;// a "getter", made public via the returned object belowfunctiongetBalance(){returnbalance;}// a "setter", made public via the returned object belowfunctionsetBalance(amount){// ... validate before updating the balancebalance=amount;}return{// ... getBalance, setBalance};}constaccount=makeBankAccount();account.setBalance(100);
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
এটা তুমি 'closures' ব্যবহার করে করতে পার(ES5 এবং তার নিচে)
খারাপ কোড:
constEmployee=function(name){this.name=name;};Employee.prototype.getName=functiongetName(){returnthis.name;};constemployee=newEmployee("John Doe");console.log(`Employee name:${employee.getName()}`);// Employee name: John Doedeleteemployee.name;console.log(`Employee name:${employee.getName()}`);// Employee name: undefined
ভালো কোড:
functionmakeEmployee(name){return{getName(){returnname;}};}constemployee=makeEmployee("John Doe");console.log(`Employee name:${employee.getName()}`);// Employee name: John Doedeleteemployee.name;console.log(`Employee name:${employee.getName()}`);// Employee name: John Doe
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
ES5 এর ক্লাস ব্যবহার করে সুপাঠ্য ক্লাস ইনহেরিটেন্স, কন্সট্রাকশন, মেথড লেখা খুবই কঠিন। তোমার যদি ইনহেরিটেন্স দরকার হয়, সেক্ষেত্রে ES2015/ES6 ক্লাসকে প্রাধান্য দাও। তবে যতক্ষণ না তোমার বড় এবং জটিল অবজেক্ট প্রয়োজন না হচ্ছে ততক্ষণ পর্যন্ত ছোট ফাংশন ব্যবহার কর।
খারাপ কোড:
constAnimal=function(age){if(!(thisinstanceofAnimal)){thrownewError("Instantiate Animal with `new`");}this.age=age;};Animal.prototype.move=functionmove(){};constMammal=function(age,furColor){if(!(thisinstanceofMammal)){thrownewError("Instantiate Mammal with `new`");}Animal.call(this,age);this.furColor=furColor;};Mammal.prototype=Object.create(Animal.prototype);Mammal.prototype.constructor=Mammal;Mammal.prototype.liveBirth=functionliveBirth(){};constHuman=function(age,furColor,languageSpoken){if(!(thisinstanceofHuman)){thrownewError("Instantiate Human with `new`");}Mammal.call(this,age,furColor);this.languageSpoken=languageSpoken;};Human.prototype=Object.create(Mammal.prototype);Human.prototype.constructor=Human;Human.prototype.speak=functionspeak(){};
ভালো কোড:
classAnimal{constructor(age){this.age=age;}move(){/* ... */}}classMammalextendsAnimal{constructor(age,furColor){super(age);this.furColor=furColor;}liveBirth(){/* ... */}}classHumanextendsMammal{constructor(age,furColor,languageSpoken){super(age,furColor);this.languageSpoken=languageSpoken;}speak(){/* ... */}}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
জাভাস্ক্রিপ্টের এই প্যাটার্নটি খুব উপকারী এবং তুমি অনেক লাইব্রেরি যেমন jQuery, Lodash এ এরকম প্যাটার্ন দেখতে পাবে। এটা তোমার কোড কে অনেক বেশি সহজবোধ্য করে তুলবে। এই জন্য বলি, মেথড চেইনিং ব্যবহার কর এবং দেখ তোমার কোড আগের থেকে অনেক পরিষ্কার হবে। তোমার ক্লাস মেথডের শেষে this রিটার্ন করলেই হবে। তারপর তুমি সেই ফাংশনেও মেথড চেইনিং করতে পারবে।
খারাপ কোড:
classCar{constructor(make,model,color){this.make=make;this.model=model;this.color=color;}setMake(make){this.make=make;}setModel(model){this.model=model;}setColor(color){this.color=color;}save(){console.log(this.make,this.model,this.color);}}constcar=newCar("Ford","F-150","red");car.setColor("pink");car.save();
ভালো কোড:
classCar{constructor(make,model,color){this.make=make;this.model=model;this.color=color;}setMake(make){this.make=make;// NOTE: Returning this for chainingreturnthis;}setModel(model){this.model=model;// NOTE: Returning this for chainingreturnthis;}setColor(color){this.color=color;// NOTE: Returning this for chainingreturnthis;}save(){console.log(this.make,this.model,this.color);// NOTE: Returning this for chainingreturnthis;}}constcar=newCar("Ford","F-150","red").setColor("pink").save();
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
The gang of Four এর লেখাডিজাইন প্যাটার্ন্স বইয়ে খুব জনপ্রিয় একটি কথা হল, "যখনই পারবে তখনই ইনহেরিটেন্স থেকে কম্পোজিশন কে বেশি গুরুত্ব দিবে"। ইনহেরিটেন্স ব্যবহারের এবং কম্পোজিশন ব্যবহারের অনেক ভালো কারণ রয়েছে। মোদ্দা কথা হল, তুমি যদি স্বতঃস্ফূর্ত ভাবেই চিন্তা কর যে ইনহেরিটেন্স তোমার সমস্যার সমাধা করবে তবে আরেকবার ভাব যে কম্পোজিশন দিয়ে কাজ টা করা যায় কিনা। অনেক ক্ষেত্রেই তুমি পারবে,
তুমি হয়ত ভাবতে পার, "তাহলে ইনহেরিটেন্স কখন ব্যবহার করব"? এটা নির্ভর করে তোমার প্রব্লেম এর উপর। যেসব ক্ষেত্রে ইনহেরিটেন্স কম্পজিশন্ থেকে ভালো নিচে তার একটা লিস্ট দিলাম,
- তোমার ইনহেরিটেন্স এ "has-a" এর বদলে "is-a" সম্পর্ক বিদ্যমান। (মানুষ>প্রানি vs. ব্যবহারকারী>ব্যবহারকারির তথ্য )
- তুমি বেজ ক্লাস থেকে কোড পুনরায় ব্যবহার করতে পারলে(human can move like all animals)।
- তুমি চাইল্ড ক্লাসে গ্লোবাল পরিবর্তন আনতে চাও বেজ ক্লাসে পরিবর্তনের মাধ্যমে। (Change the caloric expenditure of all animals when they move).
খারাপ কোড:
classEmployee{constructor(name,email){this.name=name;this.email=email;}// ...}// Bad because Employees "have" tax data. EmployeeTaxData is not a type of EmployeeclassEmployeeTaxDataextendsEmployee{constructor(ssn,salary){super();this.ssn=ssn;this.salary=salary;}// ...}
ভালো কোড:
classEmployeeTaxData{constructor(ssn,salary){this.ssn=ssn;this.salary=salary;}// ...}classEmployee{constructor(name,email){this.name=name;this.email=email;}setTaxData(ssn,salary){this.taxData=newEmployeeTaxData(ssn,salary);}// ...}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
ক্লিন কোড বইয়ে যেমনটা বলা হয়েছে, "একটা ক্লাস এ পরিবর্তনের জন্য একটার বেশি কারন থাকা উচিত না"। ফ্লাইটে একটা মাত্র সুটকেস নেয়ার মত, একটা ক্লাসে অনেক ফাংশন ঢুকিয়ে দেয়াটা খুব লোভনীয়। একটা ক্লাস পরিবর্তন করার হার কমাতে পারাটা গুরুত্বপুর্ন । তুমি যদি ক্লাসে অনেক ফাংশন রাখ, তবে একটা পরিবর্তন কোথায় কিভাবে প্রভাব ফেলছে বুঝতে পারা কঠিন হয়ে যায়।
খারাপ কোড:
classUserSettings{constructor(user){this.user=user;}changeSettings(settings){if(this.verifyCredentials()){// ...}}verifyCredentials(){// ...}}
ভালো কোড:
classUserAuth{constructor(user){this.user=user;}verifyCredentials(){// ...}}classUserSettings{constructor(user){this.user=user;this.auth=newUserAuth(user);}changeSettings(settings){if(this.auth.verifyCredentials()){// ...}}}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
বারট্রান্ড মেয়ার এর মতে, "সফটওয়ারের ইউনিট(ক্লাস, মডিউলস, ফাংশন্স ইত্যাদি) গুলো এক্সটেন্ড করার জন্য খোলা থাকতে হবে , কিন্তু মডিফিকেশনের জন্য বন্ধ থাকতে হবে" । এটা আসলে কি বুঝায়? এটা দিয়ে আসলে বুঝায়, তোমার ব্যবহারকারীকে নতুন ফাংশন যোগ করতে দিতে হবে, কিন্তু যে ফাংশন আগে থেকে আছে সেগুলো পরিবর্তন করা আটকাতে হবে।
খারাপ কোড:
classAjaxAdapterextendsAdapter{constructor(){super();this.name="ajaxAdapter";}}classNodeAdapterextendsAdapter{constructor(){super();this.name="nodeAdapter";}}classHttpRequester{constructor(adapter){this.adapter=adapter;}fetch(url){if(this.adapter.name==="ajaxAdapter"){returnmakeAjaxCall(url).then(response=>{// transform response and return});}elseif(this.adapter.name==="nodeAdapter"){returnmakeHttpCall(url).then(response=>{// transform response and return});}}}functionmakeAjaxCall(url){// request and return promise}functionmakeHttpCall(url){// request and return promise}
ভালো কোড:
classAjaxAdapterextendsAdapter{constructor(){super();this.name="ajaxAdapter";}request(url){// request and return promise}}classNodeAdapterextendsAdapter{constructor(){super();this.name="nodeAdapter";}request(url){// request and return promise}}classHttpRequester{constructor(adapter){this.adapter=adapter;}fetch(url){returnthis.adapter.request(url).then(response=>{// transform response and return});}}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
This is a scary term for a very simple concept. It's formally defined as "If Sis a subtype of T, then objects of type T may be replaced with objects of type S(i.e., objects of type S may substitute objects of type T) without altering anyof the desirable properties of that program (correctness, task performed,etc.)." That's an even scarier definition.
The best explanation for this is if you have a parent class and a child class,then the base class and child class can be used interchangeably without gettingincorrect results. This might still be confusing, so let's take a look at theclassic Square-Rectangle example. Mathematically, a square is a rectangle, butif you model it using the "is-a" relationship via inheritance, you quicklyget into trouble.
খারাপ কোড:
classRectangle{constructor(){this.width=0;this.height=0;}setColor(color){// ...}render(area){// ...}setWidth(width){this.width=width;}setHeight(height){this.height=height;}getArea(){returnthis.width*this.height;}}classSquareextendsRectangle{setWidth(width){this.width=width;this.height=width;}setHeight(height){this.width=height;this.height=height;}}functionrenderLargeRectangles(rectangles){rectangles.forEach(rectangle=>{rectangle.setWidth(4);rectangle.setHeight(5);constarea=rectangle.getArea();// BAD: Returns 25 for Square. Should be 20.rectangle.render(area);});}constrectangles=[newRectangle(),newRectangle(),newSquare()];renderLargeRectangles(rectangles);
ভালো কোড:
classShape{setColor(color){// ...}render(area){// ...}}classRectangleextendsShape{constructor(width,height){super();this.width=width;this.height=height;}getArea(){returnthis.width*this.height;}}classSquareextendsShape{constructor(length){super();this.length=length;}getArea(){returnthis.length*this.length;}}functionrenderLargeShapes(shapes){shapes.forEach(shape=>{constarea=shape.getArea();shape.render(area);});}constshapes=[newRectangle(4,5),newRectangle(4,5),newSquare(5)];renderLargeShapes(shapes);
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
জাভাস্ক্রিপ্টে যদিও ইন্টারফেস না থাকার কারণে এই নীতি খাটে না। তবে, এটা গুরুত্বপুর্ন এবং আনুসাংগিক। ISP অনুযায়ী, " ক্লায়েন্ট যে ইন্টারফেস ব্যবহার করে না সেই ইন্টারফেস চাপিয়ে দেয়া যাবে না। "এই নীতিটি বুঝার জন্য একটা ভালো উদাহরণ হল, ক্লাসের সেটিং অবজেক্ট কে আবশ্যিক না করা। এতে করে যে সকল ক্লাসের সেটিং অবজেক্ট অনেক বড় এবং ক্লায়েন্টের সব সেটিং প্রয়োজন নেই, সেখেত্রে আমরা একটা বিশাল ইন্টারফেস বানানো থেকে বেচে যাই।
খারাপ কোড:
classDOMTraverser{constructor(settings){this.settings=settings;this.setup();}setup(){this.rootNode=this.settings.rootNode;this.animationModule.setup();}traverse(){// ...}}const$=newDOMTraverser({rootNode:document.getElementsByTagName("body"),animationModule(){}// Most of the time, we won't need to animate when traversing.// ...});
ভালো কোড:
classDOMTraverser{constructor(settings){this.settings=settings;this.options=settings.options;this.setup();}setup(){this.rootNode=this.settings.rootNode;this.setupOptions();}setupOptions(){if(this.options.animationModule){// ...}}traverse(){// ...}}const$=newDOMTraverser({rootNode:document.getElementsByTagName("body"),options:{animationModule(){}}});
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
এই মূলনীতি ২টা গুরত্তপুর্ন জিনিস বলে থাকে,
- হাই লেভেল মডিউল কখনোই লো লেভেল মডিউল এর উপরে নির্ভর করবে না।
- অ্যাবস্ট্রাকশন ডিটেইলের উপর নির্ভর করবেন না। ডিটেইল অ্যাবস্ট্রাকশনের উপরে নির্ভর করবে।
হয়ত সহজে বুঝবে না, কিন্তু তুমি যদি AngularJS ব্যবহার করে থাক, তাহলে এই মূলনীতির ব্যবহার দেখার কথা। AngularJS ডিপেন্ডেন্সি ইনজেকশনের মধ্যে এই মূলনীতির প্রয়োগ করে। যদিও ২ টা একই ধারণা না। DIP হাই লেভেল মডিউল কে লো লেভেল মডিউল সম্পর্কে জানতে বাধা দেয়। এটার অনেক বড় উপকার হল, এটা মডিউলগুলোর মধ্যে কোন বন্ধন রাখেনা। কাপলিং কোড রেফ্যাক্টরের জন্য খুব খারাপ একটা জিনিস।
আগেও বলা হয়েছে, জাভাস্ক্রিপ্টে কোন ইন্টারফেস নাই, সুতরাং, এখানে অ্যাবস্ট্রাকশন অন্তর্নিহিত কন্ট্রাক্ট এর উপর নির্ভর করে। মানে একটা ক্লাস/অবজেক্ট অন্য ক্লাস বা অবজেক্ট এর জন্য নিজের যে মেথড বা মেম্বার গুলো উন্মুক্ত করে দেয় তাদেরকে আমরা কন্ট্রাক্ট বলছি। নিচের উদাহরণে, inventoryTracker এর যেকোনো মডিউল এ requestitems মেথড থাকাটাই implicit কন্ট্রাক্ট ।
খারাপ কোড:
classInventoryRequester{constructor(){this.REQ_METHODS=["HTTP"];}requestItem(item){// ...}}classInventoryTracker{constructor(items){this.items=items;// BAD: We have created a dependency on a specific request implementation.// We should just have requestItems depend on a request method: `request`this.requester=newInventoryRequester();}requestItems(){this.items.forEach(item=>{this.requester.requestItem(item);});}}constinventoryTracker=newInventoryTracker(["apples","bananas"]);inventoryTracker.requestItems();
ভালো কোড:
classInventoryTracker{constructor(items,requester){this.items=items;this.requester=requester;}requestItems(){this.items.forEach(item=>{this.requester.requestItem(item);});}}classInventoryRequesterV1{constructor(){this.REQ_METHODS=["HTTP"];}requestItem(item){// ...}}classInventoryRequesterV2{constructor(){this.REQ_METHODS=["WS"];}requestItem(item){// ...}}// By constructing our dependencies externally and injecting them, we can easily// substitute our request module for a fancy new one that uses WebSockets.constinventoryTracker=newInventoryTracker(["apples","bananas"],newInventoryRequesterV2());inventoryTracker.requestItems();
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
প্রোডাক্ট শিপিং এর থেকে টেস্টিং গুরুত্বপুর্ন । যদি তুমি ভালভাবে টেস্ট না কর বা কম টেস্ট কর, তাহলে প্রতিবার শিপিং এর সময় তুমি নিশ্চিত থাকতে পারবে না। পর্যাপ্ত টেস্ট বলতে কি বুঝায় সেটা তোমার টিম এর উপর ডিপেন্ড করে। তবে ১০০% টেস্ট কভারেজ থাকলে তুমি অনেক বেশি আত্মবিশ্বাসী এবং ডেভেলপের হিসেবে শান্তি তে থাকতে পারবে। তার মানে হল ভালো টেস্টিং ফ্রেমওয়ার্ক এর পাশাপাশিভালো কভারেজ টুল ও প্রয়োজন।
টেস্ট না লেখার কোন অজুহাত নেই।অনেক ভালো টেস্ট ফ্রেমওয়ার্ক আছে। সুতরাং, তোমার টিম যেটা পছন্দ করে সেটা বেছে নাও। তারপর প্রতিটা নতুন ফিচার, মডিউল লেখার সময় টেস্ট লেখা নিশ্চিত কর। তুমি যদি Test Driven Development পছন্দ কর তাহলে খুব ভালো। তবে আসল কথা হল, প্রতিটি নতুন ফিচার শিপিং এর আগে টেস্ট কভারেজ নিশ্চিত করা।
খারাপ কোড:
importassertfrom"assert";describe("MomentJS",()=>{it("handles date boundaries",()=>{letdate;date=newMomentJS("1/1/2015");date.addDays(30);assert.equal("1/31/2015",date);date=newMomentJS("2/1/2016");date.addDays(28);assert.equal("02/29/2016",date);date=newMomentJS("2/1/2015");date.addDays(28);assert.equal("03/01/2015",date);});});
ভালো কোড:
importassertfrom"assert";describe("MomentJS",()=>{it("handles 30-day months",()=>{constdate=newMomentJS("1/1/2015");date.addDays(30);assert.equal("1/31/2015",date);});it("handles leap year",()=>{constdate=newMomentJS("2/1/2016");date.addDays(28);assert.equal("02/29/2016",date);});it("handles non-leap year",()=>{constdate=newMomentJS("2/1/2015");date.addDays(28);assert.equal("03/01/2015",date);});});
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
কলব্যাক মেথড অনেক বেশি নেস্টিং তৈরি করে। ES2015/ES6 এর সাথে প্রমিস গ্লোবাল টাইপ হিসেবে দেয়া আছে, সেটা ব্যবহার কর।
খারাপ কোড:
import{get}from"request";import{writeFile}from"fs";get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin",(requestErr,response,body)=>{if(requestErr){console.error(requestErr);}else{writeFile("article.html",body,writeErr=>{if(writeErr){console.error(writeErr);}else{console.log("File written");}});}});
ভালো কোড:
import{get}from"request-promise";import{writeFile}from"fs-extra";get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin").then(body=>{returnwriteFile("article.html",body);}).then(()=>{console.log("File written");}).catch(err=>{console.error(err);});
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
কলব্যাকের তুলনায় প্রমিস অনেক ভাল। কিন্তু ES2017/ES8 এর সাথে asnyc এবং await আছে, যেটা আরও ভালো সমাধান দিতে পারে। তোমার শুধু ফাংশনের আগে async ব্যবহার করতে হবে। যদি ES2017/ES8 এর সুবিধা নিতে পার তাহলে আজ থেকেই async এবং await ব্যবহার কর।
খারাপ কোড:
import{get}from"request-promise";import{writeFile}from"fs-extra";get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin").then(body=>{returnwriteFile("article.html",body);}).then(()=>{console.log("File written");}).catch(err=>{console.error(err);});
ভালো কোড:
import{get}from"request-promise";import{writeFile}from"fs-extra";asyncfunctiongetCleanCodeArticle(){try{constbody=awaitget("https://en.wikipedia.org/wiki/Robert_Cecil_Martin");awaitwriteFile("article.html",body);console.log("File written");}catch(err){console.error(err);}}getCleanCodeArticle()
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
এরর থ্রু করা একটি ভাল জিনিশ। তার মানে হল তোমার রানটাইম বুঝতে পেরেছে যে কিছু একটা সমস্যা আছে।
এরর পেলে সেটা নিয়ে কিছু না করলে তুমি এরর এর কারণ বের করতে পারবে না এবং তার সমাধান ও দিতে পারবে না। কনসোলে এরর লগ করা তেমন ভাল কোন সমাধান না। এটা কনসোলে প্রিন্ট হওয়া হাজারও জিনিসের ভিড়ে হারিয়ে যেতে পারে। যদি তুমি তোমার কোড কে try/catch ব্লক এ আবদ্ধ কর তার মানে হল তুমি অনুমান করতে পারছ এখানে একটা এরর হতে পারে। সুতরাং সেক্ষেত্রে তোমার একটা পরিকল্পনা থাকা উচিত।
খারাপ কোড:
try{functionThatMightThrow();}catch(error){console.log(error);}
ভালো কোড:
try{functionThatMightThrow();}catch(error){// One option (more noisy than console.log):console.error(error);// Another option:notifyUserOfError(error);// Another option:reportErrorToService(error);// OR do all three!}
যেকারনে try/catch এ ধরা এরর কে অবজ্ঞা করবে না। একই কারণে রিজেক্টেড প্রমিসকেও অবজ্ঞা করবে না।
খারাপ কোড:
getdata().then(data=>{functionThatMightThrow(data);}).catch(error=>{console.log(error);});
ভালো কোড:
getdata().then(data=>{functionThatMightThrow(data);}).catch(error=>{// One option (more noisy than console.log):console.error(error);// Another option:notifyUserOfError(error);// Another option:reportErrorToService(error);// OR do all three!});
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
ফরম্যাটিং একটা সাব্জেটিভ ব্যপার। এখানে অন্যে রুলের মতই ফরম্যাটিং এর ও কোন ধরাবাঁধা নিয়ম নেই। আসল কথা হল, কখনো ফরম্যাটিং নিয়ে তর্ক করবে না।অনেক টুলসআছে এই কাজ টা অটোম্যাট করার জন্য। যেকোনো একটা ব্যবহার কর। ফরম্যাটিং নিয়ে তর্ক করা সময়ের অপচয় করা ছাড়া আর কিছু না।
যেগুলো অটো ফরম্যাটিং এর আওতায় পরে না সেগুলোর জন্য নিচে কিছু নিয়ম দেয়া আছে।
যেহেতু জাভাস্ক্রিপ্ট আন্টাইপড ল্যাঙ্গুয়েজ, তাই যথাযথ ক্যাপিটালাইজেশন ভ্যারিয়েবল সম্পর্কে তথ্য দিতে পারে। এই নিয়মগুলোও সাব্জেক্টিভ, তাই তোমাদের টিম এর সুবিধা মত একটা বেছে নিলেই হবে। তবে যেটাই বেছে নাও না কেন সেটাতেই স্থির থেকো।
খারাপ কোড:
constDAYS_IN_WEEK=7;constdaysInMonth=30;constsongs=["Back In Black","Stairway to Heaven","Hey Jude"];constArtists=["ACDC","Led Zeppelin","The Beatles"];functioneraseDatabase(){}functionrestore_database(){}classanimal{}classAlpaca{}
ভালো কোড:
constDAYS_IN_WEEK=7;constDAYS_IN_MONTH=30;constSONGS=["Back In Black","Stairway to Heaven","Hey Jude"];constARTISTS=["ACDC","Led Zeppelin","The Beatles"];functioneraseDatabase(){}functionrestoreDatabase(){}classAnimal{}classAlpaca{}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
যদি একটা ফাংশন অন্য একটাকে কল করে, তবে চেষ্টা করবে এদেরকে পাশাপাশি রাখতে।
খারাপ কোড:
classPerformanceReview{constructor(employee){this.employee=employee;}lookupPeers(){returndb.lookup(this.employee,"peers");}lookupManager(){returndb.lookup(this.employee,"manager");}getPeerReviews(){constpeers=this.lookupPeers();// ...}perfReview(){this.getPeerReviews();this.getManagerReview();this.getSelfReview();}getManagerReview(){constmanager=this.lookupManager();}getSelfReview(){// ...}}constreview=newPerformanceReview(employee);review.perfReview();
ভালো কোড:
classPerformanceReview{constructor(employee){this.employee=employee;}perfReview(){this.getPeerReviews();this.getManagerReview();this.getSelfReview();}getPeerReviews(){constpeers=this.lookupPeers();// ...}lookupPeers(){returndb.lookup(this.employee,"peers");}getManagerReview(){constmanager=this.lookupManager();}lookupManager(){returndb.lookup(this.employee,"manager");}getSelfReview(){// ...}}constreview=newPerformanceReview(employee);review.perfReview();
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
কমেন্ট হচ্ছে একধরনের ক্ষমা প্রার্থনা। কারণ ভালবেশিরভাগ ক্ষেত্রেই নিজেই নিজের ডকুমেন্ট হিসবে কাজ করে, কমেন্ট করার দরকার পরে না।
খারাপ কোড:
functionhashIt(data){// The hashlethash=0;// Length of stringconstlength=data.length;// Loop through every character in datafor(leti=0;i<length;i++){// Get character code.constchar=data.charCodeAt(i);// Make the hashhash=(hash<<5)-hash+char;// Convert to 32-bit integerhash&=hash;}}
ভালো কোড:
functionhashIt(data){lethash=0;constlength=data.length;for(leti=0;i<length;i++){constchar=data.charCodeAt(i);hash=(hash<<5)-hash+char;// Convert to 32-bit integerhash&=hash;}}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
ভার্শন কন্ট্রোল তৈরি হওয়ার একটা কারণ আছে। পুরাতন কোড তোমার হিস্টোরিতেই থাকবে, কমেন্ট করে রাখার দরকার নেই।
খারাপ কোড:
doStuff();// doOtherStuff();// doSomeMoreStuff();// doSoMuchStuff();
ভালো কোড:
doStuff();
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
মনে রাখবে, ভার্শন কন্ট্রোল ব্যবহার করতে হবে।git log
ব্যবহার কর হিস্টোরি দেখতে।
খারাপ কোড:
/** * 2016-12-20: Removed monads, didn't understand them (RM) * 2016-10-01: Improved using special monads (JP) * 2016-02-03: Removed type-checking (LI) * 2015-03-14: Added combine with type-checking (JR) */functioncombine(a,b){returna+b;}
ভালো কোড:
functioncombine(a,b){returna+b;}
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
এগুলো আসলে শুধু সমস্যা তৈরি করে।
খারাপ কোড:
////////////////////////////////////////////////////////////////////////////////// Scope Model Instantiation////////////////////////////////////////////////////////////////////////////////$scope.model={menu:"foo",nav:"bar"};////////////////////////////////////////////////////////////////////////////////// Action setup////////////////////////////////////////////////////////////////////////////////constactions=function(){// ...};
ভালো কোড:
$scope.model={menu:"foo",nav:"bar"};constactions=function(){// ...};
⬆ উপরে ফিরে যেতে এখানে ক্লিক করতে হবে
This is also available in other languages:
French:GavBaros/clean-code-javascript-fr
Brazilian Portuguese:fesnt/clean-code-javascript
Spanish:andersontr15/clean-code-javascript
Spanish:tureey/clean-code-javascript
Simplified Chinese:
Traditional Chinese:AllJointTW/clean-code-javascript
German:marcbruederlin/clean-code-javascript
Korean:qkraudghgh/clean-code-javascript-ko
Polish:greg-dev/clean-code-javascript-pl
Russian:
Vietnamese:hienvd/clean-code-javascript/
Japanese:mitsuruog/clean-code-javascript/
Indonesia:andirkh/clean-code-javascript/
Italian:frappacchio/clean-code-javascript/
Bangla(বাংলা):InsomniacSabbir/clean-code-javascript/
About
🛁 Clean Code concepts adapted for JavaScript
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Packages0
Languages
- JavaScript100.0%