কালেবের উত্তর, যখন তিনি সঠিক পথে ছিলেন, আসলে ভুল। তার Fooক্লাস উভয় একটি ডাটাবেস সম্মুখ এবং কারখানা হিসাবে কাজ করে। এগুলি দুটি দায়িত্ব এবং একক শ্রেণিতে রাখা উচিত নয়।
বিশেষত ডাটাবেস প্রসঙ্গে এই প্রশ্নটি অনেকবার জিজ্ঞাসা করা হয়েছে। আপনার অ্যাপ্লিকেশনটিকে কম সংযুক্ত এবং আরও বহুমুখী করার জন্য আমি আপনাকে বিমূর্ত ব্যবহার (ইন্টারফেস ব্যবহার করে) ব্যবহারের সুবিধাটি পুঙ্খানুপুঙ্খভাবে দেখানোর চেষ্টা করব।
আরও পড়ার আগে, আমি আপনাকে পড়ার এবং ডিপেন্ডেন্সি ইনজেকশনটির প্রাথমিক বোঝার পরামর্শ দেওয়ার পরামর্শ দিচ্ছি , যদি আপনি এটি এখনও জানেন না। আপনি অ্যাডাপ্টারের নকশা প্যাটার্নটিও যাচাই করতে চাইতে পারেন যা মূলত ইন্টারফেসের পাবলিক পদ্ধতির পিছনে বাস্তবায়ন সম্পর্কিত বিবরণ লুকিয়ে রাখার অর্থ।
নির্ভরতা ইনজেকশন, কারখানার নকশা প্যাটার্নের সাথে মিলিত , ভিত্তি প্রস্তর এবং কৌশল নকশার প্যাটার্ন কোড করার সহজ উপায় , যা আইওসি নীতির একটি অংশ ।
আমাদের ফোন করবেন না, আমরা আপনাকে কল করব । (একে একে হলিউডের নীতি )।
বিমূর্ততা ব্যবহার করে একটি অ্যাপ্লিকেশন ডিক্লুপিং
1. বিমূর্ত স্তর তৈরি করা
আপনি যদি একটি ইন্টারফেস তৈরি করেন - বা বিমূর্ত শ্রেণি, আপনি যদি C ++ এর মতো কোনও ভাষাতে কোডিং করে থাকেন - এবং এই ইন্টারফেসে জেনেরিক পদ্ধতি যুক্ত করেন। কারণ ইন্টারফেস এবং বিমূর্ত শ্রেণি উভয়ই আপনার সরাসরি ব্যবহার করতে সক্ষম না হওয়ার আচরণ করে তবে আপনাকে সেগুলি প্রয়োগ করে (ইন্টারফেসের ক্ষেত্রে) বা প্রসারিত করতে হবে (বিমূর্ত শ্রেণির ক্ষেত্রে) কোডটি নিজেই ইতিমধ্যে পরামর্শ দেয়, আপনি নিজেই ইন্টারফেস বা বিমূর্ত শ্রেণীর দ্বারা প্রদত্ত চুক্তিটি পূর্ণাঙ্গ করতে নির্দিষ্ট প্রয়োগকরণের প্রয়োজন।
আপনার (খুব সাধারণ উদাহরণ) ডাটাবেস ইন্টারফেসটি দেখতে দেখতে এটির মতো হতে পারে (যথাক্রমে ডাটাবেসআরসাল্ট বা ডিবিকিউয়ারি ক্লাসগুলি ডাটাবেস ক্রিয়াকলাপগুলিকে উপস্থাপনকারী নিজস্ব বাস্তবায়ন হবে):
public interface Database
{
DatabaseResult DoQuery(DbQuery query);
void BeginTransaction();
void RollbackTransaction();
void CommitTransaction();
bool IsInTransaction();
}
কারণ এটি একটি ইন্টারফেস, এটি নিজেই আসলে কিছু করে না। সুতরাং আপনার এই ইন্টারফেসটি বাস্তবায়নের জন্য একটি শ্রেণি প্রয়োজন।
public class MyMySQLDatabase : Database
{
private readonly CSharpMySQLDriver _mySQLDriver;
public MyMySQLDatabase(CSharpMySQLDriver mySQLDriver)
{
_mySQLDriver = mySQLDriver;
}
public DatabaseResult DoQuery(DbQuery query)
{
// This is a place where you will use _mySQLDriver to handle the DbQuery
}
public void BeginTransaction()
{
// This is a place where you will use _mySQLDriver to begin transaction
}
public void RollbackTransaction()
{
// This is a place where you will use _mySQLDriver to rollback transaction
}
public void CommitTransaction()
{
// This is a place where you will use _mySQLDriver to commit transaction
}
public bool IsInTransaction()
{
// This is a place where you will use _mySQLDriver to check, whether you are in a transaction
}
}
এখন আপনার একটি ক্লাস রয়েছে যা প্রয়োগ করে Database, ইন্টারফেসটি কেবল কার্যকর হয়ে উঠেছে।
2. বিমূর্ত স্তর ব্যবহার করে
আপনার আবেদনের কোথাও আপনার একটি পদ্ধতি রয়েছে, আসুন SecretMethodকেবল মজাদার জন্য এই পদ্ধতিটি কল করুন এবং এই পদ্ধতির অভ্যন্তরে আপনাকে ডাটাবেসটি ব্যবহার করতে হবে কারণ আপনি কিছু তথ্য আনতে চান to
এখন আপনার একটি ইন্টারফেস রয়েছে, যা আপনি সরাসরি তৈরি করতে পারবেন না (আহ, আমি তখন এটি কীভাবে ব্যবহার করব), তবে আপনার একটি ক্লাস রয়েছে MyMySQLDatabase, যা মূলশব্দ ব্যবহার করে নির্মিত হতে পারে new।
গ্রেট! আমি একটি ডাটাবেস ব্যবহার করতে চাই, তাই আমি এটি ব্যবহার করব MyMySQLDatabase।
আপনার পদ্ধতিটি দেখতে দেখতে এটির মতো হতে পারে:
public void SecretMethod()
{
var database = new MyMySQLDatabase(new CSharpMySQLDriver());
// you will use the database here, which has the DoQuery,
// BeginTransaction, RollbackTransaction and CommitTransaction methods
}
এটা ভাল না. আপনি এই পদ্ধতির অভ্যন্তরে সরাসরি একটি শ্রেণি তৈরি করছেন, এবং আপনি যদি এটির অভ্যন্তরে এটি SecretMethodকরে থাকেন তবে 30 টি অন্যান্য পদ্ধতিতে আপনিও এটি করছেন বলে ধরে নেওয়া নিরাপদ। আপনি যদি অন্য MyMySQLDatabaseকোনও ক্লাসে যেমন MyPostgreSQLDatabaseপরিবর্তন করতে চান তবে আপনার 30 টি পদ্ধতিতে আপনাকে এটি পরিবর্তন করতে হবে।
আরেকটি সমস্যা হ'ল, যদি সৃষ্টিটি MyMySQLDatabaseব্যর্থ হয় তবে পদ্ধতিটি কখনই শেষ হবে না এবং তাই অবৈধ।
আমরা MyMySQLDatabaseপদ্ধতির পরামিতি হিসাবে এটিকে তৈরি করে পুনরায় তৈরির মাধ্যমে শুরু করি (এটি নির্ভরতা ইনজেকশন বলা হয়)।
public void SecretMethod(MyMySQLDatabase database)
{
// use the database here
}
এটি আপনাকে সমস্যার সমাধান করে, যা MyMySQLDatabaseকখনই বস্তুটি তৈরি করা যায়নি। কারণ SecretMethodকোনও বৈধ MyMySQLDatabaseঅবজেক্টের প্রত্যাশা করে , যদি কিছু ঘটে থাকে এবং অবজেক্টটি এর কাছে কখনই পাঠানো না হয়, তবে পদ্ধতিটি কখনও চালিত হবে না। এবং এটি সম্পূর্ণ সূক্ষ্ম।
কিছু অ্যাপ্লিকেশনগুলিতে এটি যথেষ্ট হতে পারে। আপনি সন্তুষ্ট হতে পারেন, তবে আসুন এটি আরও ভাল হওয়ার জন্য চুল্লী করুন।
অন্য রিফ্যাক্টরিংয়ের উদ্দেশ্য
আপনি দেখতে পারেন, এখনই SecretMethodএকটি MyMySQLDatabaseঅবজেক্ট ব্যবহার করে । ধরে নেওয়া যাক আপনি মাইএসকিউএল থেকে এমএসএসকিউএল চলে এসেছেন। আপনি আপনার ভিতরে সমস্ত যুক্তি পরিবর্তন করার মত অনুভব করেন না SecretMethod, এমন একটি পদ্ধতি যা একটি প্যারামিটার হিসাবে পাস হওয়া ভেরিয়েবলটিতে একটি BeginTransactionএবং CommitTransactionপদ্ধতিগুলি কল করে database, তাই আপনি একটি নতুন শ্রেণি তৈরি করেন MyMSSQLDatabase, যার পদ্ধতি BeginTransactionএবং CommitTransactionপদ্ধতিগুলিও থাকবে।
তারপরে আপনি এগিয়ে যান এবং SecretMethodনিম্নলিখিতটির ঘোষণাকে পরিবর্তন করুন ।
public void SecretMethod(MyMSSQLDatabase database)
{
// use the database here
}
এবং ক্লাসগুলির MyMSSQLDatabaseএবং MyMySQLDatabaseএকই পদ্ধতি থাকার কারণে আপনার আর কোনও পরিবর্তন করার দরকার নেই এবং এটি এখনও কাজ করবে।
অপেক্ষা কর!
আপনার একটি Databaseইন্টারফেস রয়েছে, যা MyMySQLDatabaseপ্রয়োগ করে, আপনার MyMSSQLDatabaseক্লাসও রয়েছে, যার ঠিক একই পদ্ধতি রয়েছে MyMySQLDatabase, সম্ভবত এমএসএসকিউএল ড্রাইভারও Databaseইন্টারফেসটি বাস্তবায়ন করতে পারে , সুতরাং আপনি এটি সংজ্ঞাতে যুক্ত করুন।
public class MyMSSQLDatabase : Database { }
তবে যদি আমি, ভবিষ্যতে, MyMSSQLDatabaseআর আর ব্যবহার করতে চাই না, কারণ আমি পোস্টগ্রেএসকিউএল এ চলেছি? আমি আবার, এর সংজ্ঞা প্রতিস্থাপন করতে হবে SecretMethod?
হ্যাঁ, আপনি হবে। এবং এটি ঠিক শোনাচ্ছে না। এই মুহূর্তে আমরা জানি, এটি MyMSSQLDatabaseএবং MyMySQLDatabaseএকই পদ্ধতি রয়েছে এবং উভয়ই Databaseইন্টারফেস প্রয়োগ করে । সুতরাং আপনি যেমন দেখতে রিফ্যাক্টর SecretMethod।
public void SecretMethod(Database database)
{
// use the database here
}
লক্ষ্য করুন, কীভাবে SecretMethodআর জানেন না আপনি মাইএসকিউএল, এমএসএসকিউএল বা পটগ্র্যাসকিউএল ব্যবহার করছেন কিনা। এটি জানে যে এটি একটি ডাটাবেস ব্যবহার করে, তবে নির্দিষ্ট প্রয়োগের বিষয়ে চিন্তা করে না।
এখন আপনি যদি পোস্টগ্র্রেএসকিউএল জন্য উদাহরণস্বরূপ আপনার নতুন ডাটাবেস ড্রাইভার তৈরি করতে চান তবে আপনাকে এটিকে কোনও পরিবর্তন করার দরকার পড়বে না SecretMethod। আপনি একটি তৈরি করবেন MyPostgreSQLDatabase, এটি Databaseইন্টারফেসটি বাস্তবায়ন করুন এবং পোস্টগ্র্রেএসকিউএল ড্রাইভারের কোডিং হয়ে গেলে এবং এটি কাজ করে, আপনি এটির উদাহরণটি তৈরি করে এটিতে ইনজেক্ট করতে পারবেন SecretMethod।
৩. এর কাঙ্ক্ষিত বাস্তবায়ন প্রাপ্তি Database
কল করার আগে আপনাকে এখনও সিদ্ধান্ত নিতে হবে, আপনি SecretMethodযে Databaseইন্টারফেসটি চান তা কার্যকর করতে (এটি মাইএসকিউএল, এমএসএসকিউএল বা পোস্টগ্রিসকিউএল হোক)। এই জন্য, আপনি কারখানার নকশা প্যাটার্ন ব্যবহার করতে পারেন।
public class DatabaseFactory
{
private Config _config;
public DatabaseFactory(Config config)
{
_config = config;
}
public Database getDatabase()
{
var databaseType = _config.GetDatabaseType();
Database database = null;
switch (databaseType)
{
case DatabaseEnum.MySQL:
database = new MyMySQLDatabase(new CSharpMySQLDriver());
break;
case DatabaseEnum.MSSQL:
database = new MyMSSQLDatabase(new CSharpMSSQLDriver());
break;
case DatabaseEnum.PostgreSQL:
database = new MyPostgreSQLDatabase(new CSharpPostgreSQLDriver());
break;
default:
throw new DatabaseDriverNotImplementedException();
break;
}
return database;
}
}
কারখানাটি যেমন আপনি দেখতে পাচ্ছেন, কনফিগার ফাইল থেকে কোন ডাটাবেস টাইপ ব্যবহার Configকরতে হবে তা (আবার, ক্লাসটি আপনার নিজস্ব প্রয়োগ হতে পারে) জানে ।
আদর্শভাবে, আপনি DatabaseFactoryআপনার নির্ভরতা ইনজেকশন ধারক ভিতরে থাকতে পারে । আপনার প্রক্রিয়াটি এর পরে দেখতে পারে।
public class ProcessWhichCallsTheSecretMethod
{
private DIContainer _di;
private ClassWithSecretMethod _secret;
public ProcessWhichCallsTheSecretMethod(DIContainer di, ClassWithSecretMethod secret)
{
_di = di;
_secret = secret;
}
public void TheProcessMethod()
{
Database database = _di.Factories.DatabaseFactory.getDatabase();
_secret.SecretMethod(database);
}
}
দেখুন, আপনি কোনও নির্দিষ্ট ডাটাবেস প্রকার তৈরি করছেন এমন প্রক্রিয়াটি আর কোথাও নেই। শুধু তাই নয়, আপনি মোটেই কিছু তৈরি করছেন না। আপনি আপনার নির্ভরতা ইনজেকশন ধারক ( ভেরিয়েবল) এর ভিতরে GetDatabaseথাকা DatabaseFactoryঅবজেক্টে একটি পদ্ধতি কল করছেন _di, এমন একটি পদ্ধতি যা Databaseআপনার কনফিগারেশনের উপর ভিত্তি করে আপনাকে ইন্টারফেসের সঠিক উদাহরণটি ফিরিয়ে দেবে ।
যদি পোস্টগ্রি ব্যবহারের 3 সপ্তাহ পরে, আপনি মাইএসকিউএল ফিরে যেতে চান, আপনি একটি একক কনফিগারেশন ফাইল খুলুন এবং এর মান পরিবর্তন DatabaseDriverথেকে ক্ষেত্র DatabaseEnum.PostgreSQLথেকে DatabaseEnum.MySQL। এবং আপনি সম্পন্ন হয়েছে। হঠাৎ আপনার বাকী অ্যাপ্লিকেশন সঠিকভাবে একটি একক লাইন পরিবর্তন করে আবার মাইএসকিউএল ব্যবহার করে।
আপনি যদি এখনও অবাক না হন তবে আমি আপনাকে আইওসিতে আরও কিছুটা ডুব দেওয়ার পরামর্শ দিচ্ছি। আপনি কীভাবে কোনও কনফিগারেশন থেকে নয়, তবে কোনও ব্যবহারকারী ইনপুট থেকে নির্দিষ্ট সিদ্ধান্ত নিতে পারেন। এই অ্যাপ্রোচকে কৌশল প্যাটার্ন বলা হয় এবং এটি এন্টারপ্রাইজ অ্যাপ্লিকেশনগুলিতে ব্যবহৃত হতে পারে এবং এটি ব্যবহার করা যায়, কম্পিউটার গেমগুলি বিকাশ করার সময় এটি প্রায়শই ব্যবহৃত হয়।
DbQueryউদাহরণস্বরূপ, আপনার অবজেক্টটি নিন । ধরে নিই যে সেই অবজেক্টটিতে একটি এসকিউএল কোয়েরি স্ট্রিং কার্যকর করার জন্য একজন সদস্য রয়েছে, কীভাবে কেউ সেই জেনেরিক তৈরি করতে পারে?