আজকের পোস্টে, আমরা ফ্যাকাড নামে একটি সফ্টওয়্যার ডিজাইন প্যাটার্নের সন্ধান করব। যখন আমি প্রথম এটি গ্রহণ করি, এটি কিছুটা বিশ্রী মনে হয়েছিল, কিন্তু আমি যত বেশি এটি আমার রেল অ্যাপগুলিতে ব্যবহার করেছি, ততই আমি এর উপযোগিতাকে উপলব্ধি করতে শুরু করেছি। আরও গুরুত্বপূর্ণ, এটি আমাকে আমার কোডটি আরও পুঙ্খানুপুঙ্খভাবে পরীক্ষা করার অনুমতি দিয়েছে, আমার কন্ট্রোলারগুলি পরিষ্কার করতে, আমার দৃষ্টিভঙ্গির মধ্যে যুক্তি কমাতে এবং একটি অ্যাপ্লিকেশনের কোডের সামগ্রিক কাঠামো সম্পর্কে আমাকে আরও স্পষ্টভাবে ভাবতে সাহায্য করেছে৷
একটি সফ্টওয়্যার ডেভেলপমেন্ট প্যাটার্ন হওয়ার কারণে, সম্মুখভাগটি ফ্রেমওয়ার্ক অজ্ঞেয়বাদী কিন্তু আমি এখানে যে উদাহরণগুলি প্রদান করব তা হল রুবি অন রেলের জন্য। যাইহোক, আমি আপনাকে এই নিবন্ধটি পড়ার জন্য উত্সাহিত করি এবং আপনি যে ফ্রেমওয়ার্ক ব্যবহার করছেন তা নির্বিশেষে সেগুলি চেষ্টা করে দেখুন। আমি নিশ্চিত যে একবার আপনি এই প্যাটার্নের সাথে পরিচিত হয়ে গেলে, আপনি আপনার কোডবেসের অনেক অংশে এটি ব্যবহার করার সুযোগ দেখতে শুরু করবেন৷
আর কোনো ঝামেলা ছাড়াই, আসুন সরাসরি ভিতরে ঢুকি!
MVC প্যাটার্নের সমস্যা
MVC (মডেল-ভিউ-কন্ট্রোলার) প্যাটার্ন হল একটি সফ্টওয়্যার ডেভেলপমেন্ট প্যাটার্ন যা 1970 এর দশকের। এটি সফ্টওয়্যার ইন্টারফেস ডিজাইন করার জন্য একটি যুদ্ধ-পরীক্ষিত সমাধান, প্রোগ্রামিং উদ্বেগকে তিনটি প্রধান গোষ্ঠীতে বিভক্ত করে যা একে অপরের মধ্যে একটি অনন্য উপায়ে যোগাযোগ করে৷
অনেক বড় ওয়েব ফ্রেমওয়ার্ক 2000 এর দশকের গোড়ার দিকে তাদের ভিত্তি হিসাবে MVC প্যাটার্নের সাথে আবির্ভূত হয়েছিল। স্প্রিং (জাভার জন্য), জ্যাঙ্গো (পাইথনের জন্য) এবং রুবি অন রেল (রুবির জন্য), তাদের মূল অংশে আন্তঃসংযুক্ত উপাদানগুলির এই ট্রিনিটি দিয়ে তৈরি করা হয়েছিল। সফ্টওয়্যার ব্যবহার করেনি এমন সফ্টওয়্যার থেকে প্রাপ্ত স্প্যাগেটি-কোডের তুলনায়, এমভিসি প্যাটার্নটি সফ্টওয়্যার বিকাশ এবং ইন্টারনেট উভয়ের বিবর্তনে একটি বিশাল অর্জন এবং টার্নিং পয়েন্ট ছিল৷
সংক্ষেপে, মডেল-ভিউ-কন্ট্রোলার প্যাটার্ন নিম্নলিখিতগুলির জন্য অনুমতি দেয়:একজন ব্যবহারকারী ভিউতে একটি ক্রিয়া সম্পাদন করে। ভিউ একটি কন্ট্রোলারের কাছে একটি অনুরোধ ট্রিগার করে যা সম্ভাব্যভাবে একটি মডেল তৈরি/পড়/আপডেট বা মুছে ফেলতে পারে। মডেল লেনদেন কন্ট্রোলারের কাছে ফিরে আসে, যা কিছু পরিবর্তন রেন্ডার করে যা ব্যবহারকারী ভিউতে প্রতিফলিত দেখতে পাবে।
এই প্রোগ্রামিং প্যাটার্নের প্রচুর সুবিধা রয়েছে। কিছু তালিকা করতে:
- এটি উদ্বেগকে আলাদা করে কোড রক্ষণাবেক্ষণযোগ্যতা উন্নত করে
- এটি বৃহত্তর পরীক্ষাযোগ্যতার জন্য অনুমতি দেয় (মডেল, ভিউ এবং কন্ট্রোলারগুলি বিচ্ছিন্নভাবে পরীক্ষা করা যেতে পারে)
- এটি SOLID-এর একক দায়িত্ব নীতি প্রয়োগ করে ভাল কোডিং অনুশীলনকে উৎসাহিত করে:"একটি শ্রেণির পরিবর্তনের শুধুমাত্র একটি কারণ থাকা উচিত।"
তার সময়ের জন্য একটি অসাধারণ কৃতিত্ব, বিকাশকারীরা শীঘ্রই বুঝতে পেরেছিলেন যে MVC প্যাটার্নটিও কিছুটা সীমাবদ্ধ ছিল। এইচএমভিসি (হায়ারার্কিক্যাল মডেল–ভিউ–কন্ট্রোলার), এমভিএ (মডেল–ভিউ–অ্যাডাপ্টার), এমভিপি (মডেল–ভিউ–উপস্থাপক), এমভিভিএম (মডেল–ভিউ–ভিউ-মডেল) এবং অন্যান্যের মতো বৈকল্পিকগুলি আবির্ভূত হতে শুরু করেছে, যা সকলেই চেয়েছিল MVC প্যাটার্নের সীমাবদ্ধতাগুলিকে সম্বোধন করুন৷
৷MVC প্যাটার্ন যে সমস্যাগুলি প্রবর্তন করে এবং আজকের নিবন্ধের বিষয়বস্তু হল তার মধ্যে একটি হল:জটিল ভিউ লজিক পরিচালনার জন্য কে দায়ী? ভিউটি কেবল ডেটা উপস্থাপনের সাথে সম্পর্কিত হওয়া উচিত, নিয়ামকটি কেবল মডেল থেকে প্রাপ্ত বার্তাটি রিলে করছে এবং মডেলটি কোনও ভিউ লজিকের সাথে উদ্বিগ্ন হওয়া উচিত নয়৷
এই সাধারণ সমস্যায় সাহায্য করার জন্য, সমস্ত রেল অ্যাপ্লিকেশন একটি helpers
দিয়ে শুরু করা হয় ডিরেক্টরি helper
ডিরেক্টরিতে এমন পদ্ধতি সহ মডিউল থাকতে পারে যা জটিল ভিউ লজিকে সহায়তা করে।
এখানে একটি রেল অ্যাপ্লিকেশনের মধ্যে একজন সাহায্যকারীর একটি উদাহরণ রয়েছে:
app/helpers/application_helper.rb
module ApplicationHelper
def display_ad_type(advertisement)
type = advertisement.ad_type
case type
when 'foo'
content_tag(:span, class: "foo ad-#{type}") { type }
when 'bar'
content_tag(:p, 'bar advertisement')
else
content_tag(:span, class: "badge ads-badge badge-pill ad-#{type}") { type }
end
end
end
এই উদাহরণটি সহজ কিন্তু এই সত্যটি দেখায় যে আপনি টেমপ্লেট থেকে এই ধরনের সিদ্ধান্ত গ্রহণ করতে চান যাতে এর জটিলতা কম হয়৷
সাহায্যকারীরা চমৎকার, কিন্তু জটিল ভিউ লজিক পরিচালনা করার জন্য আরও একটি প্যাটার্ন রয়েছে যা বছরের পর বছর ধরে গৃহীত হয়েছে, এবং সেটি হল ফ্যাকাড প্যাটার্ন৷
ফেসেড প্যাটার্নের ভূমিকা
একটি Ruby on Rails অ্যাপ্লিকেশনে, সম্মুখভাগগুলি সাধারণত app/facades
-এর মধ্যে স্থাপন করা হয় ডিরেক্টরি।
যদিও helpers
অনুরূপ , facades
একটি মডিউল মধ্যে পদ্ধতি একটি গ্রুপ নয়. একটি ফ্যাকেড হল একটি পোরো (প্লেইন ওল্ড রুবি অবজেক্ট) যা কন্ট্রোলারের মধ্যে ইনস্ট্যান্ট করা হয়, কিন্তু একটি যা বিস্তৃত ভিউ বিজনেস লজিক পরিচালনা করে। যেমন, এটি নিম্নলিখিত সুবিধার অনুমতি দেয়:
-
UsersHelper
-এর জন্য একটি মডিউল থাকার পরিবর্তে অথবাArticlesHelper
অথবাBooksHelper
, প্রতিটি কন্ট্রোলার অ্যাকশনের নিজস্ব ফ্যাকাড থাকতে পারে:Users::IndexFacade
,Articles::ShowFacade
,Books::EditFacade
. - মডিউলের চেয়েও বেশি, একক দায়িত্ব নীতি প্রয়োগ করা হয়েছে তা নিশ্চিত করার জন্য আপনাকে সম্মুখভাগগুলি নেস্ট করার অনুমতি দিয়ে ভাল কোডিং অনুশীলনগুলিকে উত্সাহিত করে৷ যদিও আপনি সম্ভবত শত শত স্তরের গভীরে নেস্ট করা সম্মুখভাগগুলি চান না, উন্নত রক্ষণাবেক্ষণযোগ্যতা এবং পরীক্ষার কভারেজের জন্য এক বা দুই স্তরের নেস্টিং থাকা একটি ভাল জিনিস হতে পারে৷
এখানে একটি কল্পিত উদাহরণ:
module Books
class IndexFacade
attr_reader :books, :params, :user
def initialize(user:, params:)
@params = params
@user = user
@books = user.books
end
def filtered_books
@filtered_books ||= begin
scope = if query.present?
books.where('name ILIKE ?', "%#{query}%")
elsif isbn.present?
books.where(isbn: isbn)
else
books
end
scope.order(created_at: :desc).page(params[:page])
end
end
def recommended
# We have a nested facade here.
# The `Recommended Books` part of the view has a
# single responsibility so best to extract it
# to improve its encapsulation and testability.
@recommended ||= Books::RecommendedFacade.new(
books: books,
user: user
)
end
private
def query
@query ||= params[:query]
end
def isbn
@isbn ||= params[:isbn]
end
end
end
ফেসেড প্যাটার্ন কখন ব্যবহার করবেন না
আসুন একটি মুহূর্ত সময় নিয়ে ভাবি যে সম্মুখভাগগুলি কী নয়৷
৷-
সম্মুখভাগগুলি এমন ক্লাসগুলিতে স্থাপন করা উচিত নয় যেগুলি বাস করে, উদাহরণস্বরূপ,
lib
-এ৷ কোডের জন্য ডিরেক্টরি যা ভিউতে প্রদর্শিত হতে হবে। সম্মুখভাগের জীবনচক্র কন্ট্রোলার অ্যাকশনে তৈরি করা উচিত এবং এর সাথে সম্পর্কিত ভিউতে ব্যবহার করা উচিত। -
CRUD অ্যাকশনগুলি সঞ্চালনের জন্য ব্যবসায়িক যুক্তির জন্য সম্মুখভাগগুলি ব্যবহার করার উদ্দেশ্যে নয় (এর জন্য অন্যান্য প্যাটার্ন রয়েছে, যেমন পরিষেবা বা ইন্টারঅ্যাক্টর - তবে এটি অন্য দিনের জন্য একটি বিষয়।) অন্য কথায়, ফ্যাকাডগুলি তৈরির সাথে সম্পর্কিত হওয়া উচিত নয়, আপডেট করা বা মুছে ফেলা। তাদের লক্ষ্য হল ভিউ বা কন্ট্রোলার থেকে জটিল উপস্থাপনা যুক্তি বের করা এবং সেই সমস্ত তথ্য অ্যাক্সেস করার জন্য একটি একক ইন্টারফেস অফার করা৷
-
শেষ কিন্তু অন্তত না, Facades একটি রূপালী বুলেট নয়. তারা আপনাকে MVC প্যাটার্ন বাইপাস করার অনুমতি দেয় না, বরং তারা এটির সাথে খেলতে পারে। যদি একটি মডেলে একটি পরিবর্তন ঘটে, তবে তা অবিলম্বে ভিউতে প্রতিফলিত হবে না। MVC-এর ক্ষেত্রে সবসময়ের মতো, কন্ট্রোলার অ্যাকশনটিকে পুনরায় রেন্ডার করতে হবে যাতে ফ্যাকাডে ভিউতে পরিবর্তন দেখা যায়।
নিয়ন্ত্রক সুবিধাগুলি
Facades এর একটি প্রধান, সুস্পষ্ট সুবিধা হল যে তারা আপনাকে নাটকীয়ভাবে কন্ট্রোলার লজিক কমাতে দেবে।
আপনার কন্ট্রোলার কোড এইরকম কিছু থেকে কমে যাবে:
class BooksController < ApplicationController
def index
@books = if params[:query].present?
current_user.books.where('name ILIKE ?', "%#{params[:query]}%")
elsif params[:isbn].present?
current_user.books.where(isbn: params[:isbn])
else
current_user.books
end
@books.order(created_at: :desc).page(params[:page])
@recommended = @books.where(some_complex_query: true)
end
end
এর জন্য:
class BooksController < ApplicationController
def index
@index_facade = Books::IndexFacade.new(user: current_user, params: params)
end
end
সুবিধা দেখুন
ভিউগুলির জন্য, ফ্যাকেডগুলি ব্যবহার করার সময় দুটি প্রধান সুবিধা রয়েছে:
- কন্ডিশনাল চেক, ইনলাইন কোয়েরি এবং অন্যান্য লজিক টেমপ্লেট থেকে সুন্দরভাবে বের করা যেতে পারে যা কোডটিকে আরও বেশি পাঠযোগ্য করে তোলে। উদাহরণস্বরূপ, আপনি এটি একটি ফর্মে ব্যবহার করতে পারেন:
<%= f.label :location %>
<%= f.select :location, options_for_select(User::LOCATION_TYPES.map { |type| [type.underscore.humanize, type] }.sort.prepend(['All', 'all'])), multiple: (current_user.active_ips.size > 1 && current_user.settings.use_multiple_locations?) %>
শুধু হয়ে যেতে পারে:
<%= f.label :location %>
<%= f.select :location, options_for_select(@form_facade.user_locations), multiple: @form_facade.multiple_locations? %>
- যে ভেরিয়েবল একাধিকবার কল করা হয় সেগুলি ক্যাশে করা যেতে পারে। এটি আপনার অ্যাপে উল্লেখযোগ্য কর্মক্ষমতা উন্নতির প্রস্তাব দিতে পারে এবং বিরক্তিকর N+1 প্রশ্নগুলি সরাতে সাহায্য করতে পারে:
// Somewhere in the view, a query is performed.
<% current_user.books.where(isbn: params[:isbn]).each do |book| %>
// Do things
<% end %>
// Somewhere else in the view, the same query is performed again.
<% current_user.books.where(isbn: params[:isbn]).each do |book| %>
// Do things
<% end %>
হয়ে যাবে:
// Somewhere in the view, a query is performed.
<% @index_facade.filtered_books.each do |book| %>
// Do things
<% end %>
// Somewhere else in the view.
// Second query is not performed due to instance variable caching.
<% @index_facade.filtered_books.each do |book| %>
// Do things
<% end %>
পরীক্ষার সুবিধাগুলি
Facades-এর একটি বড় সুবিধা হল যে তারা আপনাকে সম্পূর্ণ নিয়ামক পরীক্ষা না লিখেই ব্যবসায়িক যুক্তির একক বিট পরীক্ষা করার অনুমতি দেয়, বা আরও খারাপ, একটি ইন্টিগ্রেশন পরীক্ষা না লিখেই যা একটি প্রবাহের মধ্য দিয়ে যায় এবং একটি পৃষ্ঠায় পৌঁছায় তা নিশ্চিত করার জন্য ডেটা উপস্থাপনা প্রত্যাশিত।
যেহেতু আপনি একক PORO পরীক্ষা করবেন, এটি একটি দ্রুত টেস্ট স্যুট বজায় রাখতে সাহায্য করবে।
এখানে প্রদর্শনের উদ্দেশ্যে Minitest-এ লেখা একটি পরীক্ষার একটি সাধারণ উদাহরণ দেওয়া হল:
require 'test_helper'
module Books
class IndexFacadeTest < ActiveSupport::TestCase
attr_reader :user, :params
setup do
@user = User.create(first_name: 'Bob', last_name: 'Dylan')
@params = {}
end
test "#filtered_books returns all user's books when params are empty"
index_facade = Books::IndexFacade.new(user: user, params: params)
expectation = user.books.order(created_at: :desc).page(params[:page])
# Without writing an entire controller test or
# integration test, we can check whether using the facade with
# empty parameters will return the correct results
# to the user.
assert_equal expectation, index_facade.filtered_books
end
test "#filtered_books returns books matching a query"
@params = { query: 'Lord of the Rings' }
index_facade = Books::IndexFacade.new(user: user, params: params)
expectation = user
.books
.where('name ILIKE ?', "%#{params[:query]}%")
.order(created_at: :desc)
.page(params[:page])
assert_equal expectation, index_facade.filtered_books
end
end
end
ইউনিট টেস্টিং ফ্যাকেডগুলি পরীক্ষা স্যুটের কার্যকারিতাকে যথেষ্ট উন্নত করে, এবং প্রতিটি বড় কোম্পানি শেষ পর্যন্ত ধীরগতির টেস্ট স্যুটের সম্মুখীন হবে যদি না এই ধরনের সমস্যাগুলিকে কিছু স্তরের গুরুত্ব সহকারে সমাধান করা হয়।
এক সম্মুখভাগ, দুই সম্মুখভাগ, তিনটি সম্মুখভাগ, আরো?
আপনি এমন একটি দৃশ্যের সম্মুখীন হতে পারেন যেখানে একটি ভিউ একটি আংশিক রেন্ডার করে যা কিছু ডেটা আউটপুট করে। সেক্ষেত্রে, আপনার কাছে প্যারেন্ট ফ্যাসাড ব্যবহার করার বা নেস্টেড ফ্যাসাড ব্যবহার করার বিকল্প আছে। এটি মূলত নির্ভর করে কতটা যুক্তি জড়িত, আপনি এটি আলাদাভাবে পরীক্ষা করতে চান কিনা এবং কার্যকারিতা বের করার জন্য এটি অর্থপূর্ণ কিনা।
কতটি সম্মুখভাগ ব্যবহার করতে হবে বা একে অপরের মধ্যে কতটি সম্মুখভাগ নেস্ট করতে হবে তার কোনো সুবর্ণ নিয়ম নেই। এটি বিকাশকারীর বিবেচনার ভিত্তিতে। আমি সাধারণত কন্ট্রোলার অ্যাকশনের জন্য একটি একক সম্মুখভাগ থাকতে পছন্দ করি এবং কোড অনুসরণ করা সহজ করার জন্য আমি একটি একক স্তরে নেস্টিং সীমাবদ্ধ করি৷
এখানে কিছু সাধারণ প্রশ্ন রয়েছে যা আপনি বিকাশের সময় নিজেকে জিজ্ঞাসা করতে পারেন:
- ভিউতে আমি যে যুক্তি উপস্থাপন করার চেষ্টা করছি তা কি সম্মুখভাগটি জুড়ে দেয়?
- এই প্রেক্ষাপটে সম্মুখের মধ্যে পদ্ধতিটি কি অর্থপূর্ণ?
- কোডটি কি এখন অনুসরণ করা সহজ, নাকি অনুসরণ করা কঠিন?
সন্দেহ হলে, সর্বদা আপনার কোড অনুসরণ করা যতটা সম্ভব সহজ করার চেষ্টা করুন।
উপসংহার
উপসংহারে, কোড রক্ষণাবেক্ষণযোগ্যতা, কর্মক্ষমতা এবং পরীক্ষাযোগ্যতা উন্নত করার সাথে সাথে আপনার নিয়ন্ত্রক এবং দৃষ্টিভঙ্গিকে ঝুঁকতে রাখার জন্য সম্মুখভাগগুলি একটি দুর্দান্ত প্যাটার্ন।
যাইহোক, যে কোন প্রোগ্রামিং দৃষ্টান্তের মত, কোন সিলভার বুলেট নেই। এমনকি সাম্প্রতিক বছরগুলিতে (HMVC, MVVM, ইত্যাদি) আবির্ভূত হওয়া প্যাটার্নের সংখ্যাও সফ্টওয়্যার বিকাশের জটিলতার সর্বোত্তম সমাধান নয়৷
তাপগতিবিদ্যার দ্বিতীয় সূত্রের মতো, যা বলে যে একটি বদ্ধ সিস্টেমে এনট্রপির অবস্থা সর্বদা বাড়বে, তাই যে কোনও সফ্টওয়্যার প্রকল্পেও জটিলতা সময়ের সাথে বৃদ্ধি পায় এবং বিবর্তিত হয়। দীর্ঘমেয়াদে, লক্ষ্য হল কোড লেখা যা যতটা সম্ভব পড়া, পরীক্ষা করা, বজায় রাখা এবং অনুসরণ করা সহজ; সম্মুখভাগ ঠিক এই অফার করে।
পি.এস. আপনি যদি রুবি ম্যাজিক পোস্টগুলি প্রেস থেকে বের হওয়ার সাথে সাথে পড়তে চান তবে আমাদের রুবি ম্যাজিক নিউজলেটারে সাবস্ক্রাইব করুন এবং একটি পোস্ট মিস করবেন না!