একাধিক ডাটাবেস ধরণের সমর্থন করতে কীভাবে বিমূর্ত ডাটাবেস ইন্টারফেসগুলি লেখা হয়?


12

মাইএসকিউএল, এসকিউএললাইট, এমএসএসকিউএল ইত্যাদির মতো বিভিন্ন ধরণের ডাটাবেসের সাথে ইন্টারফেস করতে পারে এমন একটি বৃহত্তর অ্যাপ্লিকেশনটিতে কোনও বিমূর্ত শ্রেণীর নকশা কীভাবে শুরু করা যায়?

এই নকশার প্যাটার্নটি কী বলা হয় এবং এটি ঠিক কোথায় শুরু হয়?

ধরা যাক আপনাকে এমন একটি ক্লাস লিখতে হবে যার নিম্নলিখিত পদ্ধতি রয়েছে

public class Database {
   public DatabaseType databaseType;
   public Database (DatabaseType databaseType){
      this.databaseType = databaseType;
   }

   public void SaveToDatabase(){
       // Save some data to the db
   }
   public void ReadFromDatabase(){
      // Read some data from db
   }
}

//Application
public class Foo {
    public Database db = new Database (DatabaseType.MySQL);
    public void SaveData(){
        db.SaveToDatabase();
    }
}

আমি কেবলমাত্র প্রতিটি Databaseপদ্ধতিতে একটি বিবৃতিটি ভাবতে পারি

public void SaveToDatabase(){
   if(databaseType == DatabaseType.MySQL){

   }
   else if(databaseType == DatabaseType.SQLLite){

   }
}

উত্তর:


11

আপনি কি চান একাধিক বাস্তবায়নের জন্য ইন্টারফেস আপনার আবেদন ব্যবহার করে।

তাই ভালো:

public interface IDatabase
{
    void SaveToDatabase();
    void ReadFromDatabase();
}

public class MySQLDatabase : IDatabase
{
   public MySQLDatabase ()
   {
      //init stuff
   }

   public void SaveToDatabase(){
       //MySql implementation
   }
   public void ReadFromDatabase(){
      //MySql implementation
   }
}

public class SQLLiteDatabase : IDatabase
{
   public SQLLiteDatabase ()
   {
      //init stuff
   }

   public void SaveToDatabase(){
       //SQLLite implementation
   }
   public void ReadFromDatabase(){
      //SQLLite implementation
   }
}

//Application
public class Foo {
    public IDatabase db = GetDatabase();

    public void SaveData(){
        db.SaveToDatabase();
    }

    private IDatabase GetDatabase()
    {
        if(/*some way to tell if should use MySql*/)
            return new MySQLDatabase();
        else if(/*some way to tell if should use MySql*/)
            return new SQLLiteDatabase();

        throw new Exception("You forgot to configure the database!");
    }
}

IDatabaseআপনার অ্যাপ্লিকেশনটিতে রান সময় সঠিক বাস্তবায়ন স্থাপনের আরও ভাল উপায় হিসাবে আপনার " ফ্যাক্টরি পদ্ধতি " এবং " নির্ভরতা ইনজেকশন " এর মতো বিষয়গুলি অনুসন্ধান করা উচিত ।


25

কালেবের উত্তর, যখন তিনি সঠিক পথে ছিলেন, আসলে ভুল। তার 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। এবং আপনি সম্পন্ন হয়েছে। হঠাৎ আপনার বাকী অ্যাপ্লিকেশন সঠিকভাবে একটি একক লাইন পরিবর্তন করে আবার মাইএসকিউএল ব্যবহার করে।


আপনি যদি এখনও অবাক না হন তবে আমি আপনাকে আইওসিতে আরও কিছুটা ডুব দেওয়ার পরামর্শ দিচ্ছি। আপনি কীভাবে কোনও কনফিগারেশন থেকে নয়, তবে কোনও ব্যবহারকারী ইনপুট থেকে নির্দিষ্ট সিদ্ধান্ত নিতে পারেন। এই অ্যাপ্রোচকে কৌশল প্যাটার্ন বলা হয় এবং এটি এন্টারপ্রাইজ অ্যাপ্লিকেশনগুলিতে ব্যবহৃত হতে পারে এবং এটি ব্যবহার করা যায়, কম্পিউটার গেমগুলি বিকাশ করার সময় এটি প্রায়শই ব্যবহৃত হয়।


তোমার উত্তর পছন্দ, ডেভিড। তবে এই জাতীয় সমস্ত উত্তরগুলির মতো, এটি কীভাবে বাস্তবায়িত হতে পারে তা বর্ণনা করাও কম short আসল সমস্যাটি বিভিন্ন ডাটাবেস ইঞ্জিনগুলিতে কল করার ক্ষমতা দূরে রাখছে না, সমস্যাটি আসল এসকিউএল সিনট্যাক্স। DbQueryউদাহরণস্বরূপ, আপনার অবজেক্টটি নিন । ধরে নিই যে সেই অবজেক্টটিতে একটি এসকিউএল কোয়েরি স্ট্রিং কার্যকর করার জন্য একজন সদস্য রয়েছে, কীভাবে কেউ সেই জেনেরিক তৈরি করতে পারে?
ডনবয়েটট

1
@ ডনবয়েটনট আমার মনে হয় না জেনেরিক হওয়ার জন্য আপনার কখনই সমস্ত কিছুর প্রয়োজন হবে। আপনি সাধারণত অ্যাপ্লিকেশন স্তরগুলির মধ্যে বিমূর্ততা প্রবর্তন করতে চান (ডোমেন, পরিষেবাদি, অধ্যবসায়), আপনি মডিউলগুলির জন্য বিমূর্ততাও প্রবর্তন করতে চাইতে পারেন, আপনি কোনও বৃহত প্রকল্পের জন্য বিকাশ করছেন এমন একটি ছোট তবে পুনরায় ব্যবহারযোগ্য এবং অত্যন্ত স্বনির্ধারিত লাইব্রেরিতে অ্যাবস্ট্রাকশন প্রবর্তন করতে চান etc আপনি কেবল ইন্টারফেসে সমস্ত বিমূর্ত করতে পারেন, তবে এটি খুব কমই প্রয়োজন। সর্বকালের সর্বসম্মত উত্তর দেওয়া সত্যিই কঠিন, কারণ দুঃখের বিষয়, সত্যই সেখানে একটি নেই এবং এটি প্রয়োজনীয়তা থেকে আসে।
অ্যান্ডি

2
বুঝতে পারছিল না। তবে আমি সত্যিই এটি আক্ষরিকভাবে বোঝাতে চাইছি। আপনার একবার আপনার বিমূর্ত শ্রেণিটি শেষ হয়ে গেলে এবং আপনি যে বিন্দুতে কল করতে চান সেখানে _secret.SecretMethod(database);কীভাবে একজন এই সমস্ত কাজের সাথে কীভাবে মিলন করতে পারে তা এই সত্যের SecretMethodসাথে জানতে পেরেছি যে সঠিক এসকিউএল ডায়ালেক্টটি ব্যবহার করার জন্য আমি এখন ডিবি নিয়ে কী কাজ করছি? ? সংখ্যাগরিষ্ঠ কোডটিকে এই বিষয়টিকে অজানা রাখতে আপনি খুব কঠোর পরিশ্রম করেছেন, তবে 11 ঘন্টা সময় আপনাকে অবশ্যই জানতে হবে। আমি এখন এই পরিস্থিতিতে আছি এবং অন্যেরা কীভাবে এই সমস্যার সমাধান করেছে তা নির্ধারণের চেষ্টা করছি।
ডনবয়েটট

পছন্দ করেছেন আপনি DbQueryক্লাসের একটি কংক্রিট বাস্তবায়নের পরিবর্তে একটি ইন্টারফেস ব্যবহার করতে পারেন , বলেন ইন্টারফেসটির বাস্তবায়ন সরবরাহ করতে পারেন এবং পরিবর্তে কারখানা তৈরি করে IDbQueryএকটিটিকে ব্যবহার করতে পারেন । আমি মনে করি না যে আপনার DatabaseResultক্লাসের জন্য জেনেরিক ধরণের প্রয়োজন হবে , আপনি সবসময় কোনও ডাটাবেস থেকে ফলাফল একই ধরণের ফরম্যাট করার আশা করতে পারেন। এখানে জিনিসটি হ'ল, ডাটাবেসগুলি এবং কাঁচা এসকিউএল নিয়ে কাজ করার সময় আপনি ইতিমধ্যে আপনার অ্যাপ্লিকেশনটিতে (ডাল এবং রেপোজিটরিগুলির পিছনে) এত নিম্ন স্তরে রয়েছেন যে এর দরকার নেই ...
অ্যান্ডি

... আর জেনেরিক পদ্ধতির।
অ্যান্ডি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.