আমার রুবি অন রেইলস প্যাটার্ন এবং অ্যান্টি-প্যাটার্ন সিরিজের এই শেষ অংশে আপনাকে স্বাগতম। এই সব টপিক নিয়ে লিখতে ও রিসার্চ করতে অনেক সময় বीतেছে। এই ব্লগ পোস্টে, আমি những সবচেয়ে সাধারণ সমস্যা গুলো নিয়ে আলোচনা করব যা আমি বছরের পর বছর রুবি অন রেইলস অ্যাপ্লিকেশন বিল্ড এবং শিপ করার সময় মোকাবিলা করেছি।
এখানে আমি যে ধারণাগুলো আলোচনা করব সেগুলো কোডের প্রায় সব জায়গায় প্রযোজ্য। তাই এগুলোকে সাধারণ ধারণা হিসেবে বিবেচনা করুন, মডেল-ভিউ-কন্ট্রোলার প্যাটার্নের সাথে সরাসরি সম্পর্কিত কিছু না। আপনি যদি রেইলস MVC-র সাথে সম্পর্কিত প্যাটার্ন এবং অ্যান্টি-প্যাটার্ন জানতে চান, তবে মডেল, ভিউ এবং কন্ট্রোলার ব্লগ পোস্টগুলো চেক আউট করতে পারেন।
তাহলে চলুন সাধারণ সমস্যাগুলো এবং উপসংহারগুলোতে ঢুকি।
স্বার্থপর অবজেক্ট এবং ডিমিটার নীতি (Law of Demeter)
ডিমিটার নীতি হল একটি হিউরিস্টিক যাকে ডিমিটার প্রজেক্টে কাজ করা একটি গোষ্ঠীর নামে নামকরণ করা হয়েছে। ধারণা হল আপনার অবজেক্টগুলো ততক্ষণ পর্যন্ত ঠিক আছে যতক্ষণ না সেগুলো একবারে এক মেথড কল করে এবং একাধিক মেথড কল চেইন করে না। প্র্যাকটিসে এর অর্থ হল নিচের মতো:
# খারাপ song.label.address # ভালো song.label_address
এখন, song অবজেক্টকে আর জানতে হবে না যে অ্যাড্রেসটি কোথা থেকে আসছে — অ্যাড্রেসটি label অবজেক্টের দায়িত্ব। আপনাকে শুধু এক মেথড কল চেইন করতে অনুমোদিত এবং আপনার অবজেক্টগুলাকে 'স্বার্থপর' (selfish) করার জন্য উৎসাহিত করা হয় যাতে সেগুলো তাদের পূর্ণ তথ্য সরাসরি শেয়ার না করে বরং হেল্পার মেথডের মাধ্যমে শেয়ার করে।
সৌভাগ্য से, রেইলসে আপনাকে প্রতিটি হেল্পার মেথড আলাদা লিখতে হয় না — আপনি delegate হেল্পার ব্যবহার করতে পারেন:
def Label < ApplicationModel belongs_to :song delegate :address, to: :song end
আপনি delegate-এ прийোজ্য অপশনগুলো নিয়ে এক্সপেরিমেন্ট করতে পারেন delegate-এর ডক্সে। কিন্তু ধারণা এবং বাস্তবায়ন খুবই সহজ। ডিমিটার নীতি প্রয়োগ করে আপনি স্ট্রাকচারাল কাপলিং কমাতে পারেন। শক্তিশালী delegate এর সাথে আপনি এটি কম লাইনে এবং ভালো অপশনসহ করতে পারেন।
ডিমিটার নীতির সাথে খুব মিলتی-जুলتی আরেকটি ধারণা হল সিঙ্গল রেসপন্সিবিলিটি প্রিন্সিপল (SRP)। এটি বলে যে একটি মডিউল, ক্লাস বা ফাংশন একটি সিস্টেমের একক অংশের জন্য দায়ী হওয়া উচিত। অন্য কথায়:
একই কারণগুলো জন্য পরিবর্তন হওয়া জিনিসগুলো একসাথে রাখুন। ভিন্ন কারণগুলো জন্য পরিবর্তন হওয়া জিনিসগুলো আলাদা রাখুন।
ফলাফলস্বরূপ লোকজন প্রায়শই SRP-র ভিন্ন ব্যাখ্যা করেন, কিন্তু মূল ধারণা হল আপনার বিল্ডিং ব্লকগুলোকে একক দায়িত্বের জন্য দায়ী রাখা। আপনার রেইলস অ্যাপ বাড়ার সাথে সাথে SRP অর্জন করা চ্যালেঞ্জিং হতে পারে, কিন্তু রিফ্যাক্টরিং করার সময় এর प्रति সচেতন থাকুন।
ফিচার যোগ করা এবং LOC বাড়ানোর সময়, আমি দেখেছি লোকজন প্রায়শই কুইক সমাধানের দিকে ঝুঁকেন। তাহলে চলুন কুইক ফিক্স গ্র্যাব করার বিষয়ে আলোচনা করি।
আমি একজনকে চিনি (আপনার ওই রুবি জেমটা কি सचমূচ দরকার?)
পুরোনো দিনগুলোতে যখন রেইলস হট টপিক ছিল, ওপেন সোর্স কোলাবোরেশনে বোমা যেন উঠেছিল, নতুন রুবি জেম যেন প্রতিটি কোণ থেকে ফুটে উঠেছিল (যেমন আজকাল সব জাভাস্ক্রিপ্ট লাইব্রেরির সাথে হচ্ছে, কিন্তু অনেক ছোট স্কেলে):
👆 Module Counts থেকে তথ্য।
不管怎么说, সাধারণ পদ্ধতি ছিল আপনার সমস্যা সমাধান করার জন্য একটি বিদ্যমান জেম খুঁজে বের করা।
এতে কোনো সমস্যা নেই, কিন্তু আপনি একটি জেম ইনস্টল করার সিদ্ধান্ত নেওয়ার আগে আমি কিছু পরামর্শ শেয়ার করতে চাই।
প্রথমে নিজে থেকে এই প্রশ্নগুলো জিজ্ঞেস করুন:
- জেমটির ফিচারগুলোর কোন অংশ আপনি ব্যবহার করবেন?
- কি অন্য কোনো সিমিলার জেম আছে যা 'সহজ' বা আরও আপ-টু-ডেট?
- আপনার দরকারি ফিচারটি আপনি কি সহজে এবং আত্মবিশ্বাসের সাথে বাস্তবায়ন করতে পারেন?
মূল্যায়ন করুন যে জেমের সারো ফিচার আপনি ব্যবহার করতে যাচ্ছেন না তবও বাস্তবায়ন করা কি ফायदेमন্দ। অথবা, যদি জেমের বাস্তবায়ন খুব জটিল হয় এবং আপনি মনে করেন আপনি আরও সহজে করতে পারেন, তবে কাস্টম সমাধান বেছে নিন।
আমি যে আরেকটি ফ্যাক্টর বিবেচনা করি তা হল জেমের রিপোজিটরি কতটা সক্রিয় — কি কোনো সক্রিয় মেইনটেইনার আছে? শেষ রিলিজ কবে হয়েছিল?
আপনার জেমের ডিপেন্ডেন্সিগুলো ও ওজার করে দেখতে হবে। আপনি একটি নির্দিষ্ট ভার্সনের ডিপেন্ডেন্সিতে লক হয়ে যেতে চান না, তাই সবসময় Gemfile.spec ফাইল চেক করুন। রুবিজেমস-এর জেম ভার্সন স্পেসিফাই করার পদ্ধতিতে পরামর্শ নিন।
জেমের কথা বলছিলাম, এর সাথে সম্পর্কিত আরেকটি ধারণা যা আমি মোকাবিলা করেছি: 'নট ইনভেন্টেড হিয়ার' (NIH) ফিনোমেনা যা রেইলস/রুবি বিশ্বে প্রযোজ্য। এর बाबতে পরবর্তী সেকশনে দেখি।
নট ইনভেন্টেড হিয়ার (হয়তো আপনার ওই রুবি জেমটা দরকার যাবতীয়?)
আমার ক্যারিয়ারে একাধিকবার আমি লোকজনের (আমারও) 'নট ইনভেন্টেড হিয়ার' সিন্ড্রোমের শিকার হওয়া দেখেছি। ধারণা 'রিইনভেন্টিং দ্য হইল' (চাকা পুনরায় আবিষ্কার করা) এর মতো। কখনো কখনো টিম ও অর্গানাইজেশন লাইব্রেরি (জেম) গুলোর ওপর বিশ্বাস করেন না যেগুলো তারা কন্ট্রোল করতে পারে না। বিশ্বাসের অভাব তাদের জন্য বিদ্যমান জেমকে পুনরায় আবিষ্কার করার ট্রিগার হতে পারে।
কখনো কখনো NIH অভিজ্ঞতা ভালো হতে পারে। ইন-হাউস সমাধান তৈরি করা দুর্দান্ত হতে পারে, বিশেষ করে যদি আপনি বাহ্যিক সমাধানগুলো থেকে উন্নত করতে পারেন। আপনি যদি সমাধানটি ওপেন সোর্স করেন, তবে তা আরও ভালো (রুবি অন রেইলস বা রিয়েক্ট-কে দেখুন)। কিন্তু যদি আপনি শুধু চাকা পুনরায় আবিষ্কার করতে চান, তবে করবেন না। চাকা নিজে থেকেই খুব ভালো।
এই টপিকটা খুব ট্রিকি, এবং আপনি যদি কখনো এমন পরিস্থিতিতে পড়েন, তবে নিজে থেকে এই প্রশ্নগুলো জিজ্ঞেস করুন:
- আমরা কি আত্মবিশ্বাসী যে আমরা বিদ্যমান সমাধানগুলোর চেয়ে ভালো সমাধান বানাতে পারি?
- যদি বিদ্যমান ওপেন সোর্স সমাধান আমাদের দরকারের সাথে ভিন্ন হয়, তবে কি আমরা ওপেন সোর্স কন্ট্রিবিউশন করে সেটি উন্নত করতে পারি?
- আরও, কি আমরা ওপেন সোর্স সমাধানটির মেইনটেইনার হয়ে উঠতে পারি এবং সম্ভবত অনেক ডেভেলপারদের জীবন উন্নত করতে পারি?
কিন্তু কখনো কখনো আপনাকে নিজের পথে যেতে হয় এবং নিজের লাইব্রেরি তৈরি করতে হয়। হয়তো আপনার অর্গানাইজেশন ওপেন সোর্স লাইব্রেরির লাইসেন্সিং পছন্দ করে না, তাই আপনি বাধ্য হয়ে নিজের বানাতে হয়। আপনি যা করুন, আমি বলব চাকা পুনরায় আবিষ্কার করা থেকে পরিহার করুন।
লাইফগার্ড ডিউটিতে (ওভার-রescuing এক্সেপশন)
লোকজন আসল টার্গেটের চেয়ে অধিক এক্সেপশন রেসকিউ করতে যান।
এই টপিকটা কোডের সাথে কিছুটা বেশি সম্পর্কিত। এটি কइয়ো কমন সেন্স হলেও, কোডে যदा কদা দেখা যায়। উদাহরণস্বরূপ:
begin song.upload_lyrics rescue puts 'Lyrics upload failed' end
যদি আমরা যে এক্সেপশন রেসকিউ করতে চাই সেটি স্পেসিফাই না করি, তবে আমরা কিছু অপ্রত্যাশিত এক্সেপশনও ক্যাচ করব।
এই কেসে, সমস্যা হতে পারে song অবজেক্টটি nil। যখন সেই এক্সেপশনটি এরর ট্র্যাকারে রিপোর্ট হবে, আপনি ভাবতে পারেন আপলোড প্রসেসে কিছু সমস্যা, কিন্তু আসলে আপনি সম্পূর্ণ ভিন্ন কিছু অনুভব করছেন।
তাই, সেফ সাইডে থাকতে, এক্সেপশন রেসকিউ করার সময় সম্ভাব্য সব এক্সেপশনের তালিকা নিশ্চিত করুন। যদি কোনো কারণে সব এক্সেপশন পাওয়া না যায়, তবে ওভার-রেসকিউ করার চেয়ে আন্ডার-রেসকিউ করা ভালো। যেসব এক্সেপশন আপনি জানেন সেগুলো রেসকিউ করুন এবং বাকিগুলো পরবর্তীতে হ্যান্ডেল করুন।
আপনি অনেক বেশি চাইছেন (খুব বেশি SQL কোয়েরি)
এই সেকশনে আমরা একটি ওয়েব ডেভেলপমেন্ট, রিলেশনাল ডেটাবেস সমস্যা নিয়ে আলোচনা করব।
আপনি একই রিকোয়েস্টে অনেক বেশি SQL কোয়েরি দিয়ে ওয়েব সার্ভারকে বোমব করছেন। এই সমস্যা কীভাবে উত্পন্ন হয়? এটি ঘটতে পারে যদি আপনি একই রিকোয়েস্টে একাধিক টেবিল থেকে একাধিক রেকর্ড ফেচ করার চেষ্টা করেন। কিন্তু যা সবচেয়ে বেশি ঘটে সেটি হল 악명 높은 N+1 কোয়েরি সমস্যা।
নিচের মডেলগুলো কল্পনা করুন:
class Song < ApplicationRecord belongs_to :artist end class Artist < ApplicationRecord has_many :songs end
যদি আমরা কিছু গান এবং তাদের আর্টিস্ট দেখাতে চাই:
songs = Song.where(genre: genre).limit(10) songs.each do |song| puts "#{song.title} by #{song.artist.name}" end
এই কোডটি ১০টি গান পেতে একটি SQL কোয়েরি ট্রিগার করবে। তারপর প্রতিটি গানের জন্য আর্টিস্ট ফেচ করার জন্য একটি এক্সট্রা SQL কোয়েরি হবে। মোট একাদশ (১১) কোয়েরি।
কল্পনা করুন যদি আমরা আরও গান লোড করি — ডেটাবেস আরও ভারী লোড নেবে সকল আর্টিস্ট পেতে।
বিকল্পভাবে, রেইলসের includes ব্যবহার করুন:
songs = Song.includes(:artists).where(genre: genre).limit(10) songs.each do |song| puts "#{song.title} by #{song.artist.name}" end
includes এর পর আমরা এখন শুধু দুটি SQL কোয়েরি পাব, গানের সংখ্যা কতটুকু হোক না কেন। কতটা সুন্দর।
টু মচ SQL কোয়েরি ডায়াগনোজ করার একটি উপায় হল ডেভেলপমেন্ট এনভায়রনমেন্টে। আপনি যদি একই টেবিল থেকে ডেটা ফেচ করার সিমিলার SQL কোয়েরির একটি গ্রুপ দেখেন, তবে সেখানে কিছু মাছি বুঝা উচিত। তাই আমি দৃঢ়ভাবে পরামর্শ দিই আপনার ডেভেলপমেন্ট এনভায়রনমেন্টে SQL লগিং অন করুন। রেইলস ভার্বোস কোয়েরি লগিংও সাপোর্ট করে যা দেখায় কোয়েরি কোথা থেকে কল হয়েছে।
লগ দেখতে না চাইলে বা আরও সিরিয়াস কিছু চাইলে, AppSignal-এর পারফরম্যান্স মেজারমেন্ট এবং N+1 কোয়েরি ডিটেকশন ট্রাই করুন। সেখানে আপনি পাবেন একটি চমৎকার ইন্ডিকেটর যে আপনার সমস্যাটি N+1 কোয়েরি থেকে আসছে কিনা। নিচে এর স্নিপেট দেওয়া হল:
সারসংক্ষেপ
এই ব্লগ পোস্ট সিরিজ পড়ার জন্য ধন্যবাদ। আমি খুশি যে আপনি এই আকর্ষণীয় যাত্রার সাথী ছিলেন, যেখানে আমরা রেইলসে প্যাটার্ন ও অ্যান্টি-প্যাটার্ন পরিচয় করিয়ে রেইলস MVC প্যাটার্নের ভিতরে সেগুলোর কী কী আছে তা এক্সপ্লোর করেছি, এবং এই ফাইনাল ব্লগ পোস্টে সাধারণ সমস্যাগুলো নিয়ে আলোচনা করেছি।
আশা করি আপনি অনেক কিছু শিখেছেন, অথবা কমপক্ষে যা জানতেন তা রিভাইজ ও প্রতিষ্ঠিত করেছেন। সব কিছু মেমোরাইজ করার চিন্তা করবেন না। আপনি যেকোনো এরিয়ায় সমস্যা হলে সিরিজটি কনসাল্ট করতে পারেন।
আপনি নিশ্চিতভাবে প্যাটার্ন এবং অ্যান্টি-প্যাটার্ন দুটোই এনকাউন্টার করবেন কারণ এই ওয়ার্ল্ড (বিশেষ করে সফটওয়্যার ইঞ্জিনিয়ারিং) আইডিয়াল নয়। এতে চিন্তিত হওয়ার কিছু নেই।
প্যাটার্ন ও অ্যান্টি-প্যাটার্ন 습득 করলে আপনি একজন চমৎকার সফটওয়্যার ইঞ্জিনিয়ার হবেন। কিন্তু যা আপনাকে আরও ভালো করে তোলে সেটি হল প্যাটার্ন ও সাঁচা ভাঙতে কখন হয় সেটি জানা, কারণ কোনো পারফেক্ট সমাধান নেই।
আবার একসাথে থাকা এবং পড়ার জন্য ধন্যবাদ। পরের পোস্টে দেখা হবে — চিয়ার্স!
P.S. আপনি যদি Ruby Magic পোস্টগুলো প্রেস থেকে বের होते সাথে সাথে পড়তে চান, আমাদের Ruby Magic নিউজলেটার সাবস্ক্রাইব করুন এবং কখনো একটি পোস্টও মিস করবেন না!